changeset 1069:2f812ef410af

Embedded: Text input context menu.
author leifs
date Mon, 21 May 2012 11:31:06 -0700
parents 827c6ae0d1e1
children 862bbb9698c8
files javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/PasswordFieldBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextFieldBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/EmbeddedTextContextMenuContent.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextAreaSkin.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextInputControlSkin.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/embedded.css
diffstat 8 files changed, 174 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/PasswordFieldBehavior.java	Fri May 18 18:07:30 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/PasswordFieldBehavior.java	Mon May 21 11:31:06 2012 -0700
@@ -47,6 +47,9 @@
     protected void selectNextWord() { }
     protected void previousWord() { }
     protected void nextWord() { }
+    protected void selectWord() {
+        getControl().selectAll();
+    }
     protected void mouseDoubleClick(HitInfo hit) {
         getControl().selectAll();
     }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java	Fri May 18 18:07:30 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java	Mon May 21 11:31:06 2012 -0700
@@ -28,11 +28,15 @@
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.event.EventHandler;
+import javafx.geometry.Point2D;
+import javafx.scene.Scene;
 import javafx.scene.control.ContextMenu;
 import javafx.scene.control.TextArea;
 import javafx.scene.input.MouseButton;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.input.TouchEvent;
+import javafx.stage.Screen;
+import javafx.stage.Window;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -43,8 +47,6 @@
 import static javafx.scene.input.KeyCode.*;
 import static javafx.scene.input.KeyEvent.*;
 import static com.sun.javafx.PlatformUtil.*;
