diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index c6de4697..0cdd4f7f 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -377,6 +377,10 @@ BUG_FIX Right click (or two finger tap on mac) after selecting a component with a regular click opens editor dialog in addition to the context menu + + BUG_FIX + Clicking on a component type in the left-hand component tree sometimes does not register and does not instantiate the component + https://github.com/bancika/diy-layout-creator/releases diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/tree/TreePanel.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/tree/TreePanel.java index 09a6474c..c85561f9 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/tree/TreePanel.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/tree/TreePanel.java @@ -96,6 +96,13 @@ public class TreePanel extends JPanel { private JPopupMenu popup; + /** + * When the selection listener already ran the leaf action for this path, the following + * left-button {@code mouseReleased} must not run it again. Cleared after that release or on the + * next EDT tick (keyboard-only selection). + */ + private TreePath suppressLeafActivationForPath; + @SuppressWarnings("unchecked") public TreePanel(IPlugInPort plugInPort, ISwingUI swingUI) { this.plugInPort = plugInPort; @@ -220,7 +227,14 @@ public class TreePanel extends JPanel { if (node != null && node instanceof TreeNode) { TreeNode payload = (TreeNode) node; if (payload.getClickListener() != null) { + final TreePath path = e.getPath(); + suppressLeafActivationForPath = path; payload.getClickListener().mouseClicked(null); + SwingUtilities.invokeLater(() -> { + if (suppressLeafActivationForPath != null && suppressLeafActivationForPath.equals(path)) { + suppressLeafActivationForPath = null; + } + }); } } } @@ -230,21 +244,49 @@ public class TreePanel extends JPanel { @Override public void mouseClicked(MouseEvent e) { - if (e.getClickCount() != 1) + if (e.getClickCount() != 1 || !SwingUtilities.isRightMouseButton(e)) return; - if (SwingUtilities.isRightMouseButton(e)) { - int row = tree.getClosestRowForLocation(e.getX(), e.getY()); - tree.setSelectionRow(row); - getPopup().show(e.getComponent(), e.getX(), e.getY()); - } else { - TreePath path = tree.getClosestPathForLocation(e.getX(), e.getY()); - Object node = path.getLastPathComponent(); - if (node != null && node instanceof TreeNode) { - TreeNode payload = (TreeNode) node; - if (payload.getClickListener() != null) { - payload.getClickListener().mouseClicked(e); - } + int row = tree.getClosestRowForLocation(e.getX(), e.getY()); + tree.setSelectionRow(row); + getPopup().show(e.getComponent(), e.getX(), e.getY()); + } + + /** + * Prefer release over click: {@code mouseClicked} is not posted if the cursor moves + * between press and release (common with DnD or hand jitter), while selection may still + * change — then re-clicks on the same row fire no selection event. + */ + @Override + public void mouseReleased(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e) || e.isPopupTrigger()) { + return; + } + if (e.getClickCount() != 1) { + return; + } + + TreePath path = tree.getPathForLocation(e.getX(), e.getY()); + if (path == null) { + path = tree.getClosestPathForLocation(e.getX(), e.getY()); + } + if (path == null) { + return; + } + + if (path.equals(suppressLeafActivationForPath)) { + suppressLeafActivationForPath = null; + return; + } + if (!path.equals(tree.getSelectionPath())) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof TreeNode) { + TreeNode payload = (TreeNode) node; + if (payload.getClickListener() != null) { + payload.getClickListener().mouseClicked(e); } } }