modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

*** 25,159 **** package com.sun.javafx.scene.control.behavior; import com.sun.javafx.PlatformUtil; import com.sun.javafx.geom.transform.Affine3D; ! import com.sun.javafx.scene.control.skin.TextAreaSkin; ! import com.sun.javafx.scene.text.HitInfo; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Bounds; import javafx.geometry.Point2D; import javafx.geometry.Rectangle2D; import javafx.scene.Scene; 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; import javafx.stage.Window; ! import java.util.ArrayList; ! import java.util.List; import static com.sun.javafx.PlatformUtil.isMac; import static com.sun.javafx.PlatformUtil.isWindows; import static javafx.scene.input.KeyCode.*; - import static javafx.scene.input.KeyEvent.KEY_PRESSED; /** * Text area behavior. */ public class TextAreaBehavior extends TextInputControlBehavior<TextArea> { ! /************************************************************************** ! * Setup KeyBindings * ! *************************************************************************/ ! protected static final List<KeyBinding> TEXT_AREA_BINDINGS = new ArrayList<KeyBinding>(); ! static { ! TEXT_AREA_BINDINGS.add(new KeyBinding(HOME, KEY_PRESSED, "LineStart")); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(END, KEY_PRESSED, "LineEnd")); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "PreviousLine")); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "PreviousLine")); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "NextLine")); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "NextLine")); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_UP, KEY_PRESSED, "PreviousPage")); // new ! TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_DOWN, KEY_PRESSED, "NextPage")); // new ! TEXT_AREA_BINDINGS.add(new KeyBinding(ENTER, KEY_PRESSED, "InsertNewLine")); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(TAB, KEY_PRESSED, "TraverseOrInsertTab")); // changed ! ! TEXT_AREA_BINDINGS.add(new KeyBinding(HOME, KEY_PRESSED, "SelectLineStart").shift()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(END, KEY_PRESSED, "SelectLineEnd").shift()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectPreviousLine").shift()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectPreviousLine").shift()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectNextLine").shift()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectNextLine").shift()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_UP, KEY_PRESSED, "SelectPreviousPage").shift()); // new ! TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_DOWN, KEY_PRESSED, "SelectNextPage").shift()); // new ! // Platform specific settings ! if (isMac()) { ! TEXT_AREA_BINDINGS.add(new KeyBinding(LEFT, KEY_PRESSED, "LineStart").shortcut()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED, "LineStart").shortcut()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED, "LineEnd").shortcut()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED, "LineEnd").shortcut()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "Home").shortcut()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "Home").shortcut()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "End").shortcut()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "End").shortcut()); ! ! TEXT_AREA_BINDINGS.add(new KeyBinding(LEFT, KEY_PRESSED, "SelectLineStartExtend").shift().shortcut()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED, "SelectLineStartExtend").shift().shortcut()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED, "SelectLineEndExtend").shift().shortcut()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED, "SelectLineEndExtend").shift().shortcut()); // changed ! TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectHomeExtend").shortcut().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectHomeExtend").shortcut().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectEndExtend").shortcut().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectEndExtend").shortcut().shift()); ! ! TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "ParagraphStart").alt()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "ParagraphStart").alt()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "ParagraphEnd").alt()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "ParagraphEnd").alt()); ! ! TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectParagraphStart").alt().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectParagraphStart").alt().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectParagraphEnd").alt().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectParagraphEnd").alt().shift()); ! } else { ! TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "ParagraphStart").ctrl()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "ParagraphStart").ctrl()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "ParagraphEnd").ctrl()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "ParagraphEnd").ctrl()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectParagraphStart").ctrl().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectParagraphStart").ctrl().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectParagraphEnd").ctrl().shift()); ! TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectParagraphEnd").ctrl().shift()); ! } ! // Add the other standard key bindings in ! TEXT_AREA_BINDINGS.addAll(TextInputControlBindings.BINDINGS); ! // However, we want to consume other key press / release events too, for ! // things that would have been handled by the InputCharacter normally ! TEXT_AREA_BINDINGS.add(new KeyBinding(null, KEY_PRESSED, "Consume")); ! } ! private TextAreaSkin skin; private ContextMenu contextMenu; private TwoLevelFocusBehavior tlFocus; /************************************************************************** * Constructors * *************************************************************************/ ! public TextAreaBehavior(final TextArea textArea) { ! super(textArea, TEXT_AREA_BINDINGS); contextMenu = new ContextMenu(); ! if (IS_TOUCH_SUPPORTED) { contextMenu.getStyleClass().add("text-input-context-menu"); } // Register for change events ! textArea.focusedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { // NOTE: The code in this method is *almost* and exact copy of what is in TextFieldBehavior. // The only real difference is that TextFieldBehavior selects all the text when the control // receives focus (when not gained by mouse click), whereas TextArea doesn't, and also the // TextArea doesn't lose selection on focus lost, whereas the TextField does. ! final TextArea textArea = getControl(); if (textArea.isFocused()) { if (PlatformUtil.isIOS()) { // Special handling of focus on iOS is required to allow to // control native keyboard, because native keyboard is popped-up only when native // text component gets focus. When we have JFX keyboard we can remove this code --- 25,230 ---- package com.sun.javafx.scene.control.behavior; import com.sun.javafx.PlatformUtil; import com.sun.javafx.geom.transform.Affine3D; ! import com.sun.javafx.scene.control.Properties; ! import javafx.scene.control.skin.TextAreaSkin; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Bounds; import javafx.geometry.Point2D; import javafx.geometry.Rectangle2D; import javafx.scene.Scene; import javafx.scene.control.ContextMenu; import javafx.scene.control.TextArea; + import com.sun.javafx.scene.control.skin.Utils; import javafx.scene.input.ContextMenuEvent; + import com.sun.javafx.scene.control.inputmap.InputMap; + import com.sun.javafx.scene.control.inputmap.KeyBinding; + import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.stage.Screen; import javafx.stage.Window; ! import java.util.function.Predicate; ! ! import static javafx.scene.control.skin.TextAreaSkin.TextPosInfo; import static com.sun.javafx.PlatformUtil.isMac; import static com.sun.javafx.PlatformUtil.isWindows; + import static javafx.scene.control.skin.TextInputControlSkin.TextUnit; + import static javafx.scene.control.skin.TextInputControlSkin.Direction; import static javafx.scene.input.KeyCode.*; /** * Text area behavior. */ public class TextAreaBehavior extends TextInputControlBehavior<TextArea> { ! // /************************************************************************** ! // * Setup KeyBindings * ! // *************************************************************************/ ! // protected static final List<KeyBinding> TEXT_AREA_BINDINGS = new ArrayList<KeyBinding>(); ! // static { ! // TEXT_AREA_BINDINGS.add(new KeyBinding(HOME, KEY_PRESSED, "LineStart")); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(END, KEY_PRESSED, "LineEnd")); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "PreviousLine")); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "PreviousLine")); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "NextLine")); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "NextLine")); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_UP, KEY_PRESSED, "PreviousPage")); // new ! // TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_DOWN, KEY_PRESSED, "NextPage")); // new ! // TEXT_AREA_BINDINGS.add(new KeyBinding(ENTER, KEY_PRESSED, "InsertNewLine")); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(TAB, KEY_PRESSED, "TraverseOrInsertTab")); // changed ! // ! // TEXT_AREA_BINDINGS.add(new KeyBinding(HOME, KEY_PRESSED, "SelectLineStart").shift()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(END, KEY_PRESSED, "SelectLineEnd").shift()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectPreviousLine").shift()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectPreviousLine").shift()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectNextLine").shift()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectNextLine").shift()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_UP, KEY_PRESSED, "SelectPreviousPage").shift()); // new ! // TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_DOWN, KEY_PRESSED, "SelectNextPage").shift()); // new ! // // Platform specific settings ! // if (isMac()) { ! // TEXT_AREA_BINDINGS.add(new KeyBinding(LEFT, KEY_PRESSED, "LineStart").shortcut()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED, "LineStart").shortcut()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED, "LineEnd").shortcut()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED, "LineEnd").shortcut()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "Home").shortcut()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "Home").shortcut()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "End").shortcut()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "End").shortcut()); ! // ! // TEXT_AREA_BINDINGS.add(new KeyBinding(LEFT, KEY_PRESSED, "SelectLineStartExtend").shift().shortcut()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED, "SelectLineStartExtend").shift().shortcut()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED, "SelectLineEndExtend").shift().shortcut()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED, "SelectLineEndExtend").shift().shortcut()); // changed ! // TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectHomeExtend").shortcut().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectHomeExtend").shortcut().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectEndExtend").shortcut().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectEndExtend").shortcut().shift()); ! // ! // TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "ParagraphStart").alt()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "ParagraphStart").alt()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "ParagraphEnd").alt()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "ParagraphEnd").alt()); ! // ! // TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectParagraphStart").alt().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectParagraphStart").alt().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectParagraphEnd").alt().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectParagraphEnd").alt().shift()); ! // } else { ! // TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "ParagraphStart").ctrl()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "ParagraphStart").ctrl()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "ParagraphEnd").ctrl()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "ParagraphEnd").ctrl()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectParagraphStart").ctrl().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectParagraphStart").ctrl().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectParagraphEnd").ctrl().shift()); ! // TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectParagraphEnd").ctrl().shift()); ! // } ! // // Add the other standard key bindings in ! // TEXT_AREA_BINDINGS.addAll(TextInputControlBindings.BINDINGS); ! // // However, we want to consume other key press / release events too, for ! // // things that would have been handled by the InputCharacter normally ! // TEXT_AREA_BINDINGS.add(new KeyBinding(null, KEY_PRESSED, "Consume")); ! // } ! // private TextAreaSkin skin; private ContextMenu contextMenu; private TwoLevelFocusBehavior tlFocus; /************************************************************************** * Constructors * *************************************************************************/ ! public TextAreaBehavior(final TextArea c) { ! super(c); contextMenu = new ContextMenu(); ! if (Properties.IS_TOUCH_SUPPORTED) { contextMenu.getStyleClass().add("text-input-context-menu"); } + // some of the mappings are only valid when the control is editable, or + // only on certain platforms, so we create the following predicates that filters out the mapping when the + // control is not in the correct state / on the correct platform + final Predicate<KeyEvent> validWhenEditable = e -> !c.isEditable(); + final Predicate<KeyEvent> validOnMac = e -> !PlatformUtil.isMac(); + final Predicate<KeyEvent> validOnWindows = e -> !PlatformUtil.isWindows(); + final Predicate<KeyEvent> validOnLinux = e -> !PlatformUtil.isLinux(); + + // Add these bindings as a child input map, so they take precedence + InputMap<TextArea> textAreaInputMap = new InputMap<>(c); + textAreaInputMap.getMappings().addAll( + keyMapping(HOME, e -> lineStart(false)), + keyMapping(END, e -> lineEnd(false)), + keyMapping(UP, e -> skin.moveCaret(TextUnit.LINE, Direction.UP, false)), + keyMapping(DOWN, e -> skin.moveCaret(TextUnit.LINE, Direction.DOWN, false)), + keyMapping(PAGE_UP, e -> skin.moveCaret(TextUnit.PAGE, Direction.UP, false)), + keyMapping(PAGE_DOWN, e -> skin.moveCaret(TextUnit.PAGE, Direction.DOWN, false)), + + keyMapping(new KeyBinding(HOME).shift(), e -> lineStart(true)), + keyMapping(new KeyBinding(END).shift(), e -> lineEnd(true)), + keyMapping(new KeyBinding(UP).shift(), e -> skin.moveCaret(TextUnit.LINE, Direction.UP, true)), + keyMapping(new KeyBinding(DOWN).shift(), e -> skin.moveCaret(TextUnit.LINE, Direction.DOWN, true)), + keyMapping(new KeyBinding(PAGE_UP).shift(), e -> skin.moveCaret(TextUnit.PAGE, Direction.UP, true)), + keyMapping(new KeyBinding(PAGE_DOWN).shift(), e -> skin.moveCaret(TextUnit.PAGE, Direction.DOWN, true)), + + // editing-only mappings + keyMapping(new KeyBinding(ENTER), e -> insertNewLine(), validWhenEditable), + keyMapping(new KeyBinding(TAB), e -> insertTab(), validWhenEditable) + ); + addDefaultChildMap(getInputMap(), textAreaInputMap); + + // mac os specific mappings + InputMap<TextArea> macOsInputMap = new InputMap<>(c); + macOsInputMap.setInterceptor(e -> !PlatformUtil.isMac()); + macOsInputMap.getMappings().addAll( + // Mac OS specific mappings + keyMapping(new KeyBinding(LEFT).shortcut(), e -> lineStart(false)), + keyMapping(new KeyBinding(RIGHT).shortcut(), e -> lineEnd(false)), + keyMapping(new KeyBinding(UP).shortcut(), e -> c.home()), + keyMapping(new KeyBinding(DOWN).shortcut(), e -> c.end()), + + keyMapping(new KeyBinding(LEFT).shortcut().shift(), e -> lineStart(true)), + keyMapping(new KeyBinding(RIGHT).shortcut().shift(), e -> lineEnd(true)), + keyMapping(new KeyBinding(UP).shortcut().shift(), e -> selectHomeExtend()), + keyMapping(new KeyBinding(DOWN).shortcut().shift(), e -> selectEndExtend()), + + keyMapping(new KeyBinding(UP).alt(), e -> skin.moveCaret(TextUnit.PARAGRAPH, Direction.UP, false)), + keyMapping(new KeyBinding(DOWN).alt(), e -> skin.moveCaret(TextUnit.PARAGRAPH, Direction.DOWN, false)), + keyMapping(new KeyBinding(UP).alt().shift(), e -> skin.moveCaret(TextUnit.PARAGRAPH, Direction.UP, true)), + keyMapping(new KeyBinding(DOWN).alt().shift(), e -> skin.moveCaret(TextUnit.PARAGRAPH, Direction.DOWN, true)) + ); + addDefaultChildMap(textAreaInputMap, macOsInputMap); + + // windows / linux specific mappings + InputMap<TextArea> nonMacOsInputMap = new InputMap<>(c); + nonMacOsInputMap.setInterceptor(e -> PlatformUtil.isMac()); + nonMacOsInputMap.getMappings().addAll( + keyMapping(new KeyBinding(UP).ctrl(), e -> skin.moveCaret(TextUnit.PARAGRAPH, Direction.UP, false)), + keyMapping(new KeyBinding(DOWN).ctrl(), e -> skin.moveCaret(TextUnit.PARAGRAPH, Direction.DOWN, false)), + keyMapping(new KeyBinding(UP).ctrl().shift(), e -> skin.moveCaret(TextUnit.PARAGRAPH, Direction.UP, true)), + keyMapping(new KeyBinding(DOWN).ctrl().shift(), e -> skin.moveCaret(TextUnit.PARAGRAPH, Direction.DOWN, true)) + ); + addDefaultChildMap(textAreaInputMap, nonMacOsInputMap); + + addKeyPadMappings(textAreaInputMap); + // Register for change events ! c.focusedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { // NOTE: The code in this method is *almost* and exact copy of what is in TextFieldBehavior. // The only real difference is that TextFieldBehavior selects all the text when the control // receives focus (when not gained by mouse click), whereas TextArea doesn't, and also the // TextArea doesn't lose selection on focus lost, whereas the TextField does. ! final TextArea textArea = getNode(); if (textArea.isFocused()) { if (PlatformUtil.isIOS()) { // Special handling of focus on iOS is required to allow to // control native keyboard, because native keyboard is popped-up only when native // text component gets focus. When we have JFX keyboard we can remove this code
*** 184,195 **** } } }); // Only add this if we're on an embedded platform that supports 5-button navigation ! if (com.sun.javafx.scene.control.skin.Utils.isTwoLevelFocus()) { ! tlFocus = new TwoLevelFocusBehavior(textArea); // needs to be last. } } @Override public void dispose() { if (tlFocus != null) tlFocus.dispose(); --- 255,266 ---- } } }); // Only add this if we're on an embedded platform that supports 5-button navigation ! if (Utils.isTwoLevelFocus()) { ! tlFocus = new TwoLevelFocusBehavior(c); // needs to be last. } } @Override public void dispose() { if (tlFocus != null) tlFocus.dispose();
*** 199,310 **** // An unholy back-reference! public void setTextAreaSkin(TextAreaSkin skin) { this.skin = skin; } ! /************************************************************************** ! * Key handling implementation * ! *************************************************************************/ ! ! @Override public void callAction(String name) { ! final TextArea textInputControl = getControl(); ! ! boolean done = false; ! ! if (textInputControl.isEditable()) { ! // fnCaretAnim(false); ! // setCaretOpacity(1.0); setEditing(true); ! done = true; ! if ("InsertNewLine".equals(name)) insertNewLine(); ! else if ("TraverseOrInsertTab".equals(name)) insertTab(); ! else { ! done = false; ! } setEditing(false); } - if (!done) { - done = true; - if ("LineStart".equals(name)) lineStart(false, false); - else if ("LineEnd".equals(name)) lineEnd(false, false); - else if ("SelectLineStart".equals(name)) lineStart(true, false); - else if ("SelectLineStartExtend".equals(name)) lineStart(true, true); - else if ("SelectLineEnd".equals(name)) lineEnd(true, false); - else if ("SelectLineEndExtend".equals(name)) lineEnd(true, true); - else if ("PreviousLine".equals(name)) skin.previousLine(false); - else if ("NextLine".equals(name)) skin.nextLine(false); - else if ("SelectPreviousLine".equals(name)) skin.previousLine(true); - else if ("SelectNextLine".equals(name)) skin.nextLine(true); - - else if ("ParagraphStart".equals(name)) skin.paragraphStart(true, false); - else if ("ParagraphEnd".equals(name)) skin.paragraphEnd(true, isWindows(), false); - else if ("SelectParagraphStart".equals(name)) skin.paragraphStart(true, true); - else if ("SelectParagraphEnd".equals(name)) skin.paragraphEnd(true, isWindows(), true); - - else if ("PreviousPage".equals(name)) skin.previousPage(false); - else if ("NextPage".equals(name)) skin.nextPage(false); - else if ("SelectPreviousPage".equals(name)) skin.previousPage(true); - else if ("SelectNextPage".equals(name)) skin.nextPage(true); - else if ("TraverseOrInsertTab".equals(name)) { - // RT-40312: Non-editabe mode means traverse instead of insert. - name = "TraverseNext"; - done = false; - } else { - done = false; - } - } - // fnCaretAnim(true); - - if (!done) { - super.callAction(name); - } - } - - private void insertNewLine() { - TextArea textArea = getControl(); - textArea.replaceSelection("\n"); - } - private void insertTab() { ! TextArea textArea = getControl(); ! textArea.replaceSelection("\t"); } @Override protected void deleteChar(boolean previous) { ! skin.deleteChar(previous); } @Override protected void deleteFromLineStart() { ! TextArea textArea = getControl(); int end = textArea.getCaretPosition(); if (end > 0) { ! lineStart(false, false); int start = textArea.getCaretPosition(); if (end > start) { replaceText(start, end, ""); } } } ! private void lineStart(boolean select, boolean extendSelection) { ! skin.lineStart(select, extendSelection); } ! private void lineEnd(boolean select, boolean extendSelection) { ! skin.lineEnd(select, extendSelection); ! } ! ! protected void scrollCharacterToVisible(int index) { ! // TODO this method should be removed when TextAreaSkin ! // TODO is refactored to no longer need it. ! skin.scrollCharacterToVisible(index); } @Override protected void replaceText(int start, int end, String txt) { ! getControl().replaceText(start, end, txt); } /** * If the focus is gained via response to a mouse click, then we don't * want to select all the text even if selectOnFocus is true. --- 270,322 ---- // An unholy back-reference! public void setTextAreaSkin(TextAreaSkin skin) { this.skin = skin; } ! private void insertNewLine() { setEditing(true); ! getNode().replaceSelection("\n"); setEditing(false); } private void insertTab() { ! setEditing(true); ! getNode().replaceSelection("\t"); ! setEditing(false); } @Override protected void deleteChar(boolean previous) { ! if (previous) { ! getNode().deletePreviousChar(); ! } else { ! getNode().deleteNextChar(); ! } } @Override protected void deleteFromLineStart() { ! TextArea textArea = getNode(); int end = textArea.getCaretPosition(); if (end > 0) { ! lineStart(false); int start = textArea.getCaretPosition(); if (end > start) { replaceText(start, end, ""); } } } ! private void lineStart(boolean select) { ! skin.moveCaret(TextUnit.LINE, Direction.BEGINNING, select); } ! private void lineEnd(boolean select) { ! skin.moveCaret(TextUnit.LINE, Direction.END, select); } @Override protected void replaceText(int start, int end, String txt) { ! getNode().replaceText(start, end, txt); } /** * If the focus is gained via response to a mouse click, then we don't * want to select all the text even if selectOnFocus is true.
*** 312,323 **** private boolean focusGainedByMouseClick = false; // TODO!! private boolean shiftDown = false; private boolean deferClick = false; @Override public void mousePressed(MouseEvent e) { ! TextArea textArea = getControl(); ! super.mousePressed(e); // We never respond to events if disabled if (!textArea.isDisabled()) { // If the text field doesn't have focus, then we'll attempt to set // the focus and we'll indicate that we gained focus by a mouse // click, TODO which will then NOT honor the selectOnFocus variable --- 324,334 ---- private boolean focusGainedByMouseClick = false; // TODO!! private boolean shiftDown = false; private boolean deferClick = false; @Override public void mousePressed(MouseEvent e) { ! TextArea textArea = getNode(); // We never respond to events if disabled if (!textArea.isDisabled()) { // If the text field doesn't have focus, then we'll attempt to set // the focus and we'll indicate that we gained focus by a mouse // click, TODO which will then NOT honor the selectOnFocus variable
*** 332,343 **** // only if there is no selection should we see the caret // setCaretOpacity(if (textInputControl.dot == textInputControl.mark) then 1.0 else 0.0); // if the primary button was pressed if (e.getButton() == MouseButton.PRIMARY && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown())) { ! 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(); final int caretPosition = textArea.getCaretPosition(); if (e.getClickCount() < 2 && (e.isSynthesized() || --- 343,354 ---- // only if there is no selection should we see the caret // setCaretOpacity(if (textInputControl.dot == textInputControl.mark) then 1.0 else 0.0); // if the primary button was pressed if (e.getButton() == MouseButton.PRIMARY && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown())) { ! TextPosInfo hit = skin.getIndex(e.getX(), e.getY()); ! int i = Utils.getHitInsertionIndex(hit, textArea.textProperty().getValueSafe()); // int i = skin.getInsertionPoint(e.getX(), e.getY()); final int anchor = textArea.getAnchor(); final int caretPosition = textArea.getCaretPosition(); if (e.getClickCount() < 2 && (e.isSynthesized() ||
*** 353,363 **** // TODO start a timer such that after some millis we // switch into text dragging mode, change the cursor // to indicate the text can be dragged, etc. } else if (!(e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown() || e.isShortcutDown())) { switch (e.getClickCount()) { ! case 1: skin.positionCaret(hit, false, false); break; case 2: mouseDoubleClick(hit); break; case 3: mouseTripleClick(hit); break; default: // no-op } } else if (e.isShiftDown() && !(e.isControlDown() || e.isAltDown() || e.isMetaDown() || e.isShortcutDown()) && e.getClickCount() == 1) { --- 364,374 ---- // TODO start a timer such that after some millis we // switch into text dragging mode, change the cursor // to indicate the text can be dragged, etc. } else if (!(e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown() || e.isShortcutDown())) { switch (e.getClickCount()) { ! case 1: skin.positionCaret(hit, false); break; case 2: mouseDoubleClick(hit); break; case 3: mouseTripleClick(hit); break; default: // no-op } } else if (e.isShiftDown() && !(e.isControlDown() || e.isAltDown() || e.isMetaDown() || e.isShortcutDown()) && e.getClickCount() == 1) {
*** 370,380 **** // the dot to be the new position. // everywhere else we just move the dot. if (isMac()) { textArea.extendSelection(i); } else { ! skin.positionCaret(hit, true, false); } } // skin.setForwardBias(hit.isLeading()); // if (textInputControl.editable) // displaySoftwareKeyboard(true); --- 381,391 ---- // the dot to be the new position. // everywhere else we just move the dot. if (isMac()) { textArea.extendSelection(i); } else { ! skin.positionCaret(hit, true); } } // skin.setForwardBias(hit.isLeading()); // if (textInputControl.editable) // displaySoftwareKeyboard(true);
*** 384,511 **** } } } @Override public void mouseDragged(MouseEvent e) { ! final TextArea textArea = getControl(); // we never respond to events if disabled, but we do notify any onXXX // event listeners on the control if (!textArea.isDisabled() && !e.isSynthesized()) { if (e.getButton() == MouseButton.PRIMARY && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown() || e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown())) { ! skin.positionCaret(skin.getIndex(e.getX(), e.getY()), true, false); } } deferClick = false; } @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 // event listeners on the control if (!textArea.isDisabled()) { setCaretAnimating(false); if (deferClick) { deferClick = false; ! skin.positionCaret(skin.getIndex(e.getX(), e.getY()), shiftDown, false); shiftDown = false; } setCaretAnimating(true); } } @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(); } } 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.util.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) { skin.setCaretAnimating(play); } ! protected void mouseDoubleClick(HitInfo hit) { ! final TextArea textArea = getControl(); textArea.previousWord(); if (isWindows()) { textArea.selectNextWord(); } else { textArea.selectEndOfNextWord(); } } ! protected void mouseTripleClick(HitInfo hit) { // select the line ! skin.paragraphStart(false, false); ! skin.paragraphEnd(false, isWindows(), true); } - - // public function mouseWheelMove(e:MouseEvent):Void { - // def textBox = bind skin.control as TextBox; - // // we never respond to events if disabled, but we do notify any onXXX - // // event listeners on the control - // if (not textBox.disabled) { - // var rot = Math.abs(e.wheelRotation); - // while (rot > 0) { - // rot--; - // scrollText(e.wheelRotation > 0); - // } - // } - // } - } --- 395,507 ---- } } } @Override public void mouseDragged(MouseEvent e) { ! final TextArea textArea = getNode(); // we never respond to events if disabled, but we do notify any onXXX // event listeners on the control if (!textArea.isDisabled() && !e.isSynthesized()) { if (e.getButton() == MouseButton.PRIMARY && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown() || e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown())) { ! skin.positionCaret(skin.getIndex(e.getX(), e.getY()), true); } } deferClick = false; } @Override public void mouseReleased(final MouseEvent e) { ! final TextArea textArea = getNode(); // we never respond to events if disabled, but we do notify any onXXX // event listeners on the control if (!textArea.isDisabled()) { setCaretAnimating(false); if (deferClick) { deferClick = false; ! skin.positionCaret(skin.getIndex(e.getX(), e.getY()), shiftDown); shiftDown = false; } setCaretAnimating(true); } } @Override public void contextMenuRequested(ContextMenuEvent e) { ! final TextArea textArea = getNode(); if (contextMenu.isShowing()) { contextMenu.hide(); } else if (textArea.getContextMenu() == null) { double screenX = e.getScreenX(); double screenY = e.getScreenY(); double sceneX = e.getSceneX(); ! if (Properties.IS_TOUCH_SUPPORTED) { Point2D menuPos; if (textArea.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(); } } if (menuPos != null) { ! Point2D p = getNode().localToScene(menuPos); ! Scene scene = getNode().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(); } } ! populateContextMenu(contextMenu); double menuWidth = contextMenu.prefWidth(-1); ! double menuX = screenX - (Properties.IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0); Screen currentScreen = com.sun.javafx.util.Utils.getScreenForPoint(screenX, 0); Rectangle2D bounds = currentScreen.getBounds(); if (menuX < bounds.getMinX()) { ! getNode().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX); ! getNode().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX); ! contextMenu.show(getNode(), bounds.getMinX(), screenY); } else if (screenX + menuWidth > bounds.getMaxX()) { double leftOver = menuWidth - ( bounds.getMaxX() - screenX); ! getNode().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX); ! getNode().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX); ! contextMenu.show(getNode(), screenX - leftOver, screenY); } else { ! getNode().getProperties().put("CONTEXT_MENU_SCREEN_X", 0); ! getNode().getProperties().put("CONTEXT_MENU_SCENE_X", 0); ! contextMenu.show(getNode(), menuX, screenY); } } e.consume(); } @Override protected void setCaretAnimating(boolean play) { skin.setCaretAnimating(play); } ! protected void mouseDoubleClick(TextPosInfo hit) { ! final TextArea textArea = getNode(); textArea.previousWord(); if (isWindows()) { textArea.selectNextWord(); } else { textArea.selectEndOfNextWord(); } } ! protected void mouseTripleClick(TextPosInfo hit) { // select the line ! skin.moveCaret(TextUnit.PARAGRAPH, Direction.BEGINNING, false); ! skin.moveCaret(TextUnit.PARAGRAPH, Direction.END, true); } }