-import javafx.geometry.Side;
-import javafx.stage.Screen;
 
 
 /**
@@ -325,7 +327,7 @@
         }
     }
 
-    @Override public void mouseReleased(MouseEvent e) {        
+    @Override public void mouseReleased(final MouseEvent e) {
         final TextArea textArea = getControl();
         super.mouseReleased(e);
         // we never respond to events if disabled, but we do notify any onXXX
@@ -339,31 +341,58 @@
             }
             setCaretAnimating(true);
         }
-        if (e.getButton() == MouseButton.SECONDARY) {    
+        if (e.getButton() == MouseButton.SECONDARY) {
             if (contextMenu.isShowing()) {
-                contextMenu.hide();                
+                contextMenu.hide();
             } else {
+                double screenX = e.getScreenX();
+                double screenY = e.getScreenY();
+                double sceneX = e.getSceneX();
+
+                if (PlatformUtil.isEmbedded()) {
+                    Point2D menuPos;
+                    if (textArea.getSelection().getLength() == 0) {
+                        skin.positionCaret(skin.getIndex(e), 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 = skin.localToScene(menuPos);
+                        Scene scene = skin.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 screenX = e.getScreenX();
-                double sceneX = e.getSceneX();
-                double menuWidth = contextMenu.prefWidth(-1);             
-                double menuX = screenX - menuWidth/2;
+                double menuWidth = contextMenu.prefWidth(-1);
+                double menuX = screenX - (PlatformUtil.isEmbedded() ? (menuWidth / 2) : 0);
                 Screen currentScreen = com.sun.javafx.Utils.getScreenForPoint(0, 0);
                 double maxWidth = currentScreen.getVisualBounds().getWidth();
 
                 if (menuX < 0) {
                     skin.getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
                     skin.getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
-                    contextMenu.show(getControl(), 0, e.getScreenY());
+                    contextMenu.show(getControl(), 0, screenY);
                 } else if (screenX + menuWidth > maxWidth) {
                     double leftOver = menuWidth - (maxWidth - screenX);
                     skin.getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
                     skin.getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
-                    contextMenu.show(getControl(), screenX - leftOver, e.getScreenY());
+                    contextMenu.show(getControl(), screenX - leftOver, screenY);
                 } else {
                     skin.getProperties().put("CONTEXT_MENU_SCREEN_X", 0);
                     skin.getProperties().put("CONTEXT_MENU_SCENE_X", 0);
-                    contextMenu.show(getControl(), menuX, e.getScreenY());
+                    contextMenu.show(getControl(), menuX, screenY);
                 }
             }
         }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextFieldBehavior.java	Fri May 18 18:07:30 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextFieldBehavior.java	Mon May 21 11:31:06 2012 -0700
@@ -32,12 +32,17 @@
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
 import javafx.geometry.HorizontalDirection;
+import javafx.geometry.Point2D;
+import javafx.scene.Scene;
 import javafx.scene.control.ContextMenu;
 import javafx.scene.control.IndexRange;
 import javafx.scene.control.TextField;
 import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseButton;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.input.TouchEvent;
+import javafx.stage.Screen;
+import javafx.stage.Window;
 import javafx.util.Duration;
 import java.util.List;
 
@@ -46,8 +51,6 @@
 import com.sun.javafx.scene.text.HitInfo;
 
 import static com.sun.javafx.PlatformUtil.*;
-import javafx.scene.input.MouseButton;
-import javafx.stage.Screen;
 
 /**
  * Text field behavior.
@@ -275,34 +278,61 @@
             }
             setCaretAnimating(true);
         }
-        if (e.getButton() == MouseButton.SECONDARY) {    
+        if (e.getButton() == MouseButton.SECONDARY) {
             if (contextMenu.isShowing()) {
-                contextMenu.hide();                
+                contextMenu.hide();
             } else {
+                double screenX = e.getScreenX();
+                double screenY = e.getScreenY();
+                double sceneX = e.getSceneX();
+
+                if (PlatformUtil.isEmbedded()) {
+                    Point2D menuPos;
+                    if (textField.getSelection().getLength() == 0) {
+                        skin.positionCaret(skin.getIndex(e), 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 = skin.localToScene(menuPos);
+                        Scene scene = skin.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 screenX = e.getScreenX();
-                double sceneX = e.getSceneX();
-                double menuWidth = contextMenu.prefWidth(-1);                
-                double menuX = screenX - menuWidth/2;
+                double menuWidth = contextMenu.prefWidth(-1);
+                double menuX = screenX - (PlatformUtil.isEmbedded() ? (menuWidth / 2) : 0);
                 Screen currentScreen = com.sun.javafx.Utils.getScreenForPoint(0, 0);
                 double maxWidth = currentScreen.getVisualBounds().getWidth();
 
                 if (menuX < 0) {
                     skin.getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
                     skin.getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
-                    contextMenu.show(getControl(), 0, e.getScreenY());
+                    contextMenu.show(getControl(), 0, screenY);
                 } else if (screenX + menuWidth > maxWidth) {
                     double leftOver = menuWidth - (maxWidth - screenX);
                     skin.getProperties().put("CONTEXT_MENU_SCREEN_X", screenX);
                     skin.getProperties().put("CONTEXT_MENU_SCENE_X", sceneX);
-                    contextMenu.show(getControl(), screenX - leftOver, e.getScreenY());
+                    contextMenu.show(getControl(), screenX - leftOver, screenY);
                 } else {
                     skin.getProperties().put("CONTEXT_MENU_SCREEN_X", 0);
                     skin.getProperties().put("CONTEXT_MENU_SCENE_X", 0);
-                    contextMenu.show(getControl(), menuX, e.getScreenY());
+                    contextMenu.show(getControl(), menuX, screenY);
                 }
             }
-        }        
+        }
     }
 
 //    var hadFocus = false;
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/EmbeddedTextContextMenuContent.java	Fri May 18 18:07:30 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/EmbeddedTextContextMenuContent.java	Mon May 21 11:31:06 2012 -0700
@@ -176,7 +176,11 @@
 
         @Override public void fire() {
             Event.fireEvent(item, new ActionEvent());
-            hideAllMenus(item);
+            if (Boolean.TRUE.equals((Boolean)item.getProperties().get("refreshMenu"))) {
+                //refreshMenu();
+            } else {
+                hideAllMenus(item);
+            }
         }
     }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Fri May 18 18:07:30 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Mon May 21 11:31:06 2012 -0700
@@ -275,7 +275,8 @@
                         selectionHandle2.setLayoutY(b.getMaxY());
                     } else {
                         selectionHandle1.setLayoutX(b.getMinX() - selectionHandle1.getWidth() / 2);
-                        selectionHandle1.setLayoutY(b.getMinY() - selectionHandle1.getHeight());
+                        //selectionHandle1.setLayoutY(b.getMinY() - selectionHandle1.getHeight());
+                        selectionHandle1.setLayoutY(b.getMaxY());
                     }
                 }
             }
@@ -341,7 +342,8 @@
                 if (selection.getLength() > 0) {
                     if (caretPos < anchorPos) {
                         selectionHandle1.setLayoutX(b.getMinX() - selectionHandle1.getWidth() / 2);
-                        selectionHandle1.setLayoutY(b.getMinY() - selectionHandle1.getHeight());
+                        //selectionHandle1.setLayoutY(b.getMinY() - selectionHandle1.getHeight());
+                        selectionHandle1.setLayoutY(b.getMaxY());
                     } else {
                         selectionHandle2.setLayoutX(b.getMinX() - selectionHandle2.getWidth() / 2);
                         selectionHandle2.setLayoutY(b.getMaxY());
@@ -627,7 +629,7 @@
         if (textArea.isFocused()) setCaretAnimating(true);
 
         if (PlatformUtil.isEmbedded()) {
-            selectionHandle1.setRotate(180);
+            //selectionHandle1.setRotate(180);
 
             EventHandler<MouseEvent> handlePressHandler = new EventHandler<MouseEvent>() {
                 @Override public void handle(MouseEvent e) {
@@ -668,7 +670,8 @@
                     Text textNode = getTextNode();
                     Point2D tp = textNode.localToScene(0, 0);
                     Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle1.getWidth() / 2,
-                                            e.getSceneY() - tp.getY() - pressY + selectionHandle1.getHeight() + 5);
+                                            //e.getSceneY() - tp.getY() - pressY + selectionHandle1.getHeight() + 5);
+                                            e.getSceneY() - tp.getY() - pressY - 6);
                     HitInfo hit = textNode.impl_hitTestChar(translateCaretPosition(p));
                     int pos = hit.getCharIndex();
                     if (textArea.getAnchor() < textArea.getCaretPosition()) {
@@ -697,7 +700,7 @@
                     TextArea textArea = getSkinnable();
                     Text textNode = getTextNode();
                     Point2D tp = textNode.localToScene(0, 0);
-                    Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle1.getWidth() / 2,
+                    Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle2.getWidth() / 2,
                                             e.getSceneY() - tp.getY() - pressY - 6);
                     HitInfo hit = textNode.impl_hitTestChar(translateCaretPosition(p));
                     int pos = hit.getCharIndex();
@@ -1272,4 +1275,15 @@
 //            scrollAfterDelete(textMaxXOld, caretMaxXOld);
         }
     }
+
+    @Override public Point2D getMenuPosition() {
+        contentView.layoutChildren();
+        Point2D p = super.getMenuPosition();
+        if (p != null) {
+            Insets padding = contentView.getInsets();
+            p = new Point2D(Math.max(0, p.getX() - padding.getLeft() - getSkinnable().getScrollLeft()),
+                            Math.max(0, p.getY() - padding.getTop() - getSkinnable().getScrollTop()));
+        }
+        return p;
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Fri May 18 18:07:30 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Mon May 21 11:31:06 2012 -0700
@@ -400,7 +400,7 @@
                 @Override public void handle(MouseEvent e) {
                     TextField textField = getSkinnable();
                     Point2D tp = textNode.localToScene(0, 0);
-                    Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle1.getWidth() / 2,
+                    Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle2.getWidth() / 2,
                                             e.getSceneY() - tp.getY() - pressY - 6);
                     HitInfo hit = textNode.impl_hitTestChar(translateCaretPosition(p));
                     int pos = hit.getCharIndex();
@@ -759,4 +759,14 @@
             caretHandle.setLayoutY(b.getMaxY() - 3);
         }
     }
+
+    @Override public Point2D getMenuPosition() {
+        Point2D p = super.getMenuPosition();
+        if (p != null) {
+            Insets padding = getInsets();
+            p = new Point2D(Math.max(0, p.getX() - textNode.getLayoutX() - padding.getLeft() + textTranslateX.get()),
+                            Math.max(0, p.getY() - textNode.getLayoutY() - padding.getTop()));
+        }
+        return p;
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextInputControlSkin.java	Fri May 18 18:07:30 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextInputControlSkin.java	Mon May 21 11:31:06 2012 -0700
@@ -234,6 +234,24 @@
     protected StackPane selectionHandle1 = null;
     protected StackPane selectionHandle2 = null;
 
+    public Point2D getMenuPosition() {
+        if (PlatformUtil.isEmbedded()) {
+            if (caretHandle.isVisible()) {
+                return new Point2D(caretHandle.getLayoutX() + caretHandle.getWidth() / 2,
+                                   caretHandle.getLayoutY());
+            } else if (selectionHandle1.isVisible() && selectionHandle2.isVisible()) {
+                return new Point2D((selectionHandle1.getLayoutX() + selectionHandle1.getWidth() / 2 +
+                                    selectionHandle2.getLayoutX() + selectionHandle2.getWidth() / 2) / 2,
+                                   selectionHandle2.getLayoutY() + selectionHandle2.getHeight() / 2);
+            } else {
+                return null;
+            }
+        } else {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+
     private static boolean useFXVK = PlatformUtil.isEmbedded();
 
     /* For testing only */
