From 268fe15004f59e19680f9ad7a0a46d52eda368a4 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Fri, 13 Aug 2021 00:19:34 +0200 Subject: [PATCH] Tree: improved support for `JTree.getPathForLocation(int x, int y)` in wide selection (issue #373) this is experimental and disabled by default; enable with: `UIManager.put( "FlatLaf.experimental.tree.widePathForLocation", true );` --- .../com/formdev/flatlaf/ui/FlatTreeUI.java | 20 ++++++++ .../flatlaf/testing/FlatComponents2Test.java | 48 +++++++++++++++++++ .../flatlaf/testing/FlatComponents2Test.jfd | 6 ++- 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java index 6dd2c9c45..0db85395a 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTreeUI.java @@ -230,6 +230,26 @@ private void repaintWideDropLocation(JTree.DropLocation loc) { tree.repaint( 0, r.y, tree.getWidth(), r.height ); } + @Override + public Rectangle getPathBounds( JTree tree, TreePath path ) { + Rectangle bounds = super.getPathBounds( tree, path ); + + // If this method was invoked from JTree.getPathForLocation(int x, int y) to check whether + // the location is within tree node bounds, then return the bounds of a wide node. + // This changes the behavior of JTree.getPathForLocation(int x, int y) and + // JTree.getRowForLocation(int x, int y), which now return the path/row even + // if [x,y] is in the wide row area outside of the actual tree node. + if( bounds != null && + isWideSelection() && + UIManager.getBoolean( "FlatLaf.experimental.tree.widePathForLocation" ) && + StackUtils.wasInvokedFrom( JTree.class.getName(), "getPathForLocation", 5 ) ) + { + bounds.x = 0; + bounds.width = tree.getWidth(); + } + return bounds; + } + /** * Same as super.paintRow(), but supports wide selection and uses * inactive selection background/foreground if tree is not focused. diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java index 3546933ce..0b7d09c3c 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.java @@ -24,6 +24,7 @@ import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; +import java.awt.event.*; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -39,6 +40,7 @@ import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.icons.FlatMenuArrowIcon; @@ -69,6 +71,7 @@ public static void main( String[] args ) { SwingUtilities.invokeLater( () -> { FlatTestFrame frame = FlatTestFrame.create( args, "FlatComponents2Test" ); frame.useApplyComponentOrientation = true; + UIManager.put( "FlatLaf.experimental.tree.widePathForLocation", true ); frame.showFrame( FlatComponents2Test::new ); } ); } @@ -378,6 +381,23 @@ private void treePaintSelectionChanged() { tree.putClientProperty( FlatClientProperties.TREE_PAINT_SELECTION, paintSelection ); } + private void treeMouseClicked( MouseEvent e ) { + JTree tree = (JTree) e.getSource(); + int x = e.getX(); + int y = e.getY(); + + TreePath path = tree.getPathForLocation( x, y ); + TreePath closestPath = tree.getClosestPathForLocation( x, y ); + int row = tree.getRowForLocation( x, y ); + int closestRow = tree.getClosestRowForLocation( x, y ); + + System.out.println( "---- tree mouseClicked " + x + "," + y + " ----" ); + System.out.println( " path: " + path ); + System.out.println( "closest path: " + closestPath ); + System.out.println( " row: " + row ); + System.out.println( "closest row: " + closestRow ); + } + @Override public void applyComponentOrientation( ComponentOrientation o ) { super.applyComponentOrientation( o ); @@ -605,6 +625,12 @@ private void initComponents() { //---- tree1 ---- tree1.setShowsRootHandles(true); tree1.setEditable(true); + tree1.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + treeMouseClicked(e); + } + }); scrollPane3.setViewportView(tree1); } add(scrollPane3, "cell 1 2"); @@ -614,6 +640,12 @@ private void initComponents() { //---- tree2 ---- tree2.setEnabled(false); + tree2.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + treeMouseClicked(e); + } + }); scrollPane4.setViewportView(tree2); } add(scrollPane4, "cell 2 2"); @@ -655,12 +687,28 @@ private void initComponents() { //======== scrollPane5 ======== { + + //---- xTree1 ---- + xTree1.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + treeMouseClicked(e); + } + }); scrollPane5.setViewportView(xTree1); } add(scrollPane5, "cell 1 3"); //======== scrollPane6 ======== { + + //---- checkBoxTree1 ---- + checkBoxTree1.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + treeMouseClicked(e); + } + }); scrollPane6.setViewportView(checkBoxTree1); } add(scrollPane6, "cell 2 3"); diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.jfd index caa82559f..8cf889d19 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatComponents2Test.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.3.1.342" Java: "16" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.4.0.360" Java: "16" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -185,6 +185,7 @@ new FormModel { auxiliary() { "JavaCodeGenerator.variableLocal": false } + addEvent( new FormEvent( "java.awt.event.MouseListener", "mouseClicked", "treeMouseClicked", true ) ) } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 2" @@ -197,6 +198,7 @@ new FormModel { auxiliary() { "JavaCodeGenerator.variableLocal": false } + addEvent( new FormEvent( "java.awt.event.MouseListener", "mouseClicked", "treeMouseClicked", true ) ) } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 2 2" @@ -255,6 +257,7 @@ new FormModel { auxiliary() { "JavaCodeGenerator.variableLocal": false } + addEvent( new FormEvent( "java.awt.event.MouseListener", "mouseClicked", "treeMouseClicked", true ) ) } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 3" @@ -266,6 +269,7 @@ new FormModel { auxiliary() { "JavaCodeGenerator.variableLocal": false } + addEvent( new FormEvent( "java.awt.event.MouseListener", "mouseClicked", "treeMouseClicked", true ) ) } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 2 3"