Skip to content

Commit

Permalink
Tree:
Browse files Browse the repository at this point in the history
- Fixed missing tree lines (if enabled) for wide-selected rows. (issue #598)
- Fixed scaling of tree lines and fixed alignment to expand/collapse arrows.
- Removed support for dashed tree lines. `Tree.lineTypeDashed` is now ignored.
  • Loading branch information
DevCharly committed Nov 1, 2022
1 parent 2bfbc9d commit ef21efe
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -17,6 +17,10 @@ FlatLaf Change Log
- FileChooser: Fixed layout of (optional) accessory component and fixed too
large right margin. (issue #604; regression since implementing PR #522 in
FlatLaf 2.3)
- Tree:
- Fixed missing tree lines (if enabled) for wide-selected rows. (issue #598)
- Fixed scaling of tree lines and fixed alignment to expand/collapse arrows.
- Removed support for dashed tree lines. `Tree.lineTypeDashed` is now ignored.


## 2.6
Expand Down
142 changes: 135 additions & 7 deletions flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java
Expand Up @@ -25,7 +25,11 @@
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import javax.swing.CellRendererPane;
import javax.swing.Icon;
Expand Down Expand Up @@ -152,6 +156,7 @@ public class FlatTreeUI
// only used via styling (not in UI defaults, but has likewise client properties)
/** @since 2 */ @Styleable protected boolean paintSelection = true;

private boolean paintLines;
private Color defaultCellNonSelectionBackground;
private Color defaultSelectionBackground;
private Color defaultSelectionForeground;
Expand Down Expand Up @@ -185,6 +190,7 @@ protected void installDefaults() {
wideSelection = UIManager.getBoolean( "Tree.wideSelection" );
showCellFocusIndicator = UIManager.getBoolean( "Tree.showCellFocusIndicator" );

paintLines = UIManager.getBoolean( "Tree.paintLines" );
defaultCellNonSelectionBackground = UIManager.getColor( "Tree.textBackground" );
defaultSelectionBackground = selectionBackground;
defaultSelectionForeground = selectionForeground;
Expand Down Expand Up @@ -381,6 +387,125 @@ public Object getStyleableValue( JComponent c, String key ) {
return FlatStylingSupport.getAnnotatedStyleableValue( this, key );
}

@Override
public void paint( Graphics g, JComponent c ) {
if( treeState == null )
return;

// use clip bounds to limit painting to needed rows
Rectangle clipBounds = g.getClipBounds();
TreePath firstPath = getClosestPathForLocation( tree, 0, clipBounds.y );
Enumeration<TreePath> visiblePaths = treeState.getVisiblePathsFrom( firstPath );

if( visiblePaths != null ) {
Insets insets = tree.getInsets();

HashSet<TreePath> verticalLinePaths = paintLines ? new HashSet<>() : null;
ArrayList<Runnable> paintLinesLater = paintLines ? new ArrayList<>() : null;
ArrayList<Runnable> paintExpandControlsLater = paintLines ? new ArrayList<>() : null;

// add parents for later painting of vertical lines
if( paintLines ) {
for( TreePath path = firstPath.getParentPath(); path != null; path = path.getParentPath() )
verticalLinePaths.add( path );
}

Rectangle boundsBuffer = new Rectangle();
boolean rootVisible = isRootVisible();
int row = treeState.getRowForPath( firstPath );
boolean leftToRight = tree.getComponentOrientation().isLeftToRight();
int treeWidth = tree.getWidth();

// iterate over visible rows and paint rows, expand control and lines
while( visiblePaths.hasMoreElements() ) {
TreePath path = visiblePaths.nextElement();
if( path == null )
break;

// compute path bounds
Rectangle bounds = treeState.getBounds( path, boundsBuffer );
if( bounds == null )
break;

// add tree insets to path bounds
if( leftToRight )
bounds.x += insets.left;
else
bounds.x = treeWidth - insets.right - (bounds.x + bounds.width);
bounds.y += insets.top;

boolean isLeaf = treeModel.isLeaf( path.getLastPathComponent() );
boolean isExpanded = isLeaf ? false : treeState.getExpandedState( path );
boolean hasBeenExpanded = isLeaf ? false : tree.hasBeenExpanded( path );

// paint row (including selection)
paintRow( g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf );

// collect lines for later painting
if( paintLines ) {
TreePath parentPath = path.getParentPath();

// add parent for later painting of vertical lines
if( parentPath != null )
verticalLinePaths.add( parentPath );

// paint horizontal line later (for using rendering hints)
if( parentPath != null || (rootVisible && row == 0) ) {
Rectangle bounds2 = new Rectangle( bounds );
int row2 = row;
paintLinesLater.add( () -> {
paintHorizontalPartOfLeg( g, clipBounds, insets, bounds2, path, row2, isExpanded, hasBeenExpanded, isLeaf );
} );
}
}

// paint expand control
if( shouldPaintExpandControl( path, row, isExpanded, hasBeenExpanded, isLeaf ) ) {
if( paintLines ) {
// need to paint after painting lines
Rectangle bounds2 = new Rectangle( bounds );
int row2 = row;
paintExpandControlsLater.add( () -> {
paintExpandControl( g, clipBounds, insets, bounds2, path, row2, isExpanded, hasBeenExpanded, isLeaf );
} );
} else
paintExpandControl( g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf );
}

if( bounds.y + bounds.height >= clipBounds.y + clipBounds.height )
break;

row++;
}

if( paintLines ) {
// enable antialiasing for line painting
Object[] oldRenderingHints = FlatUIUtils.setRenderingHints( g );

// paint horizontal lines
for( Runnable r : paintLinesLater )
r.run();

// paint vertical lines
g.setColor( Color.green );
for( TreePath path : verticalLinePaths )
paintVerticalPartOfLeg( g, clipBounds, insets, path );

// restore rendering hints
if( oldRenderingHints != null )
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );

// paint expand controls
for( Runnable r : paintExpandControlsLater )
r.run();
}
}

paintDropLine( g );

rendererPane.removeAll();
}

/**
* Similar to super.paintRow(), but supports wide selection and uses
* inactive selection background/foreground if tree is not focused.
Expand Down Expand Up @@ -544,13 +669,6 @@ private void paintWideSelection( Graphics g, Rectangle clipBounds, Insets insets

FlatUIUtils.paintSelection( (Graphics2D) g, 0, bounds.y, tree.getWidth(), bounds.height,
UIScale.scale( selectionInsets ), arcTop, arcTop, arcBottom, arcBottom, 0 );

// paint expand/collapse icon
// (was already painted before, but painted over with wide selection)
if( shouldPaintExpandControl( path, row, isExpanded, hasBeenExpanded, isLeaf ) ) {
paintExpandControl( g, clipBounds, insets, bounds,
path, row, isExpanded, hasBeenExpanded, isLeaf );
}
}

private void paintCellBackground( Graphics g, Component rendererComponent, Rectangle bounds,
Expand Down Expand Up @@ -596,6 +714,16 @@ private boolean useUnitedRoundedSelection() {
(selectionInsets == null || (selectionInsets.top == 0 && selectionInsets.bottom == 0));
}

@Override
protected void paintVerticalLine( Graphics g, JComponent c, int x, int top, int bottom ) {
((Graphics2D)g).fill( new Rectangle2D.Float( x, top, UIScale.scale( 1f ), bottom - top ) );
}

@Override
protected void paintHorizontalLine( Graphics g, JComponent c, int y, int left, int right ) {
((Graphics2D)g).fill( new Rectangle2D.Float( left, y, right - left, UIScale.scale( 1f ) ) );
}

/**
* Checks whether dropping on a row.
* See DefaultTreeCellRenderer.getTreeCellRendererComponent().
Expand Down
Expand Up @@ -473,6 +473,22 @@ private void treePaintSelectionChanged() {
tree.putClientProperty( FlatClientProperties.TREE_PAINT_SELECTION, paintSelection );
}

private void treePaintLinesChanged() {
boolean paintLines = treePaintLinesCheckBox.isSelected();
UIManager.put( "Tree.paintLines", paintLines ? true : null );
for( JTree tree : allTrees )
tree.updateUI();

treeRedLinesCheckBox.setEnabled( paintLines );
}

private void treeRedLinesChanged() {
boolean redLines = treeRedLinesCheckBox.isSelected();
UIManager.put( "Tree.hash", redLines ? Color.red : null );
for( JTree tree : allTrees )
tree.updateUI();
}

private void treeEditableChanged() {
boolean editable = treeEditableCheckBox.isSelected();
for( JTree tree : allTrees )
Expand Down Expand Up @@ -575,6 +591,8 @@ private void initComponents() {
treeRendererComboBox = new JComboBox<>();
treeWideSelectionCheckBox = new JCheckBox();
treePaintSelectionCheckBox = new JCheckBox();
treePaintLinesCheckBox = new JCheckBox();
treeRedLinesCheckBox = new JCheckBox();
treeEditableCheckBox = new JCheckBox();
JPanel tableOptionsPanel = new JPanel();
JLabel autoResizeModeLabel = new JLabel();
Expand Down Expand Up @@ -917,6 +935,7 @@ public void mouseClicked(MouseEvent e) {
"[]" +
"[]0" +
"[]0" +
"[]0" +
"[]"));

//---- treeRendererLabel ----
Expand Down Expand Up @@ -946,10 +965,21 @@ public void mouseClicked(MouseEvent e) {
treePaintSelectionCheckBox.addActionListener(e -> treePaintSelectionChanged());
treeOptionsPanel.add(treePaintSelectionCheckBox, "cell 0 2");

//---- treePaintLinesCheckBox ----
treePaintLinesCheckBox.setText("paint lines");
treePaintLinesCheckBox.addActionListener(e -> treePaintLinesChanged());
treeOptionsPanel.add(treePaintLinesCheckBox, "cell 0 3");

//---- treeRedLinesCheckBox ----
treeRedLinesCheckBox.setText("red lines");
treeRedLinesCheckBox.setEnabled(false);
treeRedLinesCheckBox.addActionListener(e -> treeRedLinesChanged());
treeOptionsPanel.add(treeRedLinesCheckBox, "cell 0 3");

//---- treeEditableCheckBox ----
treeEditableCheckBox.setText("editable");
treeEditableCheckBox.addActionListener(e -> treeEditableChanged());
treeOptionsPanel.add(treeEditableCheckBox, "cell 0 3");
treeOptionsPanel.add(treeEditableCheckBox, "cell 0 4");
}
add(treeOptionsPanel, "cell 0 4 4 1");

Expand Down Expand Up @@ -1083,6 +1113,8 @@ public void mouseClicked(MouseEvent e) {
private JComboBox<String> treeRendererComboBox;
private JCheckBox treeWideSelectionCheckBox;
private JCheckBox treePaintSelectionCheckBox;
private JCheckBox treePaintLinesCheckBox;
private JCheckBox treeRedLinesCheckBox;
private JCheckBox treeEditableCheckBox;
private JComboBox<String> autoResizeModeField;
private JComboBox<String> sortIconPositionComboBox;
Expand Down
@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.5.0.404" Java: "17.0.2" encoding: "UTF-8"
JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.2" encoding: "UTF-8"

new FormModel {
contentType: "form/swing"
Expand Down Expand Up @@ -395,7 +395,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[left]"
"$rowConstraints": "[][]0[]0[]"
"$rowConstraints": "[][]0[]0[]0[]"
} ) {
name: "treeOptionsPanel"
"border": new javax.swing.border.TitledBorder( "JTree Control" )
Expand Down Expand Up @@ -445,6 +445,27 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "treePaintLinesCheckBox"
"text": "paint lines"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "treePaintLinesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "treeRedLinesCheckBox"
"text": "red lines"
"enabled": false
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "treeRedLinesChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "treeEditableCheckBox"
"text": "editable"
Expand All @@ -453,7 +474,7 @@ new FormModel {
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "treeEditableChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
"value": "cell 0 4"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4 4 1"
Expand Down

0 comments on commit ef21efe

Please sign in to comment.