changeset 6224:8ce1f1916a3a

RT-34563: Context Menus overlap in TableView's TextFieldTableCell I moved the code responsible for showing context menus out of the TextField mouse event handler into the proper context menu handler, so that the event could be consumed before it bubbled up to the default context menu handler in Control. This prevents the context menu on the cell being shown, as the TextField context menu is shown first and then consumes the event, as expected.
author jgiles
date Fri, 31 Jan 2014 12:11:31 +1300
parents d32812219304
children d3b937cefc8e
files modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/BehaviorBase.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TextFieldBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/skin/BehaviorSkinBase.java modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextAreaSkin.java modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextFieldSkin.java modules/controls/src/main/java/javafx/scene/control/Control.java
diffstat 7 files changed, 147 insertions(+), 113 deletions(-) [+]
line wrap: on
line diff
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/BehaviorBase.java	Fri Jan 31 11:23:46 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/BehaviorBase.java	Fri Jan 31 12:11:31 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,6 +33,7 @@
 import javafx.geometry.NodeOrientation;
 import javafx.scene.Node;
 import javafx.scene.control.Control;
+import javafx.scene.input.ContextMenuEvent;
 import javafx.scene.input.KeyEvent;
 import javafx.scene.input.MouseEvent;
 import java.util.ArrayList;
@@ -415,4 +416,13 @@
      * @param e the mouse event
      */
     public void mouseExited(MouseEvent e) { }
+
+    /**
+     * Invoked by a Skin when the control has had its context menu requested,
+     * most commonly by right-clicking on the control. Subclasses should be sure
+     * to call super unless they intend to disable any built-in support.
+     *
+     * @param e the context menu event
+     */
+    public void contextMenuRequested(ContextMenuEvent e) { }
 }
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java	Fri Jan 31 11:23:46 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java	Fri Jan 31 12:11:31 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,6 +34,7 @@
 import javafx.scene.control.ContextMenu;
 import javafx.scene.control.IndexRange;
 import javafx.scene.control.TextArea;
+import javafx.scene.input.ContextMenuEvent;
 import javafx.scene.input.MouseButton;
 import javafx.scene.input.MouseEvent;
 import javafx.stage.Screen;
@@ -337,7 +338,7 @@
 
             // if the primary button was pressed
             if (e.getButton() == MouseButton.PRIMARY && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown())) {
-                HitInfo hit = skin.getIndex(e);
+                HitInfo hit = skin.getIndex(e.getX(), e.getY());
                 int i = com.sun.javafx.scene.control.skin.Utils.getHitInsertionIndex(hit, textArea.textProperty().getValueSafe());
 //                 int i = skin.getInsertionPoint(e.getX(), e.getY());
                 final int anchor = textArea.getAnchor();
@@ -395,7 +396,7 @@
         if (!textArea.isDisabled() && !deferClick) {
             if (e.getButton() == MouseButton.PRIMARY && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown())) {
                 if (!(e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown() || e.isShortcutDown())) {
-                    skin.positionCaret(skin.getIndex(e), true, false);
+                    skin.positionCaret(skin.getIndex(e.getX(), e.getY()), true, false);
                 }
             }
         }
@@ -410,66 +411,71 @@
             setCaretAnimating(false);
             if (deferClick) {
                 deferClick = false;
-                skin.positionCaret(skin.getIndex(e), shiftDown, false);
+                skin.positionCaret(skin.getIndex(e.getX(), e.getY()), shiftDown, false);
                 shiftDown = false;
             }
             setCaretAnimating(true);
         }
