< prev index next >
modules/controls/src/main/java/javafx/scene/control/skin/TextFieldSkin.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 26,36 ****
package javafx.scene.control.skin;
import com.sun.javafx.scene.control.behavior.BehaviorBase;
import com.sun.javafx.scene.control.behavior.TextAreaBehavior;
import com.sun.javafx.scene.control.behavior.TextInputControlBehavior;
- import com.sun.javafx.scene.control.skin.Utils;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.DoubleProperty;
--- 26,35 ----
*** 57,70 ****
import javafx.scene.paint.Paint;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import java.util.List;
import com.sun.javafx.scene.control.behavior.TextFieldBehavior;
import com.sun.javafx.scene.control.behavior.PasswordFieldBehavior;
- import com.sun.javafx.scene.text.HitInfo;
/**
* Default skin implementation for the {@link TextField} control.
*
* @see TextField
--- 56,69 ----
import javafx.scene.paint.Paint;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
+ import javafx.scene.text.HitInfo;
import java.util.List;
import com.sun.javafx.scene.control.behavior.TextFieldBehavior;
import com.sun.javafx.scene.control.behavior.PasswordFieldBehavior;
/**
* Default skin implementation for the {@link TextField} control.
*
* @see TextField
*** 210,220 ****
@Override protected String computeValue() {
return maskText(control.textProperty().getValueSafe());
}
});
textNode.fillProperty().bind(textFillProperty());
! textNode.impl_selectionFillProperty().bind(new ObjectBinding<Paint>() {
{ bind(highlightTextFillProperty(), textFillProperty(), control.focusedProperty()); }
@Override protected Paint computeValue() {
return control.isFocused() ? highlightTextFillProperty().get() : textFillProperty().get();
}
});
--- 209,219 ----
@Override protected String computeValue() {
return maskText(control.textProperty().getValueSafe());
}
});
textNode.fillProperty().bind(textFillProperty());
! textNode.selectionFillProperty().bind(new ObjectBinding<Paint>() {
{ bind(highlightTextFillProperty(), textFillProperty(), control.focusedProperty()); }
@Override protected Paint computeValue() {
return control.isFocused() ? highlightTextFillProperty().get() : textFillProperty().get();
}
});
*** 228,238 ****
selectionHighlightPath.setManaged(false);
selectionHighlightPath.setStroke(null);
selectionHighlightPath.layoutXProperty().bind(textTranslateX);
selectionHighlightPath.visibleProperty().bind(control.anchorProperty().isNotEqualTo(control.caretPositionProperty()).and(control.focusedProperty()));
selectionHighlightPath.fillProperty().bind(highlightFillProperty());
! textNode.impl_selectionShapeProperty().addListener(observable -> {
updateSelection();
});
// Add caret
caretPath.setManaged(false);
--- 227,237 ----
selectionHighlightPath.setManaged(false);
selectionHighlightPath.setStroke(null);
selectionHighlightPath.layoutXProperty().bind(textTranslateX);
selectionHighlightPath.visibleProperty().bind(control.anchorProperty().isNotEqualTo(control.caretPositionProperty()).and(control.focusedProperty()));
selectionHighlightPath.fillProperty().bind(highlightFillProperty());
! textNode.selectionShapeProperty().addListener(observable -> {
updateSelection();
});
// Add caret
caretPath.setManaged(false);
*** 247,258 ****
@Override protected double computeValue() {
return caretVisibleProperty().get() ? 1.0 : 0.0;
}
});
caretPath.layoutXProperty().bind(textTranslateX);
! textNode.impl_caretShapeProperty().addListener(observable -> {
! caretPath.getElements().setAll(textNode.impl_caretShapeProperty().get());
if (caretPath.getElements().size() == 0) {
// The caret pos is invalid.
updateTextNodeCaretPos(control.getCaretPosition());
} else if (caretPath.getElements().size() == 4) {
// The caret is split. Ignore and keep the previous width value.
--- 246,257 ----
@Override protected double computeValue() {
return caretVisibleProperty().get() ? 1.0 : 0.0;
}
});
caretPath.layoutXProperty().bind(textTranslateX);
! textNode.caretShapeProperty().addListener(observable -> {
! caretPath.getElements().setAll(textNode.caretShapeProperty().get());
if (caretPath.getElements().size() == 0) {
// The caret pos is invalid.
updateTextNodeCaretPos(control.getCaretPosition());
} else if (caretPath.getElements().size() == 4) {
// The caret is split. Ignore and keep the previous width value.
*** 329,360 ****
selectionHandle2.setOnMousePressed(handlePressHandler);
caretHandle.setOnMouseDragged(e -> {
Point2D p = new Point2D(caretHandle.getLayoutX() + e.getX() + pressX - textNode.getLayoutX(),
caretHandle.getLayoutY() + e.getY() - pressY - 6);
! HitInfo hit = textNode.impl_hitTestChar(p);
positionCaret(hit, false);
e.consume();
});
selectionHandle1.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent e) {
TextField control = getSkinnable();
Point2D tp = textNode.localToScene(0, 0);
Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle1.getWidth() / 2,
e.getSceneY() - tp.getY() - pressY - 6);
! HitInfo hit = textNode.impl_hitTestChar(p);
! int pos = hit.getCharIndex();
if (control.getAnchor() < control.getCaretPosition()) {
// Swap caret and anchor
control.selectRange(control.getCaretPosition(), control.getAnchor());
}
if (pos >= 0) {
if (pos >= control.getAnchor() - 1) {
! hit.setCharIndex(Math.max(0, control.getAnchor() - 1));
}
! positionCaret(hit, true);
}
e.consume();
}
});
--- 328,359 ----
selectionHandle2.setOnMousePressed(handlePressHandler);
caretHandle.setOnMouseDragged(e -> {
Point2D p = new Point2D(caretHandle.getLayoutX() + e.getX() + pressX - textNode.getLayoutX(),
caretHandle.getLayoutY() + e.getY() - pressY - 6);
! HitInfo hit = textNode.hitTest(p);
positionCaret(hit, false);
e.consume();
});
selectionHandle1.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent e) {
TextField control = getSkinnable();
Point2D tp = textNode.localToScene(0, 0);
Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle1.getWidth() / 2,
e.getSceneY() - tp.getY() - pressY - 6);
! HitInfo hit = textNode.hitTest(p);
if (control.getAnchor() < control.getCaretPosition()) {
// Swap caret and anchor
control.selectRange(control.getCaretPosition(), control.getAnchor());
}
+ int pos = hit.getInsertionIndex();
if (pos >= 0) {
if (pos >= control.getAnchor() - 1) {
! pos = Math.max(0, control.getAnchor() - 1);
}
! positionCaret(pos, hit.isLeading(), true);
}
e.consume();
}
});
*** 362,382 ****
@Override public void handle(MouseEvent e) {
TextField control = getSkinnable();
Point2D tp = textNode.localToScene(0, 0);
Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle2.getWidth() / 2,
e.getSceneY() - tp.getY() - pressY - 6);
! HitInfo hit = textNode.impl_hitTestChar(p);
! int pos = hit.getCharIndex();
if (control.getAnchor() > control.getCaretPosition()) {
// Swap caret and anchor
control.selectRange(control.getCaretPosition(), control.getAnchor());
}
if (pos > 0) {
if (pos <= control.getAnchor()) {
! hit.setCharIndex(Math.min(control.getAnchor() + 1, control.getLength()));
}
! positionCaret(hit, true);
}
e.consume();
}
});
}
--- 361,381 ----
@Override public void handle(MouseEvent e) {
TextField control = getSkinnable();
Point2D tp = textNode.localToScene(0, 0);
Point2D p = new Point2D(e.getSceneX() - tp.getX() + 10/*??*/ - pressX + selectionHandle2.getWidth() / 2,
e.getSceneY() - tp.getY() - pressY - 6);
! HitInfo hit = textNode.hitTest(p);
if (control.getAnchor() > control.getCaretPosition()) {
// Swap caret and anchor
control.selectRange(control.getCaretPosition(), control.getAnchor());
}
+ int pos = hit.getInsertionIndex();
if (pos > 0) {
if (pos <= control.getAnchor()) {
! pos = Math.min(control.getAnchor() + 1, control.getLength());
}
! positionCaret(pos, hit.isLeading(), true);
}
e.consume();
}
});
}
*** 476,517 ****
/**
* Performs a hit test, mapping point to index in the content.
*
* @param x the x coordinate of the point.
* @param y the y coordinate of the point.
! * @return a {@code TextPosInfo} object describing the index and forward bias.
*/
! public TextPosInfo getIndex(double x, double y) {
// adjust the event to be in the same coordinate space as the
// text content of the textInputControl
Point2D p = new Point2D(x - textTranslateX.get() - snappedLeftInset(),
y - snappedTopInset());
! return new TextPosInfo(textNode.impl_hitTestChar(p));
}
// Public for behavior
/**
* Moves the caret to the specified position.
*
* @param hit the new position and forward bias of the caret.
* @param select whether to extend selection to the new position.
*/
! public void positionCaret(TextPosInfo hit, boolean select) {
! TextField textField = getSkinnable();
! int pos = Utils.getHitInsertionIndex(hit, textField.textProperty().getValueSafe());
if (select) {
textField.selectPositionCaret(pos);
} else {
textField.positionCaret(pos);
}
!
! setForwardBias(hit.isLeading());
! }
!
! private void positionCaret(HitInfo hit, boolean select) {
! positionCaret(new TextPosInfo(hit), select);
}
/** {@inheritDoc} */
@Override public Rectangle2D getCharacterBounds(int index) {
double x, y;
--- 475,513 ----
/**
* Performs a hit test, mapping point to index in the content.
*
* @param x the x coordinate of the point.
* @param y the y coordinate of the point.
! * @return a {@code HitInfo} object describing the index and forward bias.
*/
! public HitInfo getIndex(double x, double y) {
// adjust the event to be in the same coordinate space as the
// text content of the textInputControl
Point2D p = new Point2D(x - textTranslateX.get() - snappedLeftInset(),
y - snappedTopInset());
! return textNode.hitTest(p);
}
// Public for behavior
/**
* Moves the caret to the specified position.
*
* @param hit the new position and forward bias of the caret.
* @param select whether to extend selection to the new position.
*/
! public void positionCaret(HitInfo hit, boolean select) {
! positionCaret(hit.getInsertionIndex(), hit.isLeading(), select);
! }
+ private void positionCaret(int pos, boolean leading, boolean select) {
+ TextField textField = getSkinnable();
if (select) {
textField.selectPositionCaret(pos);
} else {
textField.positionCaret(pos);
}
! setForwardBias(leading);
}
/** {@inheritDoc} */
@Override public Rectangle2D getCharacterBounds(int index) {
double x, y;
*** 522,532 ****
y = 0;
width = 0;
height = textNodeBounds.getMaxY();
} else {
characterBoundingPath.getElements().clear();
! characterBoundingPath.getElements().addAll(textNode.impl_getRangeShape(index, index + 1));
characterBoundingPath.setLayoutX(textNode.getLayoutX());
characterBoundingPath.setLayoutY(textNode.getLayoutY());
Bounds bounds = characterBoundingPath.getBoundsInLocal();
--- 518,528 ----
y = 0;
width = 0;
height = textNodeBounds.getMaxY();
} else {
characterBoundingPath.getElements().clear();
! characterBoundingPath.getElements().addAll(textNode.rangeShape(index, index + 1));
characterBoundingPath.setLayoutX(textNode.getLayoutX());
characterBoundingPath.setLayoutY(textNode.getLayoutY());
Bounds bounds = characterBoundingPath.getBoundsInLocal();
*** 543,558 ****
y + textBounds.getMinY(), width, height);
}
/** {@inheritDoc} */
@Override protected PathElement[] getUnderlineShape(int start, int end) {
! return textNode.impl_getUnderlineShape(start, end);
}
/** {@inheritDoc} */
@Override protected PathElement[] getRangeShape(int start, int end) {
! return textNode.impl_getRangeShape(start, end);
}
/** {@inheritDoc} */
@Override protected void addHighlight(List<? extends Node> nodes, int start) {
textGroup.getChildren().addAll(nodes);
--- 539,554 ----
y + textBounds.getMinY(), width, height);
}
/** {@inheritDoc} */
@Override protected PathElement[] getUnderlineShape(int start, int end) {
! return textNode.underlineShape(start, end);
}
/** {@inheritDoc} */
@Override protected PathElement[] getRangeShape(int start, int end) {
! return textNode.rangeShape(start, end);
}
/** {@inheritDoc} */
@Override protected void addHighlight(List<? extends Node> nodes, int start) {
textGroup.getChildren().addAll(nodes);
*** 595,611 ****
// See RT-25465.
caretBounds = new Path(caretPath.getElements().get(0), caretPath.getElements().get(1)).getLayoutBounds();
}
double hitX = moveRight ? caretBounds.getMaxX() : caretBounds.getMinX();
double hitY = (caretBounds.getMinY() + caretBounds.getMaxY()) / 2;
! HitInfo hit = textNode.impl_hitTestChar(new Point2D(hitX, hitY));
! Path charShape = new Path(textNode.impl_getRangeShape(hit.getCharIndex(), hit.getCharIndex() + 1));
if ((moveRight && charShape.getLayoutBounds().getMaxX() > caretBounds.getMaxX()) ||
(!moveRight && charShape.getLayoutBounds().getMinX() < caretBounds.getMinX())) {
! hit.setLeading(!hit.isLeading());
}
! positionCaret(hit, false);
}
/** {@inheritDoc} */
@Override protected void layoutChildren(final double x, final double y,
final double w, final double h) {
--- 591,608 ----
// See RT-25465.
caretBounds = new Path(caretPath.getElements().get(0), caretPath.getElements().get(1)).getLayoutBounds();
}
double hitX = moveRight ? caretBounds.getMaxX() : caretBounds.getMinX();
double hitY = (caretBounds.getMinY() + caretBounds.getMaxY()) / 2;
! HitInfo hit = textNode.hitTest(new Point2D(hitX, hitY));
! boolean leading = hit.isLeading();
! Path charShape = new Path(textNode.rangeShape(hit.getCharIndex(), hit.getCharIndex() + 1));
if ((moveRight && charShape.getLayoutBounds().getMaxX() > caretBounds.getMaxX()) ||
(!moveRight && charShape.getLayoutBounds().getMinX() < caretBounds.getMinX())) {
! leading = !leading;
}
! positionCaret(hit.getInsertionIndex(), leading, false);
}
/** {@inheritDoc} */
@Override protected void layoutChildren(final double x, final double y,
final double w, final double h) {
*** 714,728 ****
return behavior;
}
private void updateTextNodeCaretPos(int pos) {
if (pos == 0 || isForwardBias()) {
! textNode.setImpl_caretPosition(pos);
} else {
! textNode.setImpl_caretPosition(pos - 1);
}
! textNode.impl_caretBiasProperty().set(isForwardBias());
}
private void createPromptNode() {
if (promptNode != null || !usePromptText.get()) return;
--- 711,725 ----
return behavior;
}
private void updateTextNodeCaretPos(int pos) {
if (pos == 0 || isForwardBias()) {
! textNode.setCaretPosition(pos);
} else {
! textNode.setCaretPosition(pos - 1);
}
! textNode.caretBiasProperty().set(isForwardBias());
}
private void createPromptNode() {
if (promptNode != null || !usePromptText.get()) return;
*** 741,760 ****
private void updateSelection() {
TextField textField = getSkinnable();
IndexRange newValue = textField.getSelection();
if (newValue == null || newValue.getLength() == 0) {
! textNode.impl_selectionStartProperty().set(-1);
! textNode.impl_selectionEndProperty().set(-1);
} else {
! textNode.impl_selectionStartProperty().set(newValue.getStart());
// This intermediate value is needed to force selection shape layout.
! textNode.impl_selectionEndProperty().set(newValue.getStart());
! textNode.impl_selectionEndProperty().set(newValue.getEnd());
}
! PathElement[] elements = textNode.impl_selectionShapeProperty().get();
if (elements == null) {
selectionHighlightPath.getElements().clear();
} else {
selectionHighlightPath.getElements().setAll(elements);
}
--- 738,757 ----
private void updateSelection() {
TextField textField = getSkinnable();
IndexRange newValue = textField.getSelection();
if (newValue == null || newValue.getLength() == 0) {
! textNode.selectionStartProperty().set(-1);
! textNode.selectionEndProperty().set(-1);
} else {
! textNode.selectionStartProperty().set(newValue.getStart());
// This intermediate value is needed to force selection shape layout.
! textNode.selectionEndProperty().set(newValue.getStart());
! textNode.selectionEndProperty().set(newValue.getEnd());
}
! PathElement[] elements = textNode.selectionShapeProperty().get();
if (elements == null) {
selectionHighlightPath.getElements().clear();
} else {
selectionHighlightPath.getElements().setAll(elements);
}
< prev index next >