Tree: support wide cell renderer (issue #922)

This commit is contained in:
Karl Tauber
2024-12-09 19:40:07 +01:00
parent 145631fd43
commit 2a8e487c1f
16 changed files with 132 additions and 44 deletions

View File

@@ -6,6 +6,7 @@ FlatLaf Change Log
#### New features and improvements #### New features and improvements
- Tree: Support for alternate row highlighting. (PR #903) - Tree: Support for alternate row highlighting. (PR #903)
- Tree: Support wide cell renderer. (issue #922)
- Extras: `FlatSVGIcon` color filters now can access painting component to - Extras: `FlatSVGIcon` color filters now can access painting component to
implement component state based color mappings. (issue #906) implement component state based color mappings. (issue #906)

View File

@@ -1410,13 +1410,23 @@ public interface FlatClientProperties
//---- JTree -------------------------------------------------------------- //---- JTree --------------------------------------------------------------
/** /**
* Override if a tree shows a wide selection. Default is {@code true}. * Specifies whether tree shows a wide selection. Default is {@code true}.
* <p> * <p>
* <strong>Component</strong> {@link javax.swing.JTree}<br> * <strong>Component</strong> {@link javax.swing.JTree}<br>
* <strong>Value type</strong> {@link java.lang.Boolean} * <strong>Value type</strong> {@link java.lang.Boolean}
*/ */
String TREE_WIDE_SELECTION = "JTree.wideSelection"; String TREE_WIDE_SELECTION = "JTree.wideSelection";
/**
* Specifies whether tree uses a wide cell renderer. Default is {@code false}.
* <p>
* <strong>Component</strong> {@link javax.swing.JTree}<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 3.6
*/
String TREE_WIDE_CELL_RENDERER = "JTree.wideCellRenderer";
/** /**
* Specifies whether tree item selection is painted. Default is {@code true}. * Specifies whether tree item selection is painted. Default is {@code true}.
* If set to {@code false}, then the tree cell renderer is responsible for painting selection. * If set to {@code false}, then the tree cell renderer is responsible for painting selection.

View File

@@ -106,6 +106,7 @@ import com.formdev.flatlaf.util.UIScale;
* @uiDefault Tree.selectionInsets Insets * @uiDefault Tree.selectionInsets Insets
* @uiDefault Tree.selectionArc int * @uiDefault Tree.selectionArc int
* @uiDefault Tree.wideSelection boolean * @uiDefault Tree.wideSelection boolean
* @uiDefault Tree.wideCellRenderer boolean
* @uiDefault Tree.showCellFocusIndicator boolean * @uiDefault Tree.showCellFocusIndicator boolean
* @uiDefault Tree.showDefaultIcons boolean * @uiDefault Tree.showDefaultIcons boolean
* *
@@ -146,6 +147,7 @@ public class FlatTreeUI
/** @since 3 */ @Styleable protected Insets selectionInsets; /** @since 3 */ @Styleable protected Insets selectionInsets;
/** @since 3 */ @Styleable protected int selectionArc; /** @since 3 */ @Styleable protected int selectionArc;
@Styleable protected boolean wideSelection; @Styleable protected boolean wideSelection;
/** @since 3.6 */ @Styleable protected boolean wideCellRenderer;
@Styleable protected boolean showCellFocusIndicator; @Styleable protected boolean showCellFocusIndicator;
/** @since 3 */ protected boolean showDefaultIcons; /** @since 3 */ protected boolean showDefaultIcons;
@@ -198,6 +200,7 @@ public class FlatTreeUI
selectionInsets = UIManager.getInsets( "Tree.selectionInsets" ); selectionInsets = UIManager.getInsets( "Tree.selectionInsets" );
selectionArc = UIManager.getInt( "Tree.selectionArc" ); selectionArc = UIManager.getInt( "Tree.selectionArc" );
wideSelection = UIManager.getBoolean( "Tree.wideSelection" ); wideSelection = UIManager.getBoolean( "Tree.wideSelection" );
wideCellRenderer = UIManager.getBoolean( "Tree.wideCellRenderer" );
showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" ); showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" );
showDefaultIcons = UIManager.getBoolean( "Tree.showDefaultIcons" ); showDefaultIcons = UIManager.getBoolean( "Tree.showDefaultIcons" );
@@ -314,6 +317,7 @@ public class FlatTreeUI
if( e.getSource() == tree ) { if( e.getSource() == tree ) {
switch( e.getPropertyName() ) { switch( e.getPropertyName() ) {
case TREE_WIDE_SELECTION: case TREE_WIDE_SELECTION:
case TREE_WIDE_CELL_RENDERER:
case TREE_PAINT_SELECTION: case TREE_PAINT_SELECTION:
HiDPIUtils.repaint( tree ); HiDPIUtils.repaint( tree );
break; break;
@@ -584,6 +588,18 @@ public class FlatTreeUI
UIScale.scale( selectionInsets ), arc, arc, arc, arc, 0 ); UIScale.scale( selectionInsets ), arc, arc, arc, arc, 0 );
} }
// update bounds for wide cell renderer
if( isWideSelection() && isWideCellRenderer() ) {
Rectangle wideBounds = new Rectangle( bounds );
if( tree.getComponentOrientation().isLeftToRight() )
wideBounds.width = tree.getWidth() - bounds.x - insets.right;
else {
wideBounds.x = insets.left;
wideBounds.width = bounds.x + bounds.width - insets.left;
}
bounds = wideBounds;
}
// do not paint row if editing // do not paint row if editing
if( isEditing ) { if( isEditing ) {
// paint wide selection // paint wide selection
@@ -808,6 +824,11 @@ public class FlatTreeUI
return clientPropertyBoolean( tree, TREE_WIDE_SELECTION, wideSelection ); return clientPropertyBoolean( tree, TREE_WIDE_SELECTION, wideSelection );
} }
/** @since 3.6 */
protected boolean isWideCellRenderer() {
return clientPropertyBoolean( tree, TREE_WIDE_CELL_RENDERER, wideCellRenderer );
}
protected boolean isPaintSelection() { protected boolean isPaintSelection() {
return clientPropertyBoolean( tree, TREE_PAINT_SELECTION, paintSelection ); return clientPropertyBoolean( tree, TREE_PAINT_SELECTION, paintSelection );
} }

View File

@@ -934,6 +934,7 @@ Tree.rendererMargins = 1,2,1,2
Tree.selectionInsets = 0,0,0,0 Tree.selectionInsets = 0,0,0,0
Tree.selectionArc = 0 Tree.selectionArc = 0
Tree.wideSelection = true Tree.wideSelection = true
Tree.wideCellRenderer = false
Tree.repaintWholeRow = true Tree.repaintWholeRow = true
Tree.paintLines = false Tree.paintLines = false
Tree.showCellFocusIndicator = false Tree.showCellFocusIndicator = false

View File

@@ -969,6 +969,7 @@ public class TestFlatStyleableInfo
"selectionInsets", Insets.class, "selectionInsets", Insets.class,
"selectionArc", int.class, "selectionArc", int.class,
"wideSelection", boolean.class, "wideSelection", boolean.class,
"wideCellRenderer", boolean.class,
"showCellFocusIndicator", boolean.class, "showCellFocusIndicator", boolean.class,
"paintSelection", boolean.class, "paintSelection", boolean.class,

View File

@@ -942,6 +942,7 @@ public class TestFlatStyleableValue
testInsets( c, ui, "selectionInsets", 1,2,3,4 ); testInsets( c, ui, "selectionInsets", 1,2,3,4 );
testInteger( c, ui, "selectionArc", 123 ); testInteger( c, ui, "selectionArc", 123 );
testBoolean( c, ui, "wideSelection", true ); testBoolean( c, ui, "wideSelection", true );
testBoolean( c, ui, "wideCellRenderer", true );
testBoolean( c, ui, "showCellFocusIndicator", true ); testBoolean( c, ui, "showCellFocusIndicator", true );
testBoolean( c, ui, "paintSelection", false ); testBoolean( c, ui, "paintSelection", false );

View File

@@ -1191,6 +1191,7 @@ public class TestFlatStyling
ui.applyStyle( "selectionInsets: 1,2,3,4" ); ui.applyStyle( "selectionInsets: 1,2,3,4" );
ui.applyStyle( "selectionArc: 8" ); ui.applyStyle( "selectionArc: 8" );
ui.applyStyle( "wideSelection: true" ); ui.applyStyle( "wideSelection: true" );
ui.applyStyle( "wideCellRenderer: true" );
ui.applyStyle( "showCellFocusIndicator: true" ); ui.applyStyle( "showCellFocusIndicator: true" );
ui.applyStyle( "paintSelection: false" ); ui.applyStyle( "paintSelection: false" );

View File

@@ -29,19 +29,37 @@ public class FlatTree
implements FlatComponentExtension, FlatStyleableComponent implements FlatComponentExtension, FlatStyleableComponent
{ {
/** /**
* Returns if the tree shows a wide selection * Returns whether tree shows a wide selection
*/ */
public boolean isWideSelection() { public boolean isWideSelection() {
return getClientPropertyBoolean( TREE_WIDE_SELECTION, "Tree.wideSelection" ); return getClientPropertyBoolean( TREE_WIDE_SELECTION, "Tree.wideSelection" );
} }
/** /**
* Sets if the tree shows a wide selection * Specifies whether tree shows a wide selection
*/ */
public void setWideSelection( boolean wideSelection ) { public void setWideSelection( boolean wideSelection ) {
putClientProperty( TREE_WIDE_SELECTION, wideSelection ); putClientProperty( TREE_WIDE_SELECTION, wideSelection );
} }
/**
* Returns whether tree uses a wide cell renderer.
*
* @since 3.6
*/
public boolean isWideCellRenderer() {
return getClientPropertyBoolean( TREE_WIDE_CELL_RENDERER, "Tree.wideCellRenderer" );
}
/**
* Specifies whether tree uses a wide cell renderer.
*
* @since 3.6
*/
public void setWideCellRenderer( boolean wideCellRenderer ) {
putClientProperty( TREE_WIDE_CELL_RENDERER, wideCellRenderer );
}
/** /**
* Returns whether tree item selection is painted. Default is {@code true}. * Returns whether tree item selection is painted. Default is {@code true}.
* If set to {@code false}, then the tree cell renderer is responsible for painting selection. * If set to {@code false}, then the tree cell renderer is responsible for painting selection.

View File

@@ -1431,6 +1431,7 @@ Tree.showDefaultIcons false
Tree.textBackground #46494b HSL 204 3 28 javax.swing.plaf.ColorUIResource [UI] Tree.textBackground #46494b HSL 204 3 28 javax.swing.plaf.ColorUIResource [UI]
Tree.textForeground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI] Tree.textForeground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
Tree.timeFactor 1000 Tree.timeFactor 1000
Tree.wideCellRenderer false
Tree.wideSelection true Tree.wideSelection true
TreeUI com.formdev.flatlaf.ui.FlatTreeUI TreeUI com.formdev.flatlaf.ui.FlatTreeUI

View File

@@ -1436,6 +1436,7 @@ Tree.showDefaultIcons false
Tree.textBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] Tree.textBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Tree.textForeground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI] Tree.textForeground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI]
Tree.timeFactor 1000 Tree.timeFactor 1000
Tree.wideCellRenderer false
Tree.wideSelection true Tree.wideSelection true
TreeUI com.formdev.flatlaf.ui.FlatTreeUI TreeUI com.formdev.flatlaf.ui.FlatTreeUI

View File

@@ -1441,6 +1441,7 @@ Tree.showDefaultIcons false
Tree.textBackground #282828 HSL 0 0 16 javax.swing.plaf.ColorUIResource [UI] Tree.textBackground #282828 HSL 0 0 16 javax.swing.plaf.ColorUIResource [UI]
Tree.textForeground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI] Tree.textForeground #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Tree.timeFactor 1000 Tree.timeFactor 1000
Tree.wideCellRenderer false
Tree.wideSelection true Tree.wideSelection true
TreeUI com.formdev.flatlaf.ui.FlatTreeUI TreeUI com.formdev.flatlaf.ui.FlatTreeUI

View File

@@ -1445,6 +1445,7 @@ Tree.showDefaultIcons false
Tree.textBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] Tree.textBackground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
Tree.textForeground #262626 HSL 0 0 15 javax.swing.plaf.ColorUIResource [UI] Tree.textForeground #262626 HSL 0 0 15 javax.swing.plaf.ColorUIResource [UI]
Tree.timeFactor 1000 Tree.timeFactor 1000
Tree.wideCellRenderer false
Tree.wideSelection true Tree.wideSelection true
TreeUI com.formdev.flatlaf.ui.FlatTreeUI TreeUI com.formdev.flatlaf.ui.FlatTreeUI

View File

@@ -1495,6 +1495,7 @@ Tree.showDefaultIcons false
Tree.textBackground #fff0ff HSL 300 100 97 javax.swing.plaf.ColorUIResource [UI] Tree.textBackground #fff0ff HSL 300 100 97 javax.swing.plaf.ColorUIResource [UI]
Tree.textForeground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] Tree.textForeground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI]
Tree.timeFactor 1000 Tree.timeFactor 1000
Tree.wideCellRenderer false
Tree.wideSelection true Tree.wideSelection true
TreeUI com.formdev.flatlaf.ui.FlatTreeUI TreeUI com.formdev.flatlaf.ui.FlatTreeUI

View File

@@ -16,6 +16,7 @@
package com.formdev.flatlaf.testing; package com.formdev.flatlaf.testing;
import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.ComponentOrientation; import java.awt.ComponentOrientation;
@@ -32,6 +33,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.function.Supplier;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelEvent;
@@ -497,48 +499,23 @@ public class FlatComponents2Test
if( !(sel instanceof String) ) if( !(sel instanceof String) )
return; return;
JTree[] trees = { tree1, tree2, xTree1 }; Supplier<TreeCellRenderer> creator;
switch( (String) sel ) { switch( (String) sel ) {
case "default": default:
for( JTree tree : trees ) case "default": creator = DefaultTreeCellRenderer::new; break;
tree.setCellRenderer( new DefaultTreeCellRenderer() ); case "defaultSubclass": creator = TestDefaultTreeCellRenderer::new; break;
break; case "defaultWithIcons": creator = TestDefaultWithIconsTreeCellRenderer::new; break;
case "defaultWithIcon": creator = TestDefaultWithIconTreeCellRenderer::new; break;
case "defaultSubclass": case "label": creator = TestLabelTreeCellRenderer::new; break;
for( JTree tree : trees ) case "wide": creator = TestWideTreeCellRenderer::new; break;
tree.setCellRenderer( new TestDefaultTreeCellRenderer() ); case "swingxDefault": creator = DefaultTreeRenderer::new; break;
break; case "jideCheckBox": creator = () -> new CheckBoxTreeCellRenderer( new DefaultTreeCellRenderer() ); break;
case "jideStyled": creator = StyledTreeCellRenderer::new; break;
case "defaultWithIcons":
for( JTree tree : trees )
tree.setCellRenderer( new TestDefaultWithIconsTreeCellRenderer() );
break;
case "defaultWithIcon":
for( JTree tree : trees )
tree.setCellRenderer( new TestDefaultWithIconTreeCellRenderer() );
break;
case "label":
for( JTree tree : trees )
tree.setCellRenderer( new TestLabelTreeCellRenderer() );
break;
case "swingxDefault":
for( JTree tree : trees )
tree.setCellRenderer( new DefaultTreeRenderer() );
break;
case "jideCheckBox":
for( JTree tree : trees )
tree.setCellRenderer( new CheckBoxTreeCellRenderer( new DefaultTreeCellRenderer() ) );
break;
case "jideStyled":
for( JTree tree : trees )
tree.setCellRenderer( new StyledTreeCellRenderer() );
break;
} }
JTree[] trees = { tree1, tree2, xTree1 };
for( JTree tree : trees )
tree.setCellRenderer( creator.get() );
} }
private void treeWideSelectionChanged() { private void treeWideSelectionChanged() {
@@ -547,6 +524,12 @@ public class FlatComponents2Test
tree.putClientProperty( FlatClientProperties.TREE_WIDE_SELECTION, wideSelection ); tree.putClientProperty( FlatClientProperties.TREE_WIDE_SELECTION, wideSelection );
} }
private void treeWideCellRendererChanged() {
boolean wideCellRenderer = treeWideCellRendererCheckBox.isSelected();
for( JTree tree : allTrees )
tree.putClientProperty( FlatClientProperties.TREE_WIDE_CELL_RENDERER, wideCellRenderer );
}
private void treePaintSelectionChanged() { private void treePaintSelectionChanged() {
boolean paintSelection = treePaintSelectionCheckBox.isSelected(); boolean paintSelection = treePaintSelectionCheckBox.isSelected();
for( JTree tree : allTrees ) for( JTree tree : allTrees )
@@ -691,6 +674,7 @@ public class FlatComponents2Test
JLabel treeRendererLabel = new JLabel(); JLabel treeRendererLabel = new JLabel();
treeRendererComboBox = new JComboBox<>(); treeRendererComboBox = new JComboBox<>();
treeWideSelectionCheckBox = new JCheckBox(); treeWideSelectionCheckBox = new JCheckBox();
treeWideCellRendererCheckBox = new JCheckBox();
treePaintSelectionCheckBox = new JCheckBox(); treePaintSelectionCheckBox = new JCheckBox();
treePaintLinesCheckBox = new JCheckBox(); treePaintLinesCheckBox = new JCheckBox();
treeRedLinesCheckBox = new JCheckBox(); treeRedLinesCheckBox = new JCheckBox();
@@ -1088,6 +1072,7 @@ public class FlatComponents2Test
"defaultWithIcons", "defaultWithIcons",
"defaultWithIcon", "defaultWithIcon",
"label", "label",
"wide",
"swingxDefault", "swingxDefault",
"jideCheckBox", "jideCheckBox",
"jideStyled" "jideStyled"
@@ -1100,6 +1085,11 @@ public class FlatComponents2Test
treeWideSelectionCheckBox.addActionListener(e -> treeWideSelectionChanged()); treeWideSelectionCheckBox.addActionListener(e -> treeWideSelectionChanged());
treeOptionsPanel.add(treeWideSelectionCheckBox, "cell 0 1"); treeOptionsPanel.add(treeWideSelectionCheckBox, "cell 0 1");
//---- treeWideCellRendererCheckBox ----
treeWideCellRendererCheckBox.setText("wide cell renderer");
treeWideCellRendererCheckBox.addActionListener(e -> treeWideCellRendererChanged());
treeOptionsPanel.add(treeWideCellRendererCheckBox, "cell 0 1");
//---- treePaintSelectionCheckBox ---- //---- treePaintSelectionCheckBox ----
treePaintSelectionCheckBox.setText("paint selection"); treePaintSelectionCheckBox.setText("paint selection");
treePaintSelectionCheckBox.setSelected(true); treePaintSelectionCheckBox.setSelected(true);
@@ -1266,6 +1256,7 @@ public class FlatComponents2Test
private JPanel treeOptionsPanel; private JPanel treeOptionsPanel;
private JComboBox<String> treeRendererComboBox; private JComboBox<String> treeRendererComboBox;
private JCheckBox treeWideSelectionCheckBox; private JCheckBox treeWideSelectionCheckBox;
private JCheckBox treeWideCellRendererCheckBox;
private JCheckBox treePaintSelectionCheckBox; private JCheckBox treePaintSelectionCheckBox;
private JCheckBox treePaintLinesCheckBox; private JCheckBox treePaintLinesCheckBox;
private JCheckBox treeRedLinesCheckBox; private JCheckBox treeRedLinesCheckBox;
@@ -1794,6 +1785,32 @@ public class FlatComponents2Test
} }
} }
//---- class TestLabelTreeCellRenderer ------------------------------------
private static class TestWideTreeCellRenderer
extends JPanel
implements TreeCellRenderer
{
private final JLabel label = new JLabel();
private final JLabel icon = new JLabel( UIManager.getIcon( "FileView.floppyDriveIcon" ) );
TestWideTreeCellRenderer() {
super( new BorderLayout() );
setOpaque( false );
add( label, BorderLayout.CENTER );
add( icon, BorderLayout.LINE_END );
setBorder( new LineBorder( Color.red ) );
}
@Override
public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus )
{
label.setText( String.valueOf( value ) );
return this;
}
}
//---- class TestComboBoxTableCellRenderer -------------------------------- //---- class TestComboBoxTableCellRenderer --------------------------------
private static class TestComboBoxTableCellRenderer private static class TestComboBoxTableCellRenderer

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "8.2.2.0.9999" Java: "21.0.1" encoding: "UTF-8" JFDML JFormDesigner: "8.3" encoding: "UTF-8"
new FormModel { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -484,6 +484,7 @@ new FormModel {
addElement( "defaultWithIcons" ) addElement( "defaultWithIcons" )
addElement( "defaultWithIcon" ) addElement( "defaultWithIcon" )
addElement( "label" ) addElement( "label" )
addElement( "wide" )
addElement( "swingxDefault" ) addElement( "swingxDefault" )
addElement( "jideCheckBox" ) addElement( "jideCheckBox" )
addElement( "jideStyled" ) addElement( "jideStyled" )
@@ -505,6 +506,16 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1" "value": "cell 0 1"
} ) } )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "treeWideCellRendererCheckBox"
"text": "wide cell renderer"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "treeWideCellRendererChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) { add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "treePaintSelectionCheckBox" name: "treePaintSelectionCheckBox"
"text": "paint selection" "text": "paint selection"

View File

@@ -1217,6 +1217,7 @@ Tree.showDefaultIcons
Tree.textBackground Tree.textBackground
Tree.textForeground Tree.textForeground
Tree.timeFactor Tree.timeFactor
Tree.wideCellRenderer
Tree.wideSelection Tree.wideSelection
TreeUI TreeUI
TristateCheckBox.clearMixed.clientProperty TristateCheckBox.clearMixed.clientProperty