-        if (e.getButton() == MouseButton.SECONDARY) {
-            if (contextMenu.isShowing()) {
-                contextMenu.hide();
-            } else if (textArea.getContextMenu() == null) {
-                double screenX = e.getScreenX();
-                double screenY = e.getScreenY();
-                double sceneX = e.getSceneX();
+    }
 
-                if (IS_TOUCH_SUPPORTED) {
-                    Point2D menuPos;
-                    if (textArea.getSelection().getLength() == 0) {
-                        skin.positionCaret(skin.getIndex(e), false, false);
+    @Override public void contextMenuRequested(ContextMenuEvent e) {
+        final TextArea textArea = getControl();
+
+        if (contextMenu.isShowing()) {
+            contextMenu.hide();
+        } else if (textArea.getContextMenu() == null) {
+            double screenX = e.getScreenX();
+            double screenY = e.getScreenY();
+            double sceneX = e.getSceneX();
+
+            if (IS_TOUCH_SUPPORTED) {
+                Point2D menuPos;
+                if (textArea.getSelection().getLength() == 0) {
+                    skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false, false);
+                    menuPos = skin.getMenuPosition();
+                } else {
+                    menuPos = skin.getMenuPosition();
+                    if (menuPos != null && (menuPos.getX() <= 0 || menuPos.getY() <= 0)) {
+                        skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false, false);
                         menuPos = skin.getMenuPosition();
-                    } else {
-                        menuPos = skin.getMenuPosition();
-                        if (menuPos != null && (menuPos.getX() <= 0 || menuPos.getY() <= 0)) {
-                            skin.positionCaret(skin.getIndex(e), false, false);
-                            menuPos = skin.getMenuPosition();
-                        }
-                    }
-
-                    if (menuPos != null) {
-                        Point2D p = getControl().localToScene(menuPos);
-                        Scene scene = getControl().getScene();
-                        Window window = scene.getWindow();
-                        Point2D location = new Point2D(window.getX() + scene.getX() + p.getX(),
-                                                       window.getY() + scene.getY() + p.getY());
-                        screenX = location.getX();
-                        sceneX = p.getX();
-                        screenY = location.getY();
                     }
                 }
 
-                skin.populateContextMenu(contextMenu);
-                double menuWidth = contextMenu.prefWidth(-1);
-                double menuX = screenX - (IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0);
-                Screen currentScreen = com.sun.javafx.Utils.getScreenForPoint(screenX, 0);
-                Rectangle2D bounds = currentScreen.getBounds();
-
-                if (menuX < bounds.getMinX()) {
-                    getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
-                    getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
-                    contextMenu.show(getControl(), bounds.getMinX(), screenY);
-                } else if (screenX + menuWidth > bounds.getMaxX()) {
-                    double leftOver = menuWidth - ( bounds.getMaxX() - screenX);
-                    getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
-                    getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
-                    contextMenu.show(getControl(), screenX - leftOver, screenY);
-                } else {
-                    getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", 0);
-                    getControl().getProperties().put("CONTEXT_MENU_SCENE_X", 0);
-                    contextMenu.show(getControl(), menuX, screenY);
+                if (menuPos != null) {
+                    Point2D p = getControl().localToScene(menuPos);
+                    Scene scene = getControl().getScene();
+                    Window window = scene.getWindow();
+                    Point2D location = new Point2D(window.getX() + scene.getX() + p.getX(),
+                                                   window.getY() + scene.getY() + p.getY());
+                    screenX = location.getX();
+                    sceneX = p.getX();
+                    screenY = location.getY();
                 }
             }
+
+            skin.populateContextMenu(contextMenu);
+            double menuWidth = contextMenu.prefWidth(-1);
+            double menuX = screenX - (IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0);
+            Screen currentScreen = com.sun.javafx.Utils.getScreenForPoint(screenX, 0);
+            Rectangle2D bounds = currentScreen.getBounds();
+
+            if (menuX < bounds.getMinX()) {
+                getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
+                getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
+                contextMenu.show(getControl(), bounds.getMinX(), screenY);
+            } else if (screenX + menuWidth > bounds.getMaxX()) {
+                double leftOver = menuWidth - ( bounds.getMaxX() - screenX);
+                getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
+                getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
+                contextMenu.show(getControl(), screenX - leftOver, screenY);
+            } else {
+                getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", 0);
+                getControl().getProperties().put("CONTEXT_MENU_SCENE_X", 0);
+                contextMenu.show(getControl(), menuX, screenY);
+            }
         }
+
+        e.consume();
     }
 
     @Override protected void setCaretAnimating(boolean play) {
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TextFieldBehavior.java	Fri Jan 31 11:23:46 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TextFieldBehavior.java	Fri Jan 31 12:11:31 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,7 @@
 import javafx.scene.Scene;
 import javafx.scene.control.ContextMenu;
 import javafx.scene.control.TextField;
+import javafx.scene.input.ContextMenuEvent;
 import javafx.scene.input.KeyEvent;
 import javafx.scene.input.MouseButton;
 import javafx.scene.input.MouseEvent;
@@ -243,7 +244,7 @@
 
             // if the primary button was pressed
             if (e.isPrimaryButtonDown() && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown())) {
-                HitInfo hit = skin.getIndex(e);
+                HitInfo hit = skin.getIndex(e.getX(), e.getY());
                 String text = textField.textProperty().getValueSafe();
                 int i = com.sun.javafx.scene.control.skin.Utils.getHitInsertionIndex(hit, text);
                 final int anchor = textField.getAnchor();
@@ -301,7 +302,7 @@
         if (!textField.isDisabled() && !deferClick) {
             if (e.isPrimaryButtonDown() && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown())) {
                 if (!(e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown())) {
-                    skin.positionCaret(skin.getIndex(e), true);
+                    skin.positionCaret(skin.getIndex(e.getX(), e.getY()), true);
                 }
             }
         }
@@ -316,66 +317,71 @@
             setCaretAnimating(false);
             if (deferClick) {
                 deferClick = false;
-                skin.positionCaret(skin.getIndex(e), shiftDown);
+                skin.positionCaret(skin.getIndex(e.getX(), e.getY()), shiftDown);
                 shiftDown = false;
             }
             setCaretAnimating(true);
         }
-        if (e.getButton() == MouseButton.SECONDARY) {
-            if (contextMenu.isShowing()) {
-                contextMenu.hide();
-            } else if (textField.getContextMenu() == null) {
-                double screenX = e.getScreenX();
-                double screenY = e.getScreenY();
-                double sceneX = e.getSceneX();
+    }
 
-                if (IS_TOUCH_SUPPORTED) {
-                    Point2D menuPos;
-                    if (textField.getSelection().getLength() == 0) {
-                        skin.positionCaret(skin.getIndex(e), false);
+    @Override public void contextMenuRequested(ContextMenuEvent e) {
+        final TextField textField = getControl();
+
+        if (contextMenu.isShowing()) {
+            contextMenu.hide();
+        } else if (textField.getContextMenu() == null) {
+            double screenX = e.getScreenX();
+            double screenY = e.getScreenY();
+            double sceneX = e.getSceneX();
+
+            if (IS_TOUCH_SUPPORTED) {
+                Point2D menuPos;
+                if (textField.getSelection().getLength() == 0) {
+                    skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false);
+                    menuPos = skin.getMenuPosition();
+                } else {
+                    menuPos = skin.getMenuPosition();
+                    if (menuPos != null && (menuPos.getX() <= 0 || menuPos.getY() <= 0)) {
+                        skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false);
                         menuPos = skin.getMenuPosition();
-                    } else {
-                        menuPos = skin.getMenuPosition();
-                        if (menuPos != null && (menuPos.getX() <= 0 || menuPos.getY() <= 0)) {
-                            skin.positionCaret(skin.getIndex(e), false);
-                            menuPos = skin.getMenuPosition();
-                        }
-                    }
-
-                    if (menuPos != null) {
-                        Point2D p = getControl().localToScene(menuPos);
-                        Scene scene = getControl().getScene();
-                        Window window = scene.getWindow();
-                        Point2D location = new Point2D(window.getX() + scene.getX() + p.getX(),
-                                                       window.getY() + scene.getY() + p.getY());
-                        screenX = location.getX();
-                        sceneX = p.getX();
-                        screenY = location.getY();
                     }
                 }
 
-                skin.populateContextMenu(contextMenu);
-                double menuWidth = contextMenu.prefWidth(-1);
-                double menuX = screenX - (IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0);
-                Screen currentScreen = com.sun.javafx.Utils.getScreenForPoint(screenX, 0);
-                Rectangle2D bounds = currentScreen.getBounds();
-
-                if (menuX < bounds.getMinX()) {
-                    getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
-                    getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
-                    contextMenu.show(getControl(), bounds.getMinX(), screenY);
-                } else if (screenX + menuWidth > bounds.getMaxX()) {
-                    double leftOver = menuWidth - ( bounds.getMaxX() - screenX);
-                    getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
-                    getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
-                    contextMenu.show(getControl(), screenX - leftOver, screenY);
-                } else {
-                    getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", 0);
-                    getControl().getProperties().put("CONTEXT_MENU_SCENE_X", 0);
-                    contextMenu.show(getControl(), menuX, screenY);
+                if (menuPos != null) {
+                    Point2D p = getControl().localToScene(menuPos);
+                    Scene scene = getControl().getScene();
+                    Window window = scene.getWindow();
+                    Point2D location = new Point2D(window.getX() + scene.getX() + p.getX(),
+                                                   window.getY() + scene.getY() + p.getY());
+                    screenX = location.getX();
+                    sceneX = p.getX();
+                    screenY = location.getY();
                 }
             }
+
+            skin.populateContextMenu(contextMenu);
+            double menuWidth = contextMenu.prefWidth(-1);
+            double menuX = screenX - (IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0);
+            Screen currentScreen = com.sun.javafx.Utils.getScreenForPoint(screenX, 0);
+            Rectangle2D bounds = currentScreen.getBounds();
+
+            if (menuX < bounds.getMinX()) {
+                getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
+                getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
+                contextMenu.show(getControl(), bounds.getMinX(), screenY);
+            } else if (screenX + menuWidth > bounds.getMaxX()) {
+                double leftOver = menuWidth - ( bounds.getMaxX() - screenX);
+                getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
+                getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
+                contextMenu.show(getControl(), screenX - leftOver, screenY);
+            } else {
+                getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", 0);
+                getControl().getProperties().put("CONTEXT_MENU_SCENE_X", 0);
+                contextMenu.show(getControl(), menuX, screenY);
+            }
         }
+
+        e.consume();
     }
 
 //    var hadFocus = false;
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/BehaviorSkinBase.java	Fri Jan 31 11:23:46 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/BehaviorSkinBase.java	Fri Jan 31 12:11:31 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
 import javafx.event.EventType;
 import javafx.scene.control.Control;
 import javafx.scene.control.SkinBase;
+import javafx.scene.input.ContextMenuEvent;
 import javafx.scene.input.MouseEvent;
 import javafx.util.Callback;
 import com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler;
@@ -100,6 +101,13 @@
             }
         }
     };