@@ -336,7 +354,10 @@
 
             caretHandle.getStyleClass().setAll("caret-handle");
             selectionHandle1.getStyleClass().setAll("selection-handle");
-            selectionHandle2.getStyleClass().add("selection-handle");
+            selectionHandle2.getStyleClass().setAll("selection-handle");
+
+            selectionHandle1.setId("selection-handle-1");
+            selectionHandle2.setId("selection-handle-2");
 
 //             textInput.focusedProperty().addListener(new InvalidationListener() {
 //                 @Override public void invalidated(Observable observable) {
@@ -427,6 +448,10 @@
      */
     public Rectangle2D getCharacterBounds(int index) { return null; }
 
+    public double getLineHeight() {
+        return fontMetrics.get().getLineHeight();
+    }
+
     /**
      * Ensures that the character at a given index is visible.
      *
@@ -559,7 +584,7 @@
             blink.set(true);
         }
     }
-    
+
     class ContextMenuItem extends MenuItem {
         ContextMenuItem(final String action) {
             super(getString("TextInputControl.menu." + action));
@@ -578,14 +603,15 @@
     final MenuItem pasteMI  = new ContextMenuItem("Paste");
     final MenuItem deleteMI = new ContextMenuItem("DeleteSelection");
     final MenuItem selectWordMI = new ContextMenuItem("SelectWord");
-    final MenuItem selectAllMI = new ContextMenuItem("SelectAll");    
-       
+    final MenuItem selectAllMI = new ContextMenuItem("SelectAll");
+
     public void populateContextMenu(ContextMenu contextMenu) {
+        boolean hasText = (getSkinnable().getLength() > 0);
         boolean hasSelection = (getSkinnable().getSelection().getLength() > 0);
         boolean maskText = (maskText("A") != "A");
+        ObservableList<MenuItem> items = contextMenu.getItems();
 
         if (PlatformUtil.isEmbedded()) {
-            ObservableList<MenuItem> items = contextMenu.getItems();
             items.clear();
             if (!maskText && hasSelection) {
                 items.add(cutMI);
@@ -594,11 +620,19 @@
             if (Clipboard.getSystemClipboard().hasString()) {
                 items.add(pasteMI);
             }
-            if (!hasSelection) {
-                items.add(selectWordMI);
+            if (hasText) {
+                if (!hasSelection) {
+                    items.add(selectWordMI);
+                }
+                items.add(selectAllMI);
             }
-            items.add(selectAllMI);
+            selectWordMI.getProperties().put("refreshMenu", Boolean.TRUE);
+            selectAllMI.getProperties().put("refreshMenu", Boolean.TRUE);
         } else {
+            if (items.size() == 0) {
+                items.addAll(undoMI, redoMI, cutMI, copyMI, pasteMI, deleteMI,
+                             new SeparatorMenuItem(), selectAllMI);
+            }
             undoMI.setDisable(!getBehavior().canUndo());
             redoMI.setDisable(!getBehavior().canRedo());
             cutMI.setDisable(maskText || !hasSelection);
@@ -606,8 +640,8 @@
             pasteMI.setDisable(!Clipboard.getSystemClipboard().hasString());
             deleteMI.setDisable(!hasSelection);
         }
-    }    
-    
+    }
+
     private static class StyleableProperties {
         private static final StyleableProperty<TextInputControlSkin,Font> FONT =
            new StyleableProperty.FONT<TextInputControlSkin>("-fx-font", Font.getDefault()) {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/embedded.css	Fri May 18 18:07:30 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/embedded.css	Mon May 21 11:31:06 2012 -0700
@@ -106,9 +106,9 @@
 }
 
 .selection-handle {
-    -fx-background-color: #0071bc /*-fx-accent*/,
+    -fx-background-color: transparent, #0071bc /*-fx-accent*/,
                           linear-gradient(to bottom, #0063AA 0%, #008AED 100%);
-    -fx-background-insets: 0, 1;
+    -fx-background-insets: 0, 0, 1;
     -fx-shape: "M10.974,2.579L19,12.358V28H3V12.356L10.974,2.579z";
 /*
     -fx-shape: "M10.972,1L2,12v17h18V12L10.972,1L10.972,1z";
@@ -118,6 +118,13 @@
     -fx-cursor: hand;
 }
 
+#selection-handle-1 {
+    -fx-background-insets: -4 0 -4 -4, 0, 1;
+}
+
+#selection-handle-2 {
+    -fx-background-insets: -4 -4 -4 0, 0, 1;
+}
 
 /*******************************************************************************
  *                                                                             *