/*
* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javafx.scene.text;
import com.sun.javafx.css.converters.
BooleanConverter;
import com.sun.javafx.css.converters.
EnumConverter;
import com.sun.javafx.css.converters.
SizeConverter;
import com.sun.javafx.geom.
BaseBounds;
import com.sun.javafx.geom.
Path2D;
import com.sun.javafx.geom.
RectBounds;
import com.sun.javafx.geom.
TransformedShape;
import com.sun.javafx.geom.transform.
BaseTransform;
import com.sun.javafx.scene.
DirtyBits;
import com.sun.javafx.scene.text.*;
import com.sun.javafx.sg.prism.
NGNode;
import com.sun.javafx.sg.prism.
NGShape;
import com.sun.javafx.sg.prism.
NGText;
import com.sun.javafx.tk.
Toolkit;
import javafx.beans.
DefaultProperty;
import javafx.beans.
InvalidationListener;
import javafx.beans.binding.
DoubleBinding;
import javafx.beans.binding.
ObjectBinding;
import javafx.beans.property.*;
import javafx.css.*;
import javafx.geometry.*;
import javafx.scene.
AccessibleAttribute;
import javafx.scene.
AccessibleRole;
import javafx.scene.paint.
Color;
import javafx.scene.paint.
Paint;
import javafx.scene.shape.
LineTo;
import javafx.scene.shape.
MoveTo;
import javafx.scene.shape.
PathElement;
import javafx.scene.shape.
Shape;
import javafx.scene.shape.
StrokeType;
import java.util.
ArrayList;
import java.util.
Collections;
import java.util.
List;
/**
* The {@code Text} class defines a node that displays a text.
*
* Paragraphs are separated by {@code '\n'} and the text is wrapped on
* paragraph boundaries.
*
<PRE>
import javafx.scene.text.*;
Text t = new Text(10, 50, "This is a test");
t.setFont(new Font(20));
</PRE>
*
<PRE>
import javafx.scene.text.*;
Text t = new Text();
text.setFont(new Font(20));
text.setText("First row\nSecond row");
</PRE>
*
<PRE>
import javafx.scene.text.*;
Text t = new Text();
text.setFont(new Font(20));
text.setWrappingWidth(200);
text.setTextAlignment(TextAlignment.JUSTIFY)
text.setText("The quick brown fox jumps over the lazy dog");
</PRE>
* @since JavaFX 2.0
*/
@
DefaultProperty("text")
public class
Text extends
Shape {
private
TextLayout layout;
private static final
PathElement[]
EMPTY_PATH_ELEMENT_ARRAY = new
PathElement[0];
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
@
Override
protected final
NGNode impl_createPeer() {
return new
NGText();
}
/**
* Creates an empty instance of Text.
*/
public
Text() {
setAccessibleRole(
AccessibleRole.
TEXT);
InvalidationListener listener =
observable ->
checkSpan();
parentProperty().
addListener(
listener);
managedProperty().
addListener(
listener);
effectiveNodeOrientationProperty().
addListener(
observable ->
checkOrientation());
setPickOnBounds(true);
}
/**
* Creates an instance of Text containing the given string.
* @param text text to be contained in the instance
*/
public
Text(
String text) {
this();
setText(
text);
}
/**
* Creates an instance of Text on the given coordinates containing the
* given string.
* @param x the horizontal position of the text
* @param y the vertical position of the text
* @param text text to be contained in the instance
*/
public
Text(double
x, double
y,
String text) {
this(
text);
setX(
x);
setY(
y);
}
private boolean
isSpan;
private boolean
isSpan() {
return
isSpan;
}
private void
checkSpan() {
isSpan =
isManaged() &&
getParent() instanceof
TextFlow;
if (
isSpan() && !
pickOnBoundsProperty().
isBound()) {
/* Documented behavior. See class description for TextFlow */
setPickOnBounds(false);
}
}
private void
checkOrientation() {
if (!
isSpan()) {
NodeOrientation orientation =
getEffectiveNodeOrientation();
boolean
rtl =
orientation ==
NodeOrientation.
RIGHT_TO_LEFT;
int
dir =
rtl ?
TextLayout.
DIRECTION_RTL :
TextLayout.
DIRECTION_LTR;
TextLayout layout =
getTextLayout();
if (
layout.
setDirection(
dir)) {
needsTextLayout();
}
}
}
@
Override
public boolean
usesMirroring() {
return false;
}
private void
needsFullTextLayout() {
if (
isSpan()) {
/* Create new text span every time the font or text changes
* so the text layout can see that the content has changed.
*/
textSpan = null;
/* Relies on impl_geomChanged() to request text flow to relayout */
} else {
TextLayout layout =
getTextLayout();
String string =
getTextInternal();
Object font =
getFontInternal();
layout.
setContent(
string,
font);
}
needsTextLayout();
}
private void
needsTextLayout() {
textRuns = null;
impl_geomChanged();
impl_markDirty(
DirtyBits.
NODE_CONTENTS);
}
private
TextSpan textSpan;
TextSpan getTextSpan() {
if (
textSpan == null) {
textSpan = new
TextSpan() {
@
Override public
String getText() {
return
getTextInternal();
}
@
Override public
Object getFont() {
return
getFontInternal();
}
@
Override public
RectBounds getBounds() {
return null;
}
};
}
return
textSpan;
}
private
TextLayout getTextLayout() {
if (
isSpan()) {
layout = null;
TextFlow parent = (
TextFlow)
getParent();
return
parent.
getTextLayout();
}
if (
layout == null) {
TextLayoutFactory factory =
Toolkit.
getToolkit().
getTextLayoutFactory();
layout =
factory.
createLayout();
String string =
getTextInternal();
Object font =
getFontInternal();
TextAlignment alignment =
getTextAlignment();
if (
alignment == null)
alignment =
DEFAULT_TEXT_ALIGNMENT;
layout.
setContent(
string,
font);
layout.
setAlignment(
alignment.
ordinal());
layout.
setLineSpacing((float)
getLineSpacing());
layout.
setWrapWidth((float)
getWrappingWidth());
if (
getEffectiveNodeOrientation() ==
NodeOrientation.
RIGHT_TO_LEFT) {
layout.
setDirection(
TextLayout.
DIRECTION_RTL);
} else {
layout.
setDirection(
TextLayout.
DIRECTION_LTR);
}
}
return
layout;
}
private
GlyphList[]
textRuns = null;
private
BaseBounds spanBounds = new
RectBounds(); /* relative to the textlayout */
private boolean
spanBoundsInvalid = true;
void
layoutSpan(
GlyphList[]
runs) {
TextSpan span =
getTextSpan();
int
count = 0;
for (int
i = 0;
i <
runs.length;
i++) {
GlyphList run =
runs[
i];
if (
run.
getTextSpan() ==
span) {
count++;
}
}
textRuns = new
GlyphList[
count];
count = 0;
for (int
i = 0;
i <
runs.length;
i++) {
GlyphList run =
runs[
i];
if (
run.
getTextSpan() ==
span) {
textRuns[
count++] =
run;
}
}
spanBoundsInvalid = true;
/* Sometimes a property change in the text node will causes layout in
* text flow. In this case all the dirty bits are already clear and no
* extra work is necessary. Other times the layout is caused by changes
* in the text flow object (wrapping width and text alignment for example).
* In the second case the dirty bits must be set here using
* impl_geomChanged() and impl_markDirty(). Note that impl_geomChanged()
* causes another (undesired) layout request in the parent.
* In general this is not a problem because shapes are not resizable and
* region objects do not propagate layout changes to the parent.
* This is a special case where a shape is resized by the parent during
* layoutChildren(). See TextFlow#requestLayout() for information how
* text flow deals with this situation.
*/
impl_geomChanged();
impl_markDirty(
DirtyBits.
NODE_CONTENTS);
}
BaseBounds getSpanBounds() {
if (
spanBoundsInvalid) {
GlyphList[]
runs =
getRuns();
if (
runs.length != 0) {
float
left =
Float.
POSITIVE_INFINITY;
float
top =
Float.
POSITIVE_INFINITY;
float
right = 0;
float
bottom = 0;
for (int
i = 0;
i <
runs.length;
i++) {
GlyphList run =
runs[
i];
com.sun.javafx.geom.
Point2D location =
run.
getLocation();
float
width =
run.
getWidth();
float
height =
run.
getLineBounds().
getHeight();
left =
Math.
min(
location.
x,
left);
top =
Math.
min(
location.
y,
top);
right =
Math.
max(
location.
x +
width,
right);
bottom =
Math.
max(
location.
y +
height,
bottom);
}
spanBounds =
spanBounds.
deriveWithNewBounds(
left,
top, 0,
right,
bottom, 0);
} else {
spanBounds =
spanBounds.
makeEmpty();
}
spanBoundsInvalid = false;
}
return
spanBounds;
}
private
GlyphList[]
getRuns() {
if (
textRuns != null) return
textRuns;
if (
isSpan()) {
/* List of run is initialized when the TextFlow layout the children */
getParent().
layout();
} else {
TextLayout layout =
getTextLayout();
textRuns =
layout.
getRuns();
}
return
textRuns;
}
private com.sun.javafx.geom.
Shape getShape() {
TextLayout layout =
getTextLayout();
/* TextLayout has the text shape cached */
int
type =
TextLayout.
TYPE_TEXT;
if (
isStrikethrough())
type |=
TextLayout.
TYPE_STRIKETHROUGH;
if (
isUnderline())
type |=
TextLayout.
TYPE_UNDERLINE;
TextSpan filter = null;
if (
isSpan()) {
/* Spans are always relative to the top */
type |=
TextLayout.
TYPE_TOP;
filter =
getTextSpan();
} else {
/* Relative to baseline (first line)
* This shape can be translate in the y axis according
* to text origin, see impl_configShape().
*/
type |=
TextLayout.
TYPE_BASELINE;
}
return
layout.
getShape(
type,
filter);
}
private
BaseBounds getVisualBounds() {
if (
impl_mode ==
NGShape.
Mode.
FILL ||
getStrokeType() ==
StrokeType.
INSIDE) {
int
type =
TextLayout.
TYPE_TEXT;
if (
isStrikethrough())
type |=
TextLayout.
TYPE_STRIKETHROUGH;
if (
isUnderline())
type |=
TextLayout.
TYPE_UNDERLINE;
return
getTextLayout().
getVisualBounds(
type);
} else {
return
getShape().
getBounds();
}
}
private
BaseBounds getLogicalBounds() {
TextLayout layout =
getTextLayout();
/* TextLayout has the bounds cached */
return
layout.
getBounds();
}
/**
* Defines text string that is to be displayed.
*
* @defaultValue empty string
*/
private
StringProperty text;
public final void
setText(
String value) {
if (
value == null)
value = "";
textProperty().
set(
value);
}
public final
String getText() {
return
text == null ? "" :
text.
get();
}
private
String getTextInternal() {
// this might return null in case of bound property
String localText =
getText();
return
localText == null ? "" :
localText;
}
public final
StringProperty textProperty() {
if (
text == null) {
text = new
StringPropertyBase("") {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "text"; }
@
Override public void
invalidated() {
needsFullTextLayout();
setImpl_selectionStart(-1);
setImpl_selectionEnd(-1);
setImpl_caretPosition(-1);
setImpl_caretBias(true);
// MH: Functionality copied from store() method,
// which was removed.
// Wonder what should happen if text is bound
// and becomes null?
final
String value =
get();
if ((
value == null) && !
isBound()) {
set("");
}
notifyAccessibleAttributeChanged(
AccessibleAttribute.
TEXT);
}
};
}
return
text;
}
/**
* Defines the X coordinate of text origin.
*
* @defaultValue 0
*/
private
DoubleProperty x;
public final void
setX(double
value) {
xProperty().
set(
value);
}
public final double
getX() {
return
x == null ? 0.0 :
x.
get();
}
public final
DoubleProperty xProperty() {
if (
x == null) {
x = new
DoublePropertyBase() {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "x"; }
@
Override public void
invalidated() {
impl_geomChanged();
}
};
}
return
x;
}
/**
* Defines the Y coordinate of text origin.
*
* @defaultValue 0
*/
private
DoubleProperty y;
public final void
setY(double
value) {
yProperty().
set(
value);
}
public final double
getY() {
return
y == null ? 0.0 :
y.
get();
}
public final
DoubleProperty yProperty() {
if (
y == null) {
y = new
DoublePropertyBase() {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "y"; }
@
Override public void
invalidated() {
impl_geomChanged();
}
};
}
return
y;
}
/**
* Defines the font of text.
*
* @defaultValue Font{}
*/
private
ObjectProperty<
Font>
font;
public final void
setFont(
Font value) {
fontProperty().
set(
value);
}
public final
Font getFont() {
return
font == null ?
Font.
getDefault() :
font.
get();
}
/**
* Internally used safe version of getFont which never returns null.
*
* @return the font
*/
private
Object getFontInternal() {
Font font =
getFont();
if (
font == null)
font =
Font.
getDefault();
return
font.
impl_getNativeFont();
}
public final
ObjectProperty<
Font>
fontProperty() {
if (
font == null) {
font = new
StyleableObjectProperty<
Font>(
Font.
getDefault()) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "font"; }
@
Override public
CssMetaData<
Text,
Font>
getCssMetaData() {
return
StyleableProperties.
FONT;
}
@
Override public void
invalidated() {
needsFullTextLayout();
impl_markDirty(
DirtyBits.
TEXT_FONT);
}
};
}
return
font;
}
public final void
setTextOrigin(
VPos value) {
textOriginProperty().
set(
value);
}
public final
VPos getTextOrigin() {
if (
attributes == null ||
attributes.
textOrigin == null) {
return
DEFAULT_TEXT_ORIGIN;
}
return
attributes.
getTextOrigin();
}
/**
* Defines the origin of text coordinate system in local coordinates.
* Note: in case multiple rows are rendered {@code VPos.BASELINE} and
* {@code VPos.TOP} define the origin of the top row while
* {@code VPos.BOTTOM} defines the origin of the bottom row.
*
* @defaultValue VPos.BASELINE
*/
public final
ObjectProperty<
VPos>
textOriginProperty() {
return
getTextAttribute().
textOriginProperty();
}
/**
* Determines how the bounds of the text node are calculated.
* Logical bounds is a more appropriate default for text than
* the visual bounds. See {@code TextBoundsType} for more information.
*
* @defaultValue TextBoundsType.LOGICAL
*/
private
ObjectProperty<
TextBoundsType>
boundsType;
public final void
setBoundsType(
TextBoundsType value) {
boundsTypeProperty().
set(
value);
}
public final
TextBoundsType getBoundsType() {
return
boundsType == null ?
DEFAULT_BOUNDS_TYPE :
boundsTypeProperty().
get();
}
public final
ObjectProperty<
TextBoundsType>
boundsTypeProperty() {
if (
boundsType == null) {
boundsType =
new
StyleableObjectProperty<
TextBoundsType>(
DEFAULT_BOUNDS_TYPE) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "boundsType"; }
@
Override public
CssMetaData<
Text,
TextBoundsType>
getCssMetaData() {
return
StyleableProperties.
BOUNDS_TYPE;
}
@
Override public void
invalidated() {
TextLayout layout =
getTextLayout();
int
type = 0;
if (
boundsType.
get() ==
TextBoundsType.
LOGICAL_VERTICAL_CENTER) {
type |=
TextLayout.
BOUNDS_CENTER;
}
if (
layout.
setBoundsType(
type)) {
needsTextLayout();
} else {
impl_geomChanged();
}
}
};
}
return
boundsType;
}
/**
* Defines a width constraint for the text in user space coordinates,
* e.g. pixels, not glyph or character count.
* If the value is {@code > 0} text will be line wrapped as needed
* to satisfy this constraint.
*
* @defaultValue 0
*/
private
DoubleProperty wrappingWidth;
public final void
setWrappingWidth(double
value) {
wrappingWidthProperty().
set(
value);
}
public final double
getWrappingWidth() {
return
wrappingWidth == null ? 0 :
wrappingWidth.
get();
}
public final
DoubleProperty wrappingWidthProperty() {
if (
wrappingWidth == null) {
wrappingWidth = new
DoublePropertyBase() {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "wrappingWidth"; }
@
Override public void
invalidated() {
if (!
isSpan()) {
TextLayout layout =
getTextLayout();
if (
layout.
setWrapWidth((float)
get())) {
needsTextLayout();
} else {
impl_geomChanged();
}
}
}
};
}
return
wrappingWidth;
}
public final void
setUnderline(boolean
value) {
underlineProperty().
set(
value);
}
public final boolean
isUnderline() {
if (
attributes == null ||
attributes.
underline == null) {
return
DEFAULT_UNDERLINE;
}
return
attributes.
isUnderline();
}
/**
* Defines if each line of text should have a line below it.
*
* @defaultValue false
*/
public final
BooleanProperty underlineProperty() {
return
getTextAttribute().
underlineProperty();
}
public final void
setStrikethrough(boolean
value) {
strikethroughProperty().
set(
value);
}
public final boolean
isStrikethrough() {
if (
attributes == null ||
attributes.
strikethrough == null) {
return
DEFAULT_STRIKETHROUGH;
}
return
attributes.
isStrikethrough();
}
/**
* Defines if each line of text should have a line through it.
*
* @defaultValue false
*/
public final
BooleanProperty strikethroughProperty() {
return
getTextAttribute().
strikethroughProperty();
}
public final void
setTextAlignment(
TextAlignment value) {
textAlignmentProperty().
set(
value);
}
public final
TextAlignment getTextAlignment() {
if (
attributes == null ||
attributes.
textAlignment == null) {
return
DEFAULT_TEXT_ALIGNMENT;
}
return
attributes.
getTextAlignment();
}
/**
* Defines horizontal text alignment in the bounding box.
*
* The width of the bounding box is defined by the widest row.
*
* Note: In the case of a single line of text, where the width of the
* node is determined by the width of the text, the alignment setting
* has no effect.
*
* @defaultValue TextAlignment.LEFT
*/
public final
ObjectProperty<
TextAlignment>
textAlignmentProperty() {
return
getTextAttribute().
textAlignmentProperty();
}
public final void
setLineSpacing(double
spacing) {
lineSpacingProperty().
set(
spacing);
}
public final double
getLineSpacing() {
if (
attributes == null ||
attributes.
lineSpacing == null) {
return
DEFAULT_LINE_SPACING;
}
return
attributes.
getLineSpacing();
}
/**
* Defines the vertical space in pixel between lines.
*
* @defaultValue 0
*
* @since JavaFX 8.0
*/
public final
DoubleProperty lineSpacingProperty() {
return
getTextAttribute().
lineSpacingProperty();
}
@
Override
public final double
getBaselineOffset() {
return
baselineOffsetProperty().
get();
}
/**
* The 'alphabetic' (or roman) baseline offset from the Text node's
* layoutBounds.minY location.
* The value typically corresponds to the max ascent of the font.
*/
public final
ReadOnlyDoubleProperty baselineOffsetProperty() {
return
getTextAttribute().
baselineOffsetProperty();
}
/**
* Specifies a requested font smoothing type : gray or LCD.
*
* The width of the bounding box is defined by the widest row.
*
* Note: LCD mode doesn't apply in numerous cases, such as various
* compositing modes, where effects are applied and very large glyphs.
*
* @defaultValue FontSmoothingType.GRAY
* @since JavaFX 2.1
*/
private
ObjectProperty<
FontSmoothingType>
fontSmoothingType;
public final void
setFontSmoothingType(
FontSmoothingType value) {
fontSmoothingTypeProperty().
set(
value);
}
public final
FontSmoothingType getFontSmoothingType() {
return
fontSmoothingType == null ?
FontSmoothingType.
GRAY :
fontSmoothingType.
get();
}
public final
ObjectProperty<
FontSmoothingType>
fontSmoothingTypeProperty() {
if (
fontSmoothingType == null) {
fontSmoothingType =
new
StyleableObjectProperty<
FontSmoothingType>
(
FontSmoothingType.
GRAY) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "fontSmoothingType"; }
@
Override public
CssMetaData<
Text,
FontSmoothingType>
getCssMetaData() {
return
StyleableProperties.
FONT_SMOOTHING_TYPE;
}
@
Override public void
invalidated() {
impl_markDirty(
DirtyBits.
TEXT_ATTRS);
impl_geomChanged();
}
};
}
return
fontSmoothingType;
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
@
Override
protected final void
impl_geomChanged() {
super.impl_geomChanged();
if (
attributes != null) {
if (
attributes.
impl_caretBinding != null) {
attributes.
impl_caretBinding.
invalidate();
}
if (
attributes.
impl_selectionBinding != null) {
attributes.
impl_selectionBinding.
invalidate();
}
}
impl_markDirty(
DirtyBits.
NODE_GEOMETRY);
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
PathElement[]
getImpl_selectionShape() {
return
impl_selectionShapeProperty().
get();
}
/**
* Shape of selection in local coordinates.
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
ReadOnlyObjectProperty<
PathElement[]>
impl_selectionShapeProperty() {
return
getTextAttribute().
impl_selectionShapeProperty();
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final void
setImpl_selectionStart(int
value) {
if (
value == -1 &&
(
attributes == null ||
attributes.
impl_selectionStart == null)) {
return;
}
impl_selectionStartProperty().
set(
value);
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final int
getImpl_selectionStart() {
if (
attributes == null ||
attributes.
impl_selectionStart == null) {
return
DEFAULT_SELECTION_START;
}
return
attributes.
getImpl_selectionStart();
}
/**
* Selection start index in the content.
* set to {@code -1} to unset selection.
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
IntegerProperty impl_selectionStartProperty() {
return
getTextAttribute().
impl_selectionStartProperty();
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final void
setImpl_selectionEnd(int
value) {
if (
value == -1 &&
(
attributes == null ||
attributes.
impl_selectionEnd == null)) {
return;
}
impl_selectionEndProperty().
set(
value);
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final int
getImpl_selectionEnd() {
if (
attributes == null ||
attributes.
impl_selectionEnd == null) {
return
DEFAULT_SELECTION_END;
}
return
attributes.
getImpl_selectionEnd();
}
/**
* Selection end index in the content.
* set to {@code -1} to unset selection.
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
IntegerProperty impl_selectionEndProperty() {
return
getTextAttribute().
impl_selectionEndProperty();
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
ObjectProperty<
Paint>
impl_selectionFillProperty() {
return
getTextAttribute().
impl_selectionFillProperty();
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
PathElement[]
getImpl_caretShape() {
return
impl_caretShapeProperty().
get();
}
/**
* Shape of caret in local coordinates.
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
ReadOnlyObjectProperty<
PathElement[]>
impl_caretShapeProperty() {
return
getTextAttribute().
impl_caretShapeProperty();
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final void
setImpl_caretPosition(int
value) {
if (
value == -1 &&
(
attributes == null ||
attributes.
impl_caretPosition == null)) {
return;
}
impl_caretPositionProperty().
set(
value);
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final int
getImpl_caretPosition() {
if (
attributes == null ||
attributes.
impl_caretPosition == null) {
return
DEFAULT_CARET_POSITION;
}
return
attributes.
getImpl_caretPosition();
}
/**
* caret index in the content.
* set to {@code -1} to unset caret.
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
IntegerProperty impl_caretPositionProperty() {
return
getTextAttribute().
impl_caretPositionProperty();
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final void
setImpl_caretBias(boolean
value) {
if (
value && (
attributes == null ||
attributes.
impl_caretBias == null)) {
return;
}
impl_caretBiasProperty().
set(
value);
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final boolean
isImpl_caretBias() {
if (
attributes == null ||
attributes.
impl_caretBias == null) {
return
DEFAULT_CARET_BIAS;
}
return
getTextAttribute().
isImpl_caretBias();
}
/**
* caret bias in the content. true means a bias towards forward character
* (true=leading/false=trailing)
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
BooleanProperty impl_caretBiasProperty() {
return
getTextAttribute().
impl_caretBiasProperty();
}
/**
* Maps local point to index in the content.
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
HitInfo impl_hitTestChar(
Point2D point) {
if (
point == null) return null;
TextLayout layout =
getTextLayout();
double
x =
point.
getX() -
getX();
double
y =
point.
getY() -
getY() +
getYRendering();
return
layout.
getHitInfo((float)
x, (float)
y);
}
private
PathElement[]
getRange(int
start, int
end, int
type) {
int
length =
getTextInternal().
length();
if (0 <=
start &&
start <
end &&
end <=
length) {
TextLayout layout =
getTextLayout();
float
x = (float)
getX();
float
y = (float)
getY() -
getYRendering();
return
layout.
getRange(
start,
end,
type,
x,
y);
}
return
EMPTY_PATH_ELEMENT_ARRAY;
}
/**
* Returns shape for the range of the text in local coordinates.
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
PathElement[]
impl_getRangeShape(int
start, int
end) {
return
getRange(
start,
end,
TextLayout.
TYPE_TEXT);
}
/**
* Returns shape for the underline in local coordinates.
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final
PathElement[]
impl_getUnderlineShape(int
start, int
end) {
return
getRange(
start,
end,
TextLayout.
TYPE_UNDERLINE);
}
/**
* Shows/Hides on-screen keyboard if available (mobile platform)
*
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
public final void
impl_displaySoftwareKeyboard(boolean
display) {
}
private float
getYAdjustment(
BaseBounds bounds) {
VPos origin =
getTextOrigin();
if (
origin == null)
origin =
DEFAULT_TEXT_ORIGIN;
switch (
origin) {
case
TOP: return -
bounds.
getMinY();
case
BASELINE: return 0;
case
CENTER: return -
bounds.
getMinY() -
bounds.
getHeight() / 2;
case
BOTTOM: return -
bounds.
getMinY() -
bounds.
getHeight();
default: return 0;
}
}
private float
getYRendering() {
if (
isSpan()) return 0;
/* Always logical for rendering */
BaseBounds bounds =
getLogicalBounds();
VPos origin =
getTextOrigin();
if (
origin == null)
origin =
DEFAULT_TEXT_ORIGIN;
if (
getBoundsType() ==
TextBoundsType.
VISUAL) {
BaseBounds vBounds =
getVisualBounds();
float
delta =
vBounds.
getMinY() -
bounds.
getMinY();
switch (
origin) {
case
TOP: return
delta;
case
BASELINE: return -
vBounds.
getMinY() +
delta;
case
CENTER: return
vBounds.
getHeight() / 2 +
delta;
case
BOTTOM: return
vBounds.
getHeight() +
delta;
default: return 0;
}
} else {
switch (
origin) {
case
TOP: return 0;
case
BASELINE: return -
bounds.
getMinY();
case
CENTER: return
bounds.
getHeight() / 2;
case
BOTTOM: return
bounds.
getHeight();
default: return 0;
}
}
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
@
Override
protected final
Bounds impl_computeLayoutBounds() {
if (
isSpan()) {
BaseBounds bounds =
getSpanBounds();
double
width =
bounds.
getWidth();
double
height =
bounds.
getHeight();
return new
BoundingBox(0, 0,
width,
height);
}
if (
getBoundsType() ==
TextBoundsType.
VISUAL) {
/* In Node the layout bounds is computed based in the geom
* bounds and in Shape the geom bounds is computed based
* on the shape (generated here in #configShape()) */
return super.impl_computeLayoutBounds();
}
BaseBounds bounds =
getLogicalBounds();
double
x =
bounds.
getMinX() +
getX();
double
y =
bounds.
getMinY() +
getY() +
getYAdjustment(
bounds);
double
width =
bounds.
getWidth();
double
height =
bounds.
getHeight();
double
wrappingWidth =
getWrappingWidth();
if (
wrappingWidth != 0)
width =
wrappingWidth;
return new
BoundingBox(
x,
y,
width,
height);
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
@
Override
public final
BaseBounds impl_computeGeomBounds(
BaseBounds bounds,
BaseTransform tx) {
if (
isSpan()) {
if (
impl_mode !=
NGShape.
Mode.
FILL &&
getStrokeType() !=
StrokeType.
INSIDE) {
return super.impl_computeGeomBounds(
bounds,
tx);
}
TextLayout layout =
getTextLayout();
bounds =
layout.
getBounds(
getTextSpan(),
bounds);
BaseBounds spanBounds =
getSpanBounds();
float
minX =
bounds.
getMinX() -
spanBounds.
getMinX();
float
minY =
bounds.
getMinY() -
spanBounds.
getMinY();
float
maxX =
minX +
bounds.
getWidth();
float
maxY =
minY +
bounds.
getHeight();
bounds =
bounds.
deriveWithNewBounds(
minX,
minY, 0,
maxX,
maxY, 0);
return
tx.
transform(
bounds,
bounds);
}
if (
getBoundsType() ==
TextBoundsType.
VISUAL) {
if (
getTextInternal().
length() == 0 ||
impl_mode ==
NGShape.
Mode.
EMPTY) {
return
bounds.
makeEmpty();
}
if (
impl_mode ==
NGShape.
Mode.
FILL ||
getStrokeType() ==
StrokeType.
INSIDE) {
/* Optimize for FILL and INNER STROKE: save the cost of shaping each glyph */
BaseBounds visualBounds =
getVisualBounds();
float
x =
visualBounds.
getMinX() + (float)
getX();
float
yadj =
getYAdjustment(
visualBounds);
float
y =
visualBounds.
getMinY() +
yadj + (float)
getY();
bounds.
deriveWithNewBounds(
x,
y, 0,
x +
visualBounds.
getWidth(),
y +
visualBounds.
getHeight(), 0);
return
tx.
transform(
bounds,
bounds);
} else {
/* Let the super class compute the bounds using shape */
return super.impl_computeGeomBounds(
bounds,
tx);
}
}
BaseBounds textBounds =
getLogicalBounds();
float
x =
textBounds.
getMinX() + (float)
getX();
float
yadj =
getYAdjustment(
textBounds);
float
y =
textBounds.
getMinY() +
yadj + (float)
getY();
float
width =
textBounds.
getWidth();
float
height =
textBounds.
getHeight();
float
wrappingWidth = (float)
getWrappingWidth();
if (
wrappingWidth >
width) {
width =
wrappingWidth;
} else {
/* The following adjustment is necessary for the text bounds to be
* relative to the same location as the mirrored bounds returned
* by layout.getBounds().
*/
if (
wrappingWidth > 0) {
NodeOrientation orientation =
getEffectiveNodeOrientation();
if (
orientation ==
NodeOrientation.
RIGHT_TO_LEFT) {
x -=
width -
wrappingWidth;
}
}
}
textBounds = new
RectBounds(
x,
y,
x +
width,
y +
height);
/* handle stroked text */
if (
impl_mode !=
NGShape.
Mode.
FILL &&
getStrokeType() !=
StrokeType.
INSIDE) {
bounds =
super.impl_computeGeomBounds(
bounds,
BaseTransform.
IDENTITY_TRANSFORM);
} else {
TextLayout layout =
getTextLayout();
bounds =
layout.
getBounds(null,
bounds);
x =
bounds.
getMinX() + (float)
getX();
width =
bounds.
getWidth();
bounds =
bounds.
deriveWithNewBounds(
x,
y, 0,
x +
width,
y +
height, 0);
}
bounds =
bounds.
deriveWithUnion(
textBounds);
return
tx.
transform(
bounds,
bounds);
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
@
Override
protected final boolean
impl_computeContains(double
localX, double
localY) {
/* Used for spans, regular text uses bounds based picking */
double
x =
localX +
getSpanBounds().
getMinX();
double
y =
localY +
getSpanBounds().
getMinY();
GlyphList[]
runs =
getRuns();
if (
runs.length != 0) {
for (int
i = 0;
i <
runs.length;
i++) {
GlyphList run =
runs[
i];
com.sun.javafx.geom.
Point2D location =
run.
getLocation();
float
width =
run.
getWidth();
RectBounds lineBounds =
run.
getLineBounds();
float
height =
lineBounds.
getHeight();
if (
location.
x <=
x &&
x <
location.
x +
width &&
location.
y <=
y &&
y <
location.
y +
height) {
return true;
}
}
}
return false;
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
@
Override
public final com.sun.javafx.geom.
Shape impl_configShape() {
if (
impl_mode ==
NGShape.
Mode.
EMPTY ||
getTextInternal().
length() == 0) {
return new
Path2D();
}
com.sun.javafx.geom.
Shape shape =
getShape();
float
x,
y;
if (
isSpan()) {
BaseBounds bounds =
getSpanBounds();
x = -
bounds.
getMinX();
y = -
bounds.
getMinY();
} else {
x = (float)
getX();
y =
getYAdjustment(
getVisualBounds()) + (float)
getY();
}
return
TransformedShape.
translatedShape(
shape,
x,
y);
}
/***************************************************************************
* *
* Stylesheet Handling *
* *
**************************************************************************/
/**
* Super-lazy instantiation pattern from Bill Pugh.
* @treatAsPrivate implementation detail
*/
private static class
StyleableProperties {
private static final
CssMetaData<
Text,
Font>
FONT =
new
FontCssMetaData<
Text>("-fx-font",
Font.
getDefault()) {
@
Override
public boolean
isSettable(
Text node) {
return
node.
font == null || !
node.
font.
isBound();
}
@
Override
public
StyleableProperty<
Font>
getStyleableProperty(
Text node) {
return (
StyleableProperty<
Font>)
node.
fontProperty();
}
};
private static final
CssMetaData<
Text,
Boolean>
UNDERLINE =
new
CssMetaData<
Text,
Boolean>("-fx-underline",
BooleanConverter.
getInstance(),
Boolean.
FALSE) {
@
Override
public boolean
isSettable(
Text node) {
return
node.
attributes == null ||
node.
attributes.
underline == null ||
!
node.
attributes.
underline.
isBound();
}
@
Override
public
StyleableProperty<
Boolean>
getStyleableProperty(
Text node) {
return (
StyleableProperty<
Boolean>)
node.
underlineProperty();
}
};
private static final
CssMetaData<
Text,
Boolean>
STRIKETHROUGH =
new
CssMetaData<
Text,
Boolean>("-fx-strikethrough",
BooleanConverter.
getInstance(),
Boolean.
FALSE) {
@
Override
public boolean
isSettable(
Text node) {
return
node.
attributes == null ||
node.
attributes.
strikethrough == null ||
!
node.
attributes.
strikethrough.
isBound();
}
@
Override
public
StyleableProperty<
Boolean>
getStyleableProperty(
Text node) {
return (
StyleableProperty<
Boolean>)
node.
strikethroughProperty();
}
};
private static final
CssMetaData<
Text,
TextAlignment>
TEXT_ALIGNMENT =
new
CssMetaData<
Text,
TextAlignment>("-fx-text-alignment",
new
EnumConverter<
TextAlignment>(
TextAlignment.class),
TextAlignment.
LEFT) {
@
Override
public boolean
isSettable(
Text node) {
return
node.
attributes == null ||
node.
attributes.
textAlignment == null ||
!
node.
attributes.
textAlignment.
isBound();
}
@
Override
public
StyleableProperty<
TextAlignment>
getStyleableProperty(
Text node) {
return (
StyleableProperty<
TextAlignment>)
node.
textAlignmentProperty();
}
};
private static final
CssMetaData<
Text,
VPos>
TEXT_ORIGIN =
new
CssMetaData<
Text,
VPos>("-fx-text-origin",
new
EnumConverter<
VPos>(
VPos.class),
VPos.
BASELINE) {
@
Override
public boolean
isSettable(
Text node) {
return
node.
attributes == null ||
node.
attributes.
textOrigin == null ||
!
node.
attributes.
textOrigin.
isBound();
}
@
Override
public
StyleableProperty<
VPos>
getStyleableProperty(
Text node) {
return (
StyleableProperty<
VPos>)
node.
textOriginProperty();
}
};
private static final
CssMetaData<
Text,
FontSmoothingType>
FONT_SMOOTHING_TYPE =
new
CssMetaData<
Text,
FontSmoothingType>(
"-fx-font-smoothing-type",
new
EnumConverter<
FontSmoothingType>(
FontSmoothingType.class),
FontSmoothingType.
GRAY) {
@
Override
public boolean
isSettable(
Text node) {
return
node.
fontSmoothingType == null ||
!
node.
fontSmoothingType.
isBound();
}
@
Override
public
StyleableProperty<
FontSmoothingType>
getStyleableProperty(
Text node) {
return (
StyleableProperty<
FontSmoothingType>)
node.
fontSmoothingTypeProperty();
}
};
private static final
CssMetaData<
Text,
Number>
LINE_SPACING =
new
CssMetaData<
Text,
Number>("-fx-line-spacing",
SizeConverter.
getInstance(), 0) {
@
Override
public boolean
isSettable(
Text node) {
return
node.
attributes == null ||
node.
attributes.
lineSpacing == null ||
!
node.
attributes.
lineSpacing.
isBound();
}
@
Override
public
StyleableProperty<
Number>
getStyleableProperty(
Text node) {
return (
StyleableProperty<
Number>)
node.
lineSpacingProperty();
}
};
private static final
CssMetaData<
Text,
TextBoundsType>
BOUNDS_TYPE =
new
CssMetaData<
Text,
TextBoundsType>(
"-fx-bounds-type",
new
EnumConverter<
TextBoundsType>(
TextBoundsType.class),
DEFAULT_BOUNDS_TYPE) {
@
Override
public boolean
isSettable(
Text node) {
return
node.
boundsType == null || !
node.
boundsType.
isBound();
}
@
Override
public
StyleableProperty<
TextBoundsType>
getStyleableProperty(
Text node) {
return (
StyleableProperty<
TextBoundsType>)
node.
boundsTypeProperty();
}
};
private final static
List<
CssMetaData<? extends
Styleable, ?>>
STYLEABLES;
static {
final
List<
CssMetaData<? extends
Styleable, ?>>
styleables =
new
ArrayList<
CssMetaData<? extends
Styleable, ?>>(
Shape.
getClassCssMetaData());
styleables.
add(
FONT);
styleables.
add(
UNDERLINE);
styleables.
add(
STRIKETHROUGH);
styleables.
add(
TEXT_ALIGNMENT);
styleables.
add(
TEXT_ORIGIN);
styleables.
add(
FONT_SMOOTHING_TYPE);
styleables.
add(
LINE_SPACING);
styleables.
add(
BOUNDS_TYPE);
STYLEABLES =
Collections.
unmodifiableList(
styleables);
}
}
/**
* @return The CssMetaData associated with this class, which may include the
* CssMetaData of its super classes.
* @since JavaFX 8.0
*/
public static
List<
CssMetaData<? extends
Styleable, ?>>
getClassCssMetaData() {
return
StyleableProperties.
STYLEABLES;
}
/**
* {@inheritDoc}
*
* @since JavaFX 8.0
*/
@
Override
public
List<
CssMetaData<? extends
Styleable, ?>>
getCssMetaData() {
return
getClassCssMetaData();
}
@
SuppressWarnings("deprecation")
private void
updatePGText() {
final
NGText peer =
impl_getPeer();
if (
impl_isDirty(
DirtyBits.
TEXT_ATTRS)) {
peer.
setUnderline(
isUnderline());
peer.
setStrikethrough(
isStrikethrough());
FontSmoothingType smoothing =
getFontSmoothingType();
if (
smoothing == null)
smoothing =
FontSmoothingType.
GRAY;
peer.
setFontSmoothingType(
smoothing.
ordinal());
}
if (
impl_isDirty(
DirtyBits.
TEXT_FONT)) {
peer.
setFont(
getFontInternal());
}
if (
impl_isDirty(
DirtyBits.
NODE_CONTENTS)) {
peer.
setGlyphs(
getRuns());
}
if (
impl_isDirty(
DirtyBits.
NODE_GEOMETRY)) {
if (
isSpan()) {
BaseBounds spanBounds =
getSpanBounds();
peer.
setLayoutLocation(
spanBounds.
getMinX(),
spanBounds.
getMinY());
} else {
float
x = (float)
getX();
float
y = (float)
getY();
float
yadj =
getYRendering();
peer.
setLayoutLocation(-
x,
yadj -
y);
}
}
if (
impl_isDirty(
DirtyBits.
TEXT_SELECTION)) {
Object fillObj = null;
int
start =
getImpl_selectionStart();
int
end =
getImpl_selectionEnd();
int
length =
getTextInternal().
length();
if (0 <=
start &&
start <
end &&
end <=
length) {
Paint fill =
impl_selectionFillProperty().
get();
fillObj =
fill != null ?
Toolkit.
getPaintAccessor().
getPlatformPaint(
fill) : null;
}
peer.
setSelection(
start,
end,
fillObj);
}
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended
* for use and will be removed in the next version
*/
@
Deprecated
@
Override
public final void
impl_updatePeer() {
super.impl_updatePeer();
updatePGText();
}
/***************************************************************************
* *
* Seldom Used Properties *
* *
**************************************************************************/
private
TextAttribute attributes;
private
TextAttribute getTextAttribute() {
if (
attributes == null) {
attributes = new
TextAttribute();
}
return
attributes;
}
private static final
VPos DEFAULT_TEXT_ORIGIN =
VPos.
BASELINE;
private static final
TextBoundsType DEFAULT_BOUNDS_TYPE =
TextBoundsType.
LOGICAL;
private static final boolean
DEFAULT_UNDERLINE = false;
private static final boolean
DEFAULT_STRIKETHROUGH = false;
private static final
TextAlignment DEFAULT_TEXT_ALIGNMENT =
TextAlignment.
LEFT;
private static final double
DEFAULT_LINE_SPACING = 0;
private static final int
DEFAULT_CARET_POSITION = -1;
private static final int
DEFAULT_SELECTION_START = -1;
private static final int
DEFAULT_SELECTION_END = -1;
private static final
Color DEFAULT_SELECTION_FILL=
Color.
WHITE;
private static final boolean
DEFAULT_CARET_BIAS = true;
private final class
TextAttribute {
private
ObjectProperty<
VPos>
textOrigin;
public final
VPos getTextOrigin() {
return
textOrigin == null ?
DEFAULT_TEXT_ORIGIN :
textOrigin.
get();
}
public final
ObjectProperty<
VPos>
textOriginProperty() {
if (
textOrigin == null) {
textOrigin = new
StyleableObjectProperty<
VPos>(
DEFAULT_TEXT_ORIGIN) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "textOrigin"; }
@
Override public
CssMetaData getCssMetaData() {
return
StyleableProperties.
TEXT_ORIGIN;
}
@
Override public void
invalidated() {
impl_geomChanged();
}
};
}
return
textOrigin;
}
private
BooleanProperty underline;
public final boolean
isUnderline() {
return
underline == null ?
DEFAULT_UNDERLINE :
underline.
get();
}
public final
BooleanProperty underlineProperty() {
if (
underline == null) {
underline = new
StyleableBooleanProperty() {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "underline"; }
@
Override public
CssMetaData getCssMetaData() {
return
StyleableProperties.
UNDERLINE;
}
@
Override public void
invalidated() {
impl_markDirty(
DirtyBits.
TEXT_ATTRS);
if (
getBoundsType() ==
TextBoundsType.
VISUAL) {
impl_geomChanged();
}
}
};
}
return
underline;
}
private
BooleanProperty strikethrough;
public final boolean
isStrikethrough() {
return
strikethrough == null ?
DEFAULT_STRIKETHROUGH :
strikethrough.
get();
}
public final
BooleanProperty strikethroughProperty() {
if (
strikethrough == null) {
strikethrough = new
StyleableBooleanProperty() {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "strikethrough"; }
@
Override public
CssMetaData getCssMetaData() {
return
StyleableProperties.
STRIKETHROUGH;
}
@
Override public void
invalidated() {
impl_markDirty(
DirtyBits.
TEXT_ATTRS);
if (
getBoundsType() ==
TextBoundsType.
VISUAL) {
impl_geomChanged();
}
}
};
}
return
strikethrough;
}
private
ObjectProperty<
TextAlignment>
textAlignment;
public final
TextAlignment getTextAlignment() {
return
textAlignment == null ?
DEFAULT_TEXT_ALIGNMENT :
textAlignment.
get();
}
public final
ObjectProperty<
TextAlignment>
textAlignmentProperty() {
if (
textAlignment == null) {
textAlignment =
new
StyleableObjectProperty<
TextAlignment>(
DEFAULT_TEXT_ALIGNMENT) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "textAlignment"; }
@
Override public
CssMetaData getCssMetaData() {
return
StyleableProperties.
TEXT_ALIGNMENT;
}
@
Override public void
invalidated() {
if (!
isSpan()) {
TextAlignment alignment =
get();
if (
alignment == null) {
alignment =
DEFAULT_TEXT_ALIGNMENT;
}
TextLayout layout =
getTextLayout();
if (
layout.
setAlignment(
alignment.
ordinal())) {
needsTextLayout();
}
}
}
};
}
return
textAlignment;
}
private
DoubleProperty lineSpacing;
public final double
getLineSpacing() {
return
lineSpacing == null ?
DEFAULT_LINE_SPACING :
lineSpacing.
get();
}
public final
DoubleProperty lineSpacingProperty() {
if (
lineSpacing == null) {
lineSpacing =
new
StyleableDoubleProperty(
DEFAULT_LINE_SPACING) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "lineSpacing"; }
@
Override public
CssMetaData getCssMetaData() {
return
StyleableProperties.
LINE_SPACING;
}
@
Override public void
invalidated() {
if (!
isSpan()) {
TextLayout layout =
getTextLayout();
if (
layout.
setLineSpacing((float)
get())) {
needsTextLayout();
}
}
}
};
}
return
lineSpacing;
}
private
ReadOnlyDoubleWrapper baselineOffset;
public final
ReadOnlyDoubleProperty baselineOffsetProperty() {
if (
baselineOffset == null) {
baselineOffset = new
ReadOnlyDoubleWrapper(
Text.this, "baselineOffset") {
{
bind(new
DoubleBinding() {
{
bind(
fontProperty());}
@
Override protected double
computeValue() {
/* This method should never be used for spans.
* If it is, it will still returns the ascent
* for the first line in the layout */
BaseBounds bounds =
getLogicalBounds();
return -
bounds.
getMinY();
}
});}
};
}
return
baselineOffset.
getReadOnlyProperty();
}
@
Deprecated
private
ObjectProperty<
PathElement[]>
impl_selectionShape;
private
ObjectBinding<
PathElement[]>
impl_selectionBinding;
@
Deprecated
public final
ReadOnlyObjectProperty<
PathElement[]>
impl_selectionShapeProperty() {
if (
impl_selectionShape == null) {
impl_selectionBinding = new
ObjectBinding<
PathElement[]>() {
{
bind(
impl_selectionStartProperty(),
impl_selectionEndProperty());}
@
Override protected
PathElement[]
computeValue() {
int
start =
getImpl_selectionStart();
int
end =
getImpl_selectionEnd();
return
getRange(
start,
end,
TextLayout.
TYPE_TEXT);
}
};
impl_selectionShape = new
SimpleObjectProperty<
PathElement[]>(
Text.this, "impl_selectionShape");
impl_selectionShape.
bind(
impl_selectionBinding);
}
return
impl_selectionShape;
}
private
ObjectProperty<
Paint>
selectionFill;
@
Deprecated
public final
ObjectProperty<
Paint>
impl_selectionFillProperty() {
if (
selectionFill == null) {
selectionFill =
new
ObjectPropertyBase<
Paint>(
DEFAULT_SELECTION_FILL) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "impl_selectionFill"; }
@
Override protected void
invalidated() {
impl_markDirty(
DirtyBits.
TEXT_SELECTION);
}
};
}
return
selectionFill;
}
@
Deprecated
private
IntegerProperty impl_selectionStart;
@
Deprecated
public final int
getImpl_selectionStart() {
return
impl_selectionStart == null ?
DEFAULT_SELECTION_START :
impl_selectionStart.
get();
}
@
Deprecated
public final
IntegerProperty impl_selectionStartProperty() {
if (
impl_selectionStart == null) {
impl_selectionStart =
new
IntegerPropertyBase(
DEFAULT_SELECTION_START) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "impl_selectionStart"; }
@
Override protected void
invalidated() {
impl_markDirty(
DirtyBits.
TEXT_SELECTION);
notifyAccessibleAttributeChanged(
AccessibleAttribute.
SELECTION_START);
}
};
}
return
impl_selectionStart;
}
@
Deprecated
private
IntegerProperty impl_selectionEnd;
@
Deprecated
public final int
getImpl_selectionEnd() {
return
impl_selectionEnd == null ?
DEFAULT_SELECTION_END :
impl_selectionEnd.
get();
}
@
Deprecated
public final
IntegerProperty impl_selectionEndProperty() {
if (
impl_selectionEnd == null) {
impl_selectionEnd =
new
IntegerPropertyBase(
DEFAULT_SELECTION_END) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "impl_selectionEnd"; }
@
Override protected void
invalidated() {
impl_markDirty(
DirtyBits.
TEXT_SELECTION);
notifyAccessibleAttributeChanged(
AccessibleAttribute.
SELECTION_END);
}
};
}
return
impl_selectionEnd;
}
@
Deprecated
private
ObjectProperty<
PathElement[]>
impl_caretShape;
private
ObjectBinding<
PathElement[]>
impl_caretBinding;
@
Deprecated
public final
ReadOnlyObjectProperty<
PathElement[]>
impl_caretShapeProperty() {
if (
impl_caretShape == null) {
impl_caretBinding = new
ObjectBinding<
PathElement[]>() {
{
bind(
impl_caretPositionProperty(),
impl_caretBiasProperty());}
@
Override protected
PathElement[]
computeValue() {
int
pos =
getImpl_caretPosition();
int
length =
getTextInternal().
length();
if (0 <=
pos &&
pos <=
length) {
boolean
bias =
isImpl_caretBias();
float
x = (float)
getX();
float
y = (float)
getY() -
getYRendering();
TextLayout layout =
getTextLayout();
return
layout.
getCaretShape(
pos,
bias,
x,
y);
}
return
EMPTY_PATH_ELEMENT_ARRAY;
}
};
impl_caretShape = new
SimpleObjectProperty<
PathElement[]>(
Text.this, "impl_caretShape");
impl_caretShape.
bind(
impl_caretBinding);
}
return
impl_caretShape;
}
@
Deprecated
private
IntegerProperty impl_caretPosition;
@
Deprecated
public final int
getImpl_caretPosition() {
return
impl_caretPosition == null ?
DEFAULT_CARET_POSITION :
impl_caretPosition.
get();
}
@
Deprecated
public final
IntegerProperty impl_caretPositionProperty() {
if (
impl_caretPosition == null) {
impl_caretPosition =
new
IntegerPropertyBase(
DEFAULT_CARET_POSITION) {
@
Override public
Object getBean() { return
Text.this; }
@
Override public
String getName() { return "impl_caretPosition"; }
@
Override protected void
invalidated() {
notifyAccessibleAttributeChanged(
AccessibleAttribute.
SELECTION_END);
}
};
}
return
impl_caretPosition;
}
@
Deprecated
private
BooleanProperty impl_caretBias;
@
Deprecated
public final boolean
isImpl_caretBias() {
return
impl_caretBias == null ?
DEFAULT_CARET_BIAS :
impl_caretBias.
get();
}
@
Deprecated
public final
BooleanProperty impl_caretBiasProperty() {
if (
impl_caretBias == null) {
impl_caretBias =
new
SimpleBooleanProperty(
Text.this, "impl_caretBias",
DEFAULT_CARET_BIAS);
}
return
impl_caretBias;
}
}
/**
* Returns a string representation of this {@code Text} object.
* @return a string representation of this {@code Text} object.
*/
@
Override
public
String toString() {
final
StringBuilder sb = new
StringBuilder("Text[");
String id =
getId();
if (
id != null) {
sb.
append("id=").
append(
id).
append(", ");
}
sb.
append("text=\"").
append(
getText()).
append("\"");
sb.
append(", x=").
append(
getX());
sb.
append(", y=").
append(
getY());
sb.
append(", alignment=").
append(
getTextAlignment());
sb.
append(", origin=").
append(
getTextOrigin());
sb.
append(", boundsType=").
append(
getBoundsType());
double
spacing =
getLineSpacing();
if (
spacing !=
DEFAULT_LINE_SPACING) {
sb.
append(", lineSpacing=").
append(
spacing);
}
double
wrap =
getWrappingWidth();
if (
wrap != 0) {
sb.
append(", wrappingWidth=").
append(
wrap);
}
sb.
append(", font=").
append(
getFont());
sb.
append(", fontSmoothingType=").
append(
getFontSmoothingType());
if (
isStrikethrough()) {
sb.
append(", strikethrough");
}
if (
isUnderline()) {
sb.
append(", underline");
}
sb.
append(", fill=").
append(
getFill());
Paint stroke =
getStroke();
if (
stroke != null) {
sb.
append(", stroke=").
append(
stroke);
sb.
append(", strokeWidth=").
append(
getStrokeWidth());
}
return
sb.
append("]").
toString();
}
@
Override
public
Object queryAccessibleAttribute(
AccessibleAttribute attribute,
Object...
parameters) {
switch (
attribute) {
case
TEXT: {
String accText =
getAccessibleText();
if (
accText != null && !
accText.
isEmpty()) return
accText;
return
getText();
}
case
FONT: return
getFont();
case
CARET_OFFSET: {
int
sel =
getImpl_caretPosition();
if (
sel >= 0) return
sel;
return
getText().
length();
}
case
SELECTION_START: {
int
sel =
getImpl_selectionStart();
if (
sel >= 0) return
sel;
sel =
getImpl_caretPosition();
if (
sel >= 0) return
sel;
return
getText().
length();
}
case
SELECTION_END: {
int
sel =
getImpl_selectionEnd();
if (
sel >= 0) return
sel;
sel =
getImpl_caretPosition();
if (
sel >= 0) return
sel;
return
getText().
length();
}
case
LINE_FOR_OFFSET: {
int
offset = (
Integer)
parameters[0];
if (
offset >
getTextInternal().
length()) return null;
TextLine[]
lines =
getTextLayout().
getLines();
int
lineIndex = 0;
for (int
i = 1;
i <
lines.length;
i++) {
TextLine line =
lines[
i];
if (
line.
getStart() >
offset) break;
lineIndex++;
}
return
lineIndex;
}
case
LINE_START: {
int
lineIndex = (
Integer)
parameters[0];
TextLine[]
lines =
getTextLayout().
getLines();
if (0 <=
lineIndex &&
lineIndex <
lines.length) {
TextLine line =
lines[
lineIndex];
return
line.
getStart();
}
return null;
}
case
LINE_END: {
int
lineIndex = (
Integer)
parameters[0];
TextLine[]
lines =
getTextLayout().
getLines();
if (0 <=
lineIndex &&
lineIndex <
lines.length) {
TextLine line =
lines[
lineIndex];
return
line.
getStart() +
line.
getLength();
}
return null;
}
case
OFFSET_AT_POINT: {
Point2D point = (
Point2D)
parameters[0];
point =
screenToLocal(
point);
return
impl_hitTestChar(
point).
getCharIndex();
}
case
BOUNDS_FOR_RANGE: {
int
start = (
Integer)
parameters[0];
int
end = (
Integer)
parameters[1];
PathElement[]
elements =
impl_getRangeShape(
start,
end + 1);
/* Each bounds is defined by a MoveTo (top-left) followed by
* 4 LineTo (to top-right, bottom-right, bottom-left, back to top-left).
*/
Bounds[]
bounds = new
Bounds[
elements.length / 5];
int
index = 0;
for (int
i = 0;
i <
bounds.length;
i++) {
MoveTo topLeft = (
MoveTo)
elements[
index];
LineTo topRight = (
LineTo)
elements[
index+1];
LineTo bottomRight = (
LineTo)
elements[
index+2];
BoundingBox b = new
BoundingBox(
topLeft.
getX(),
topLeft.
getY(),
topRight.
getX() -
topLeft.
getX(),
bottomRight.
getY() -
topRight.
getY());
bounds[
i] =
localToScreen(
b);
index += 5;
}
return
bounds;
}
default: return super.queryAccessibleAttribute(
attribute,
parameters);
}
}
}