+
+    private final EventHandler<ContextMenuEvent> contextMenuHandler =
+            new EventHandler<ContextMenuEvent>() {
+                @Override public void handle(ContextMenuEvent event) {
+                    behavior.contextMenuRequested(event);
+                }
+            };
     
     /***************************************************************************
      *                                                                         *
@@ -130,6 +138,8 @@
         control.addEventHandler(MouseEvent.MOUSE_PRESSED, mouseHandler);
         control.addEventHandler(MouseEvent.MOUSE_RELEASED, mouseHandler);
         control.addEventHandler(MouseEvent.MOUSE_DRAGGED, mouseHandler);
+
+        control.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, contextMenuHandler);
     }
     
     
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Fri Jan 31 11:23:46 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Fri Jan 31 12:11:31 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1121,18 +1121,18 @@
         return (Text)paragraphNodes.getChildren().get(0);
     }
 
-    public HitInfo getIndex(MouseEvent e) {
+    public HitInfo getIndex(double x, double y) {
         // adjust the event to be in the same coordinate space as the
         // text content of the textInputControl
         Text textNode = getTextNode();
-        Point2D p = new Point2D(e.getX() - textNode.getLayoutX(), e.getY() - getTextTranslateY());
+        Point2D p = new Point2D(x - textNode.getLayoutX(), y - getTextTranslateY());
         HitInfo hit = textNode.impl_hitTestChar(translateCaretPosition(p));
         int pos = hit.getCharIndex();
         if (pos > 0) {
             int oldPos = textNode.getImpl_caretPosition();
             textNode.setImpl_caretPosition(pos);
             PathElement element = textNode.getImpl_caretShape()[0];
-            if (element instanceof MoveTo && ((MoveTo)element).getY() > e.getY() - getTextTranslateY()) {
+            if (element instanceof MoveTo && ((MoveTo)element).getY() > y - getTextTranslateY()) {
                 hit.setCharIndex(pos - 1);
             }
             textNode.setImpl_caretPosition(oldPos);
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Fri Jan 31 11:23:46 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Fri Jan 31 12:11:31 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -646,13 +646,13 @@
         updateCaretOff();
     }
 
-    public HitInfo getIndex(MouseEvent e) {
+    public HitInfo getIndex(double x, double y) {
         // adjust the event to be in the same coordinate space as the
         // text content of the textInputControl
         Point2D p;
 
-        p = new Point2D(e.getX() - textTranslateX.get() - snappedLeftInset(),
-                        e.getY() - snappedTopInset());
+        p = new Point2D(x - textTranslateX.get() - snappedLeftInset(),
+                        y - snappedTopInset());
         return textNode.impl_hitTestChar(translateCaretPosition(p));
     }
 
--- a/modules/controls/src/main/java/javafx/scene/control/Control.java	Fri Jan 31 11:23:46 2014 +1300
+++ b/modules/controls/src/main/java/javafx/scene/control/Control.java	Fri Jan 31 12:11:31 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -170,6 +170,8 @@
      */
     private final static EventHandler<ContextMenuEvent> contextMenuHandler = new EventHandler<ContextMenuEvent>() {
         @Override public void handle(ContextMenuEvent event) {
+            if (event.isConsumed()) return;
+
             // If a context menu was shown, consume the event to prevent multiple context menus
             Object source = event.getSource();
             if (source instanceof Control) {