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);
}
}