Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.text; 027 028import java.util.ArrayList; 029import java.util.Collections; 030import java.util.List; 031 032import javafx.beans.DefaultProperty; 033import javafx.beans.InvalidationListener; 034import javafx.beans.Observable; 035import javafx.beans.binding.DoubleBinding; 036import javafx.beans.binding.ObjectBinding; 037import javafx.beans.property.BooleanProperty; 038import javafx.beans.property.DoubleProperty; 039import javafx.beans.property.DoublePropertyBase; 040import javafx.beans.property.IntegerProperty; 041import javafx.beans.property.IntegerPropertyBase; 042import javafx.beans.property.ObjectProperty; 043import javafx.beans.property.ObjectPropertyBase; 044import javafx.beans.property.ReadOnlyDoubleProperty; 045import javafx.beans.property.ReadOnlyDoubleWrapper; 046import javafx.beans.property.ReadOnlyObjectProperty; 047import javafx.beans.property.SimpleBooleanProperty; 048import javafx.beans.property.SimpleIntegerProperty; 049import javafx.beans.property.SimpleObjectProperty; 050import javafx.beans.property.StringProperty; 051import javafx.beans.property.StringPropertyBase; 052import javafx.geometry.BoundingBox; 053import javafx.geometry.Bounds; 054import javafx.geometry.NodeOrientation; 055import javafx.geometry.Point2D; 056import javafx.geometry.VPos; 057import javafx.scene.paint.Color; 058import javafx.scene.paint.Paint; 059import javafx.scene.shape.PathElement; 060import javafx.scene.shape.Shape; 061import javafx.scene.shape.StrokeType; 062 063import javafx.css.StyleableBooleanProperty; 064import javafx.css.StyleableDoubleProperty; 065import javafx.css.StyleableObjectProperty; 066import javafx.css.CssMetaData; 067import com.sun.javafx.css.converters.BooleanConverter; 068import com.sun.javafx.css.converters.EnumConverter; 069import com.sun.javafx.css.converters.SizeConverter; 070import com.sun.javafx.geom.BaseBounds; 071import com.sun.javafx.geom.Path2D; 072import com.sun.javafx.geom.RectBounds; 073import com.sun.javafx.geom.TransformedShape; 074import com.sun.javafx.geom.transform.BaseTransform; 075import com.sun.javafx.accessible.AccessibleText; 076import com.sun.javafx.scene.DirtyBits; 077import com.sun.javafx.scene.text.GlyphList; 078import com.sun.javafx.scene.text.HitInfo; 079import com.sun.javafx.scene.text.TextLayout; 080import com.sun.javafx.scene.text.TextLayoutFactory; 081import com.sun.javafx.scene.text.TextSpan; 082import com.sun.javafx.sg.PGNode; 083import com.sun.javafx.sg.PGText; 084import com.sun.javafx.sg.PGShape.Mode; 085import com.sun.javafx.tk.Toolkit; 086import com.sun.javafx.accessible.providers.AccessibleProvider; 087import com.sun.javafx.accessible.AccessibleNode; 088import javafx.css.FontCssMetaData; 089import javafx.css.Styleable; 090import javafx.css.StyleableProperty; 091 092/** 093 * The {@code Text} class defines a node that displays a text. 094 * 095 * Paragraphs are separated by {@code '\n'} and the text is wrapped on 096 * paragraph boundaries. 097 * 098<PRE> 099import javafx.scene.text.*; 100 101Text t = new Text(10, 50, "This is a test"); 102t.setFont(new Font(20)); 103</PRE> 104 * 105<PRE> 106import javafx.scene.text.*; 107 108Text t = new Text(); 109text.setFont(new Font(20)); 110text.setText("First row\nSecond row"); 111</PRE> 112 * 113<PRE> 114import javafx.scene.text.*; 115 116Text t = new Text(); 117text.setFont(new Font(20)); 118text.setWrappingWidth(200); 119text.setTextAlignment(TextAlignment.JUSTIFY) 120text.setText("The quick brown fox jumps over the lazy dog"); 121</PRE> 122 */ 123@DefaultProperty("text") 124public class Text extends Shape { 125 126 private TextLayout layout; 127 private static final PathElement[] EMPTY_PATH_ELEMENT_ARRAY = new PathElement[0]; 128 129 /** 130 * @treatAsPrivate implementation detail 131 * @deprecated This is an internal API that is not intended 132 * for use and will be removed in the next version 133 */ 134 @Deprecated 135 @Override 136 protected final PGNode impl_createPGNode() { 137 return Toolkit.getToolkit().createPGText(); 138 } 139 140 private PGText getPGText() { 141 return (PGText) impl_getPGNode(); 142 } 143 144 /** 145 * Creates an empty instance of Text. 146 */ 147 public Text() { 148 InvalidationListener listener = new InvalidationListener() { 149 @Override public void invalidated(Observable observable) { 150 checkSpan(); 151 } 152 }; 153 parentProperty().addListener(listener); 154 managedProperty().addListener(listener); 155 effectiveNodeOrientationProperty().addListener(new InvalidationListener() { 156 @Override public void invalidated(Observable observable) { 157 checkOrientation(); 158 } 159 }); 160 setPickOnBounds(true); 161 } 162 163 /** 164 * Creates an instance of Text containing the given string. 165 * @param text text to be contained in the instance 166 */ 167 public Text(String text) { 168 this(); 169 setText(text); 170 } 171 172 /** 173 * Creates an instance of Text on the given coordinates containing the 174 * given string. 175 * @param x the horizontal position of the text 176 * @param y the vertical position of the text 177 * @param text text to be contained in the instance 178 */ 179 public Text(double x, double y, String text) { 180 this(text); 181 setX(x); 182 setY(y); 183 } 184 185 private boolean isSpan; 186 private boolean isSpan() { 187 return isSpan; 188 } 189 190 private void checkSpan() { 191 isSpan = isManaged() && getParent() instanceof TextFlow; 192 } 193 194 private void checkOrientation() { 195 if (!isSpan()) { 196 /* Using impl_transformsChanged to detect for orientation change. 197 * This can be improved if EffectiveNodeOrientation becomes a 198 * property. See http://javafx-jira.kenai.com/browse/RT-26140 199 */ 200 NodeOrientation orientation = getEffectiveNodeOrientation(); 201 boolean rtl = orientation == NodeOrientation.RIGHT_TO_LEFT; 202 int dir = rtl ? TextLayout.DIRECTION_RTL : TextLayout.DIRECTION_LTR; 203 TextLayout layout = getTextLayout(); 204 if (layout.setDirection(dir)) { 205 needsTextLayout(); 206 } 207 } 208 } 209 210 @Override 211 public boolean usesMirroring() { 212 return false; 213 } 214 215 private void needsFullTextLayout() { 216 if (isSpan()) { 217 /* Create new text span every time the font or text changes 218 * so the text layout can see that the content has changed. 219 */ 220 textSpan = null; 221 222 /* Relies on impl_geomChanged() to request text flow to relayout */ 223 } else { 224 TextLayout layout = getTextLayout(); 225 String string = getTextInternal(); 226 Object font = getFontInternal(); 227 layout.setContent(string, font); 228 } 229 needsTextLayout(); 230 } 231 232 private void needsTextLayout() { 233 textRuns = null; 234 impl_geomChanged(); 235 impl_markDirty(DirtyBits.NODE_CONTENTS); 236 } 237 238 private TextSpan textSpan; 239 TextSpan getTextSpan() { 240 if (textSpan == null) { 241 textSpan = new TextSpan() { 242 @Override public String getText() { 243 return getTextInternal(); 244 } 245 @Override public Object getFont() { 246 return getFontInternal(); 247 } 248 @Override public RectBounds getBounds() { 249 return null; 250 } 251 }; 252 } 253 return textSpan; 254 } 255 256 private TextLayout getTextLayout() { 257 if (isSpan()) { 258 layout = null; 259 TextFlow parent = (TextFlow)getParent(); 260 return parent.getTextLayout(); 261 } 262 if (layout == null) { 263 TextLayoutFactory factory = Toolkit.getToolkit().getTextLayoutFactory(); 264 layout = factory.createLayout(); 265 String string = getTextInternal(); 266 Object font = getFontInternal(); 267 TextAlignment alignment = getTextAlignment(); 268 if (alignment == null) alignment = DEFAULT_TEXT_ALIGNMENT; 269 layout.setContent(string, font); 270 layout.setAlignment(alignment.ordinal()); 271 layout.setLineSpacing((float)getLineSpacing()); 272 layout.setWrapWidth((float)getWrappingWidth()); 273 if (getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT) { 274 layout.setDirection(TextLayout.DIRECTION_RTL); 275 } else { 276 layout.setDirection(TextLayout.DIRECTION_LTR); 277 } 278 } 279 return layout; 280 } 281 282 private GlyphList[] textRuns = null; 283 private BaseBounds spanBounds = new RectBounds(); /* relative to the textlayout */ 284 private boolean spanBoundsInvalid = true; 285 286 void layoutSpan(GlyphList[] runs) { 287 /* Sometimes a property change in the text node will causes layout in 288 * text flow. In this case all the dirty bits are already clear and no 289 * extra work is necessary. Other times the layout is caused by changes 290 * in the text flow object (wrapping width and text alignment for example). 291 * In the second case the dirty bits must be set here using 292 * needsTextLayout(). Note that needsTextLayout() uses impl_geomChanged() 293 * which causes another (undesired) layout request in the parent. 294 * In general this is not a problem because shapes are not resizable and 295 * region do not propagate layout changes to the parent. 296 * This is a special case where a shape is resized by the parent during 297 * layoutChildren(). See TextFlow#requestLayout() for information how 298 * text flow deals with this situation. 299 */ 300 needsTextLayout(); 301 302 spanBoundsInvalid = true; 303 int count = 0; 304 TextSpan span = getTextSpan(); 305 for (int i = 0; i < runs.length; i++) { 306 GlyphList run = runs[i]; 307 if (run.getTextSpan() == span) { 308 count++; 309 } 310 } 311 textRuns = new GlyphList[count]; 312 count = 0; 313 for (int i = 0; i < runs.length; i++) { 314 GlyphList run = runs[i]; 315 if (run.getTextSpan() == span) { 316 textRuns[count++] = run; 317 } 318 } 319 } 320 321 BaseBounds getSpanBounds() { 322 if (spanBoundsInvalid) { 323 GlyphList[] runs = getRuns(); 324 if (runs.length != 0) { 325 float left = Float.POSITIVE_INFINITY; 326 float top = Float.POSITIVE_INFINITY; 327 float right = 0; 328 float bottom = 0; 329 for (int i = 0; i < runs.length; i++) { 330 GlyphList run = runs[i]; 331 com.sun.javafx.geom.Point2D location = run.getLocation(); 332 float width = run.getWidth(); 333 float height = run.getLineBounds().getHeight(); 334 left = Math.min(location.x, left); 335 top = Math.min(location.y, top); 336 right = Math.max(location.x + width, right); 337 bottom = Math.max(location.y + height, bottom); 338 } 339 spanBounds = spanBounds.deriveWithNewBounds(left, top, 0, 340 right, bottom, 0); 341 } else { 342 spanBounds = spanBounds.makeEmpty(); 343 } 344 spanBoundsInvalid = false; 345 } 346 return spanBounds; 347 } 348 349 private GlyphList[] getRuns() { 350 if (textRuns != null) return textRuns; 351 if (isSpan()) { 352 /* List of run is initialized when the TextFlow layout the children */ 353 getParent().layout(); 354 } else { 355 TextLayout layout = getTextLayout(); 356 textRuns = layout.getRuns(); 357 } 358 return textRuns; 359 } 360 361 private com.sun.javafx.geom.Shape getShape() { 362 TextLayout layout = getTextLayout(); 363 /* TextLayout has the text shape cached */ 364 int type = TextLayout.TYPE_TEXT; 365 TextSpan filter = null; 366 if (isSpan()) { 367 /* Spans are always relative to the top */ 368 type |= TextLayout.TYPE_TOP; 369 filter = getTextSpan(); 370 } else { 371 /* Relative to baseline (first line) 372 * This shape can be translate in the y axis according 373 * to text origin, see impl_configShape(). 374 */ 375 type |= TextLayout.TYPE_BASELINE; 376 } 377 return layout.getShape(type, filter); 378 } 379 380 private BaseBounds getVisualBounds() { 381 return getShape().getBounds(); 382 } 383 384 private BaseBounds getLogicalBounds() { 385 TextLayout layout = getTextLayout(); 386 /* TextLayout has the bounds cached */ 387 return layout.getBounds(); 388 } 389 390 /** 391 * Defines text string that is to be displayed. 392 * 393 * @defaultValue empty string 394 */ 395 private StringProperty text; 396 397 public final void setText(String value) { 398 if (value == null) value = ""; 399 textProperty().set(value); 400 } 401 402 public final String getText() { 403 return text == null ? "" : text.get(); 404 } 405 406 private String getTextInternal() { 407 // this might return null in case of bound property 408 String localText = getText(); 409 return localText == null ? "" : localText; 410 } 411 412 public final StringProperty textProperty() { 413 if (text == null) { 414 text = new StringPropertyBase("") { 415 @Override public Object getBean() { return Text.this; } 416 @Override public String getName() { return "text"; } 417 @Override public void invalidated() { 418 needsFullTextLayout(); 419 setImpl_selectionStart(-1); 420 setImpl_selectionEnd(-1); 421 setImpl_caretPosition(-1); 422 setImpl_caretBias(true); 423 424 // MH: Functionality copied from store() method, 425 // which was removed. 426 // Wonder what should happen if text is bound 427 // and becomes null? 428 final String value = get(); 429 if ((value == null) && !isBound()) { 430 set(""); 431 } 432 } 433 }; 434 } 435 return text; 436 } 437 438 /** 439 * Defines the X coordinate of text origin. 440 * 441 * @defaultValue 0 442 */ 443 private DoubleProperty x; 444 445 public final void setX(double value) { 446 xProperty().set(value); 447 } 448 449 public final double getX() { 450 return x == null ? 0.0 : x.get(); 451 } 452 453 public final DoubleProperty xProperty() { 454 if (x == null) { 455 x = new DoublePropertyBase() { 456 @Override public Object getBean() { return Text.this; } 457 @Override public String getName() { return "x"; } 458 @Override public void invalidated() { 459 impl_geomChanged(); 460 } 461 }; 462 } 463 return x; 464 } 465 466 /** 467 * Defines the Y coordinate of text origin. 468 * 469 * @defaultValue 0 470 */ 471 private DoubleProperty y; 472 473 public final void setY(double value) { 474 yProperty().set(value); 475 } 476 477 public final double getY() { 478 return y == null ? 0.0 : y.get(); 479 } 480 481 public final DoubleProperty yProperty() { 482 if (y == null) { 483 y = new DoublePropertyBase() { 484 @Override public Object getBean() { return Text.this; } 485 @Override public String getName() { return "y"; } 486 @Override public void invalidated() { 487 impl_geomChanged(); 488 } 489 }; 490 } 491 return y; 492 } 493 494 /** 495 * Defines the font of text. 496 * 497 * @defaultValue Font{} 498 */ 499 private ObjectProperty<Font> font; 500 501 public final void setFont(Font value) { 502 fontProperty().set(value); 503 } 504 505 public final Font getFont() { 506 return font == null ? Font.getDefault() : font.get(); 507 } 508 509 /** 510 * Internally used safe version of getFont which never returns null. 511 * 512 * @return the font 513 */ 514 private Object getFontInternal() { 515 Font font = getFont(); 516 if (font == null) font = Font.getDefault(); 517 return font.impl_getNativeFont(); 518 } 519 520 public final ObjectProperty<Font> fontProperty() { 521 if (font == null) { 522 font = new StyleableObjectProperty<Font>(Font.getDefault()) { 523 @Override public Object getBean() { return Text.this; } 524 @Override public String getName() { return "font"; } 525 @Override public CssMetaData<Text,Font> getCssMetaData() { 526 return StyleableProperties.FONT; 527 } 528 @Override public void invalidated() { 529 needsFullTextLayout(); 530 impl_markDirty(DirtyBits.TEXT_FONT); 531 } 532 }; 533 } 534 return font; 535 } 536 537 public final void setTextOrigin(VPos value) { 538 textOriginProperty().set(value); 539 } 540 541 public final VPos getTextOrigin() { 542 if (attributes == null || attributes.textOrigin == null) { 543 return DEFAULT_TEXT_ORIGIN; 544 } 545 return attributes.getTextOrigin(); 546 } 547 548 /** 549 * Defines the origin of text coordinate system in local coordinates. 550 * Note: in case multiple rows are rendered {@code VPos.BASELINE} and 551 * {@code VPos.TOP} define the origin of the top row while 552 * {@code VPos.BOTTOM} defines the origin of the bottom row. 553 * 554 * @defaultValue VPos.BASELINE 555 */ 556 public final ObjectProperty<VPos> textOriginProperty() { 557 return getTextAttribute().textOriginProperty(); 558 } 559 560 /** 561 * Determines how the bounds of the text node are calculated. 562 * Logical bounds is a more appropriate default for text than 563 * the visual bounds. See {@code TextBoundsType} for more information. 564 * 565 * @defaultValue TextBoundsType.LOGICAL 566 * @since JavaFX 1.3 567 */ 568 private ObjectProperty<TextBoundsType> boundsType; 569 570 public final void setBoundsType(TextBoundsType value) { 571 boundsTypeProperty().set(value); 572 } 573 574 public final TextBoundsType getBoundsType() { 575 return boundsType == null ? 576 DEFAULT_BOUNDS_TYPE : boundsTypeProperty().get(); 577 } 578 579 public final ObjectProperty<TextBoundsType> boundsTypeProperty() { 580 if (boundsType == null) { 581 boundsType = 582 new StyleableObjectProperty<TextBoundsType>(DEFAULT_BOUNDS_TYPE) { 583 @Override public Object getBean() { return Text.this; } 584 @Override public String getName() { return "boundsType"; } 585 @Override public CssMetaData<Text,TextBoundsType> getCssMetaData() { 586 return StyleableProperties.BOUNDS_TYPE; 587 } 588 @Override public void invalidated() { 589 TextLayout layout = getTextLayout(); 590 int type = 0; 591 if (boundsType.get() == TextBoundsType.LOGICAL_VERTICAL_CENTER) { 592 type |= TextLayout.BOUNDS_CENTER; 593 } 594 if (layout.setBoundsType(type)) { 595 needsTextLayout(); 596 } else { 597 impl_geomChanged(); 598 } 599 } 600 }; 601 } 602 return boundsType; 603 } 604 605 /** 606 * Defines a width constraint for the text in user space coordinates, 607 * e.g. pixels, not glyph or character count. 608 * If the value is {@code > 0} text will be line wrapped as needed 609 * to satisfy this constraint. 610 * 611 * @defaultValue 0 612 */ 613 private DoubleProperty wrappingWidth; 614 615 public final void setWrappingWidth(double value) { 616 wrappingWidthProperty().set(value); 617 } 618 619 public final double getWrappingWidth() { 620 return wrappingWidth == null ? 0 : wrappingWidth.get(); 621 } 622 623 public final DoubleProperty wrappingWidthProperty() { 624 if (wrappingWidth == null) { 625 wrappingWidth = new DoublePropertyBase() { 626 @Override public Object getBean() { return Text.this; } 627 @Override public String getName() { return "wrappingWidth"; } 628 @Override public void invalidated() { 629 if (!isSpan()) { 630 TextLayout layout = getTextLayout(); 631 if (layout.setWrapWidth((float)get())) { 632 needsTextLayout(); 633 } else { 634 impl_geomChanged(); 635 } 636 } 637 } 638 }; 639 } 640 return wrappingWidth; 641 } 642 643 public final void setUnderline(boolean value) { 644 underlineProperty().set(value); 645 } 646 647 public final boolean isUnderline() { 648 if (attributes == null || attributes.underline == null) { 649 return DEFAULT_UNDERLINE; 650 } 651 return attributes.isUnderline(); 652 } 653 654 /** 655 * Defines if each line of text should have a line below it. 656 * 657 * @defaultValue false 658 */ 659 public final BooleanProperty underlineProperty() { 660 return getTextAttribute().underlineProperty(); 661 } 662 663 public final void setStrikethrough(boolean value) { 664 strikethroughProperty().set(value); 665 } 666 667 public final boolean isStrikethrough() { 668 if (attributes == null || attributes.strikethrough == null) { 669 return DEFAULT_STRIKETHROUGH; 670 } 671 return attributes.isStrikethrough(); 672 } 673 674 /** 675 * Defines if each line of text should have a line through it. 676 * 677 * @defaultValue false 678 */ 679 public final BooleanProperty strikethroughProperty() { 680 return getTextAttribute().strikethroughProperty(); 681 } 682 683 public final void setTextAlignment(TextAlignment value) { 684 textAlignmentProperty().set(value); 685 } 686 687 public final TextAlignment getTextAlignment() { 688 if (attributes == null || attributes.textAlignment == null) { 689 return DEFAULT_TEXT_ALIGNMENT; 690 } 691 return attributes.getTextAlignment(); 692 } 693 694 /** 695 * Defines horizontal text alignment in the bounding box. 696 * 697 * The width of the bounding box is defined by the widest row. 698 * 699 * Note: In the case of a single line of text, where the width of the 700 * node is determined by the width of the text, the alignment setting 701 * has no effect. 702 * 703 * @defaultValue TextAlignment.LEFT 704 */ 705 public final ObjectProperty<TextAlignment> textAlignmentProperty() { 706 return getTextAttribute().textAlignmentProperty(); 707 } 708 709 public final void setLineSpacing(double spacing) { 710 lineSpacingProperty().set(spacing); 711 } 712 713 public final double getLineSpacing() { 714 if (attributes == null || attributes.lineSpacing == null) { 715 return DEFAULT_LINE_SPACING; 716 } 717 return attributes.getLineSpacing(); 718 } 719 720 /** 721 * Defines the vertical space in pixel between lines. 722 * 723 * @defaultValue 0 724 * 725 * @since 8.0 726 */ 727 public final DoubleProperty lineSpacingProperty() { 728 return getTextAttribute().lineSpacingProperty(); 729 } 730 731 @Override 732 public final double getBaselineOffset() { 733 return baselineOffsetProperty().get(); 734 } 735 736 /** 737 * The 'alphabetic' (or roman) baseline offset from the Text node's 738 * layoutBounds.minY location. 739 * The value typically corresponds to the max ascent of the font. 740 * 741 * @since JavaFX 1.3 742 */ 743 public final ReadOnlyDoubleProperty baselineOffsetProperty() { 744 return getTextAttribute().baselineOffsetProperty(); 745 } 746 747 /** 748 * Specifies a requested font smoothing type : gray or LCD. 749 * 750 * The width of the bounding box is defined by the widest row. 751 * 752 * Note: LCD mode doesn't apply in numerous cases, such as various 753 * compositing modes, where effects are applied and very large glyphs. 754 * 755 * @defaultValue FontSmoothingType.GRAY 756 */ 757 private ObjectProperty<FontSmoothingType> fontSmoothingType; 758 759 public final void setFontSmoothingType(FontSmoothingType value) { 760 fontSmoothingTypeProperty().set(value); 761 } 762 763 public final FontSmoothingType getFontSmoothingType() { 764 return fontSmoothingType == null ? 765 FontSmoothingType.GRAY : fontSmoothingType.get(); 766 } 767 768 public final ObjectProperty<FontSmoothingType> 769 fontSmoothingTypeProperty() { 770 if (fontSmoothingType == null) { 771 fontSmoothingType = 772 new StyleableObjectProperty<FontSmoothingType> 773 (FontSmoothingType.GRAY) { 774 @Override public Object getBean() { return Text.this; } 775 @Override public String getName() { return "fontSmoothingType"; } 776 @Override public CssMetaData<Text,FontSmoothingType> getCssMetaData() { 777 return StyleableProperties.FONT_SMOOTHING_TYPE; 778 } 779 @Override public void invalidated() { 780 impl_markDirty(DirtyBits.TEXT_ATTRS); 781 impl_geomChanged(); 782 } 783 }; 784 } 785 return fontSmoothingType; 786 } 787 788 /** 789 * Defines how the picking computation is done for this text node when 790 * triggered by a {@code MouseEvent} or a {@code contains} function call. 791 * 792 * If {@code pickOnBounds} is true, then picking is computed by 793 * intersecting with the bounds of this text node, else picking is computed 794 * by intersecting with the individual characters (geometric shape) of this 795 * text node. 796 * Picking based on bounds is more efficient and allows the spaces within 797 * and between characters to be picked. 798 * 799 * @defaultValue true 800 * @since JavaFX 1.3 801 */ 802 //@GenerateProperty private boolean pickOnBounds = true; 803 804 // private API to enable cursor and selection for text editing control 805 806 /** 807 * @treatAsPrivate implementation detail 808 * @deprecated This is an internal API that is not intended 809 * for use and will be removed in the next version 810 */ 811 @Deprecated 812 @Override 813 protected final void impl_geomChanged() { 814 super.impl_geomChanged(); 815 if (attributes != null) { 816 if (attributes.impl_caretBinding != null) { 817 attributes.impl_caretBinding.invalidate(); 818 } 819 if (attributes.impl_selectionBinding != null) { 820 attributes.impl_selectionBinding.invalidate(); 821 } 822 } 823 impl_markDirty(DirtyBits.NODE_GEOMETRY); 824 } 825 826 /** 827 * @treatAsPrivate implementation detail 828 * @deprecated This is an internal API that is not intended 829 * for use and will be removed in the next version 830 */ 831 @Deprecated 832 public final PathElement[] getImpl_selectionShape() { 833 return impl_selectionShapeProperty().get(); 834 } 835 836 /** 837 * Shape of selection in local coordinates. 838 * 839 * @treatAsPrivate implementation detail 840 * @deprecated This is an internal API that is not intended 841 * for use and will be removed in the next version 842 */ 843 @Deprecated 844 public final ReadOnlyObjectProperty<PathElement[]> impl_selectionShapeProperty() { 845 return getTextAttribute().impl_selectionShapeProperty(); 846 } 847 848 /** 849 * @treatAsPrivate implementation detail 850 * @deprecated This is an internal API that is not intended 851 * for use and will be removed in the next version 852 */ 853 @Deprecated 854 public final void setImpl_selectionStart(int value) { 855 if (value == -1 && 856 (attributes == null || attributes.impl_selectionStart == null)) { 857 return; 858 } 859 impl_selectionStartProperty().set(value); 860 } 861 862 /** 863 * @treatAsPrivate implementation detail 864 * @deprecated This is an internal API that is not intended 865 * for use and will be removed in the next version 866 */ 867 @Deprecated 868 public final int getImpl_selectionStart() { 869 if (attributes == null || attributes.impl_selectionStart == null) { 870 return DEFAULT_SELECTION_START; 871 } 872 return attributes.getImpl_selectionStart(); 873 } 874 875 /** 876 * Selection start index in the content. 877 * set to {@code -1} to unset selection. 878 * 879 * @treatAsPrivate implementation detail 880 * @deprecated This is an internal API that is not intended 881 * for use and will be removed in the next version 882 */ 883 @Deprecated 884 public final IntegerProperty impl_selectionStartProperty() { 885 return getTextAttribute().impl_selectionStartProperty(); 886 } 887 888 /** 889 * @treatAsPrivate implementation detail 890 * @deprecated This is an internal API that is not intended 891 * for use and will be removed in the next version 892 */ 893 @Deprecated 894 public final void setImpl_selectionEnd(int value) { 895 if (value == -1 && 896 (attributes == null || attributes.impl_selectionEnd == null)) { 897 return; 898 } 899 impl_selectionEndProperty().set(value); 900 } 901 902 /** 903 * @treatAsPrivate implementation detail 904 * @deprecated This is an internal API that is not intended 905 * for use and will be removed in the next version 906 */ 907 @Deprecated 908 public final int getImpl_selectionEnd() { 909 if (attributes == null || attributes.impl_selectionEnd == null) { 910 return DEFAULT_SELECTION_END; 911 } 912 return attributes.getImpl_selectionEnd(); 913 } 914 915 /** 916 * Selection end index in the content. 917 * set to {@code -1} to unset selection. 918 * 919 * @treatAsPrivate implementation detail 920 * @deprecated This is an internal API that is not intended 921 * for use and will be removed in the next version 922 */ 923 @Deprecated 924 public final IntegerProperty impl_selectionEndProperty() { 925 return getTextAttribute().impl_selectionEndProperty(); 926 } 927 928 /** 929 * @treatAsPrivate implementation detail 930 * @deprecated This is an internal API that is not intended 931 * for use and will be removed in the next version 932 */ 933 @Deprecated 934 public final ObjectProperty<Paint> impl_selectionFillProperty() { 935 return getTextAttribute().impl_selectionFillProperty(); 936 } 937 938 /** 939 * @treatAsPrivate implementation detail 940 * @deprecated This is an internal API that is not intended 941 * for use and will be removed in the next version 942 */ 943 @Deprecated 944 public final PathElement[] getImpl_caretShape() { 945 return impl_caretShapeProperty().get(); 946 } 947 948 /** 949 * Shape of caret in local coordinates. 950 * 951 * @treatAsPrivate implementation detail 952 * @deprecated This is an internal API that is not intended 953 * for use and will be removed in the next version 954 */ 955 @Deprecated 956 public final ReadOnlyObjectProperty<PathElement[]> impl_caretShapeProperty() { 957 return getTextAttribute().impl_caretShapeProperty(); 958 } 959 960 /** 961 * @treatAsPrivate implementation detail 962 * @deprecated This is an internal API that is not intended 963 * for use and will be removed in the next version 964 */ 965 @Deprecated 966 public final void setImpl_caretPosition(int value) { 967 if (value == -1 && 968 (attributes == null || attributes.impl_caretPosition == null)) { 969 return; 970 } 971 impl_caretPositionProperty().set(value); 972 } 973 974 /** 975 * @treatAsPrivate implementation detail 976 * @deprecated This is an internal API that is not intended 977 * for use and will be removed in the next version 978 */ 979 @Deprecated 980 public final int getImpl_caretPosition() { 981 if (attributes == null || attributes.impl_caretPosition == null) { 982 return DEFAULT_CARET_POSITION; 983 } 984 return attributes.getImpl_caretPosition(); 985 } 986 987 /** 988 * caret index in the content. 989 * set to {@code -1} to unset caret. 990 * 991 * @treatAsPrivate implementation detail 992 * @deprecated This is an internal API that is not intended 993 * for use and will be removed in the next version 994 */ 995 @Deprecated 996 public final IntegerProperty impl_caretPositionProperty() { 997 return getTextAttribute().impl_caretPositionProperty(); 998 } 999 1000 /** 1001 * @treatAsPrivate implementation detail 1002 * @deprecated This is an internal API that is not intended 1003 * for use and will be removed in the next version 1004 */ 1005 @Deprecated 1006 public final void setImpl_caretBias(boolean value) { 1007 if (value && (attributes == null || attributes.impl_caretBias == null)) { 1008 return; 1009 } 1010 impl_caretBiasProperty().set(value); 1011 } 1012 1013 /** 1014 * @treatAsPrivate implementation detail 1015 * @deprecated This is an internal API that is not intended 1016 * for use and will be removed in the next version 1017 */ 1018 @Deprecated 1019 public final boolean isImpl_caretBias() { 1020 if (attributes == null || attributes.impl_caretBias == null) { 1021 return DEFAULT_CARET_BIAS; 1022 } 1023 return getTextAttribute().isImpl_caretBias(); 1024 } 1025 1026 /** 1027 * caret bias in the content. true means a bias towards forward character 1028 * (true=leading/false=trailing) 1029 * 1030 * @treatAsPrivate implementation detail 1031 * @deprecated This is an internal API that is not intended 1032 * for use and will be removed in the next version 1033 */ 1034 @Deprecated 1035 public final BooleanProperty impl_caretBiasProperty() { 1036 return getTextAttribute().impl_caretBiasProperty(); 1037 } 1038 1039 /** 1040 * Maps local point to index in the content. 1041 * 1042 * @treatAsPrivate implementation detail 1043 * @deprecated This is an internal API that is not intended 1044 * for use and will be removed in the next version 1045 */ 1046 @Deprecated 1047 public final HitInfo impl_hitTestChar(Point2D point) { 1048 if (point == null) return null; 1049 TextLayout layout = getTextLayout(); 1050 double x = point.getX() - getX(); 1051 double y = point.getY() - getY() + getYRendering(); 1052 return layout.getHitInfo((float)x, (float)y); 1053 } 1054 1055 private PathElement[] getRange(int start, int end, int type) { 1056 int length = getTextInternal().length(); 1057 if (0 <= start && start < end && end <= length) { 1058 TextLayout layout = getTextLayout(); 1059 float x = (float)getX(); 1060 float y = (float)getY() - getYRendering(); 1061 return layout.getRange(start, end, type, x, y); 1062 } 1063 return EMPTY_PATH_ELEMENT_ARRAY; 1064 } 1065 1066 /** 1067 * Returns shape for the range of the text in local coordinates. 1068 * 1069 * @treatAsPrivate implementation detail 1070 * @deprecated This is an internal API that is not intended 1071 * for use and will be removed in the next version 1072 */ 1073 @Deprecated 1074 public final PathElement[] impl_getRangeShape(int start, int end) { 1075 return getRange(start, end, TextLayout.TYPE_TEXT); 1076 } 1077 1078 /** 1079 * Returns shape for the underline in local coordinates. 1080 * 1081 * @treatAsPrivate implementation detail 1082 * @deprecated This is an internal API that is not intended 1083 * for use and will be removed in the next version 1084 */ 1085 @Deprecated 1086 public final PathElement[] impl_getUnderlineShape(int start, int end) { 1087 return getRange(start, end, TextLayout.TYPE_UNDERLINE); 1088 } 1089 1090 /** 1091 * Shows/Hides on-screen keyboard if available (mobile platform) 1092 * 1093 * @treatAsPrivate implementation detail 1094 * @deprecated This is an internal API that is not intended 1095 * for use and will be removed in the next version 1096 */ 1097 @Deprecated 1098 public final void impl_displaySoftwareKeyboard(boolean display) { 1099 } 1100 1101 private float getYAdjustment(BaseBounds bounds) { 1102 VPos origin = getTextOrigin(); 1103 if (origin == null) origin = DEFAULT_TEXT_ORIGIN; 1104 switch (origin) { 1105 case TOP: return -bounds.getMinY(); 1106 case BASELINE: return 0; 1107 case CENTER: return -bounds.getMinY() - bounds.getHeight() / 2; 1108 case BOTTOM: return -bounds.getMinY() - bounds.getHeight(); 1109 default: return 0; 1110 } 1111 } 1112 1113 private float getYRendering() { 1114 /* Always logical for rendering */ 1115 BaseBounds bounds = getLogicalBounds(); 1116 1117 VPos origin = getTextOrigin(); 1118 if (origin == null) origin = DEFAULT_TEXT_ORIGIN; 1119 if (getBoundsType() == TextBoundsType.VISUAL) { 1120 BaseBounds vBounds = getVisualBounds(); 1121 float delta = vBounds.getMinY() - bounds.getMinY(); 1122 switch (origin) { 1123 case TOP: return delta; 1124 case BASELINE: return -vBounds.getMinY() + delta; 1125 case CENTER: return vBounds.getHeight() / 2 + delta; 1126 case BOTTOM: return vBounds.getHeight() + delta; 1127 default: return 0; 1128 } 1129 } else { 1130 switch (origin) { 1131 case TOP: return 0; 1132 case BASELINE: return -bounds.getMinY(); 1133 case CENTER: return bounds.getHeight() / 2; 1134 case BOTTOM: return bounds.getHeight(); 1135 default: return 0; 1136 } 1137 } 1138 } 1139 1140 /** 1141 * @treatAsPrivate implementation detail 1142 * @deprecated This is an internal API that is not intended 1143 * for use and will be removed in the next version 1144 */ 1145 @Deprecated 1146 @Override 1147 protected final Bounds impl_computeLayoutBounds() { 1148 if (isSpan()) { 1149 BaseBounds bounds = getSpanBounds(); 1150 double width = bounds.getWidth(); 1151 double height = bounds.getHeight(); 1152 return new BoundingBox(0, 0, width, height); 1153 } 1154 1155 if (getBoundsType() == TextBoundsType.VISUAL) { 1156 /* In Node the layout bounds is computed based in the geom 1157 * bounds and in Shape the geom bounds is computed based 1158 * on the shape (generated here in #configShape()) */ 1159 return super.impl_computeLayoutBounds(); 1160 } 1161 BaseBounds bounds = getLogicalBounds(); 1162 double x = bounds.getMinX() + getX(); 1163 double y = bounds.getMinY() + getY() + getYAdjustment(bounds); 1164 double width = bounds.getWidth(); 1165 double height = bounds.getHeight(); 1166 double wrappingWidth = getWrappingWidth(); 1167 if (wrappingWidth != 0) width = wrappingWidth; 1168 return new BoundingBox(x, y, width, height); 1169 } 1170 1171 /** 1172 * @treatAsPrivate implementation detail 1173 * @deprecated This is an internal API that is not intended 1174 * for use and will be removed in the next version 1175 */ 1176 @Deprecated 1177 @Override 1178 public final BaseBounds impl_computeGeomBounds(BaseBounds bounds, 1179 BaseTransform tx) { 1180 if (isSpan()) { 1181 if (impl_mode != Mode.FILL && getStrokeType() != StrokeType.INSIDE) { 1182 return super.impl_computeGeomBounds(bounds, tx); 1183 } 1184 TextLayout layout = getTextLayout(); 1185 bounds = layout.getBounds(getTextSpan(), bounds); 1186 BaseBounds spanBounds = getSpanBounds(); 1187 float minX = bounds.getMinX() - spanBounds.getMinX(); 1188 float minY = bounds.getMinY() - spanBounds.getMinY(); 1189 float maxX = minX + bounds.getWidth(); 1190 float maxY = minY + bounds.getHeight(); 1191 bounds = bounds.deriveWithNewBounds(minX, minY, 0, maxX, maxY, 0); 1192 return tx.transform(bounds, bounds); 1193 } 1194 1195 if (getBoundsType() == TextBoundsType.VISUAL) { 1196 if (getTextInternal().length() == 0 || impl_mode == Mode.EMPTY) { 1197 return bounds.makeEmpty(); 1198 } 1199 1200 /* Let the super class compute the bounds using shape */ 1201 return super.impl_computeGeomBounds(bounds, tx); 1202 } 1203 1204 BaseBounds textBounds = getLogicalBounds(); 1205 float x = textBounds.getMinX() + (float)getX(); 1206 float yadj = getYAdjustment(textBounds); 1207 float y = textBounds.getMinY() + yadj + (float)getY(); 1208 float width = textBounds.getWidth(); 1209 float height = textBounds.getHeight(); 1210 float wrappingWidth = (float)getWrappingWidth(); 1211 if (wrappingWidth > width) { 1212 width = wrappingWidth; 1213 } else { 1214 /* The following adjustment is necessary for the text bounds to be 1215 * relative to the same location as the mirrored bounds returned 1216 * by layout.getBounds(). 1217 */ 1218 if (wrappingWidth > 0) { 1219 NodeOrientation orientation = getEffectiveNodeOrientation(); 1220 if (orientation == NodeOrientation.RIGHT_TO_LEFT) { 1221 x -= width - wrappingWidth; 1222 } 1223 } 1224 } 1225 textBounds = new RectBounds(x, y, x + width, y + height); 1226 1227 /* handle stroked text */ 1228 if (impl_mode != Mode.FILL && getStrokeType() != StrokeType.INSIDE) { 1229 bounds = 1230 super.impl_computeGeomBounds(bounds, 1231 BaseTransform.IDENTITY_TRANSFORM); 1232 } else { 1233 TextLayout layout = getTextLayout(); 1234 bounds = layout.getBounds(null, bounds); 1235 x = bounds.getMinX() + (float)getX(); 1236 width = bounds.getWidth(); 1237 bounds = bounds.deriveWithNewBounds(x, y, 0, x + width, y + height, 0); 1238 } 1239 1240 bounds = bounds.deriveWithUnion(textBounds); 1241 return tx.transform(bounds, bounds); 1242 } 1243 1244 /** 1245 * @treatAsPrivate implementation detail 1246 * @deprecated This is an internal API that is not intended 1247 * for use and will be removed in the next version 1248 */ 1249 @Deprecated 1250 @Override 1251 protected final boolean impl_computeContains(double localX, double localY) { 1252 //TODO Presently only support bounds based picking. 1253 return true; 1254 } 1255 1256 /** 1257 * @treatAsPrivate implementation detail 1258 * @deprecated This is an internal API that is not intended 1259 * for use and will be removed in the next version 1260 */ 1261 @Deprecated 1262 @Override 1263 public final com.sun.javafx.geom.Shape impl_configShape() { 1264 if (impl_mode == Mode.EMPTY || getTextInternal().length() == 0) { 1265 return new Path2D(); 1266 } 1267 com.sun.javafx.geom.Shape shape = getShape(); 1268 float x, y; 1269 if (isSpan()) { 1270 BaseBounds bounds = getSpanBounds(); 1271 x = -bounds.getMinX(); 1272 y = -bounds.getMinY(); 1273 } else { 1274 x = (float)getX(); 1275 y = getYAdjustment(getVisualBounds()) + (float)getY(); 1276 } 1277 return TransformedShape.translatedShape(shape, x, y); 1278 } 1279 1280 /*************************************************************************** 1281 * * 1282 * Stylesheet Handling * 1283 * * 1284 **************************************************************************/ 1285 1286 /** 1287 * Super-lazy instantiation pattern from Bill Pugh. 1288 * @treatAsPrivate implementation detail 1289 */ 1290 private static class StyleableProperties { 1291 1292 private static final CssMetaData<Text,Font> FONT = 1293 new FontCssMetaData<Text>("-fx-font", Font.getDefault()) { 1294 1295 @Override 1296 public boolean isSettable(Text node) { 1297 return node.font == null || !node.font.isBound(); 1298 } 1299 1300 @Override 1301 public StyleableProperty<Font> getStyleableProperty(Text node) { 1302 return (StyleableProperty<Font>)node.fontProperty(); 1303 } 1304 }; 1305 1306 private static final CssMetaData<Text,Boolean> UNDERLINE = 1307 new CssMetaData<Text,Boolean>("-fx-underline", 1308 BooleanConverter.getInstance(), Boolean.FALSE) { 1309 1310 @Override 1311 public boolean isSettable(Text node) { 1312 return node.attributes == null || 1313 node.attributes.underline == null || 1314 !node.attributes.underline.isBound(); 1315 } 1316 1317 @Override 1318 public StyleableProperty<Boolean> getStyleableProperty(Text node) { 1319 return (StyleableProperty<Boolean>)node.underlineProperty(); 1320 } 1321 }; 1322 1323 private static final CssMetaData<Text,Boolean> STRIKETHROUGH = 1324 new CssMetaData<Text,Boolean>("-fx-strikethrough", 1325 BooleanConverter.getInstance(), Boolean.FALSE) { 1326 1327 @Override 1328 public boolean isSettable(Text node) { 1329 return node.attributes == null || 1330 node.attributes.strikethrough == null || 1331 !node.attributes.strikethrough.isBound(); 1332 } 1333 1334 @Override 1335 public StyleableProperty<Boolean> getStyleableProperty(Text node) { 1336 return (StyleableProperty<Boolean>)node.strikethroughProperty(); 1337 } 1338 }; 1339 1340 private static final 1341 CssMetaData<Text,TextAlignment> TEXT_ALIGNMENT = 1342 new CssMetaData<Text,TextAlignment>("-fx-text-alignment", 1343 new EnumConverter<TextAlignment>(TextAlignment.class), 1344 TextAlignment.LEFT) { 1345 1346 @Override 1347 public boolean isSettable(Text node) { 1348 return node.attributes == null || 1349 node.attributes.textAlignment == null || 1350 !node.attributes.textAlignment.isBound(); 1351 } 1352 1353 @Override 1354 public StyleableProperty<TextAlignment> getStyleableProperty(Text node) { 1355 return (StyleableProperty<TextAlignment>)node.textAlignmentProperty(); 1356 } 1357 }; 1358 1359 private static final CssMetaData<Text,VPos> TEXT_ORIGIN = 1360 new CssMetaData<Text,VPos>("-fx-text-origin", 1361 new EnumConverter<VPos>(VPos.class), 1362 VPos.BASELINE) { 1363 1364 @Override 1365 public boolean isSettable(Text node) { 1366 return node.attributes == null || 1367 node.attributes.textOrigin == null || 1368 !node.attributes.textOrigin.isBound(); 1369 } 1370 1371 @Override 1372 public StyleableProperty<VPos> getStyleableProperty(Text node) { 1373 return (StyleableProperty<VPos>)node.textOriginProperty(); 1374 } 1375 }; 1376 1377 private static final CssMetaData<Text,FontSmoothingType> 1378 FONT_SMOOTHING_TYPE = 1379 new CssMetaData<Text,FontSmoothingType>( 1380 "-fx-font-smoothing-type", 1381 new EnumConverter<FontSmoothingType>(FontSmoothingType.class), 1382 FontSmoothingType.GRAY) { 1383 1384 @Override 1385 public boolean isSettable(Text node) { 1386 return node.fontSmoothingType == null || 1387 !node.fontSmoothingType.isBound(); 1388 } 1389 1390 @Override 1391 public StyleableProperty<FontSmoothingType> 1392 getStyleableProperty(Text node) { 1393 1394 return (StyleableProperty<FontSmoothingType>)node.fontSmoothingTypeProperty(); 1395 } 1396 }; 1397 1398 private static final 1399 CssMetaData<Text,Number> LINE_SPACING = 1400 new CssMetaData<Text,Number>("-fx-line-spacing", 1401 SizeConverter.getInstance(), 0) { 1402 1403 @Override 1404 public boolean isSettable(Text node) { 1405 return node.attributes == null || 1406 node.attributes.lineSpacing == null || 1407 !node.attributes.lineSpacing.isBound(); 1408 } 1409 1410 @Override 1411 public StyleableProperty<Number> getStyleableProperty(Text node) { 1412 return (StyleableProperty<Number>)node.lineSpacingProperty(); 1413 } 1414 }; 1415 1416 private static final CssMetaData<Text, TextBoundsType> 1417 BOUNDS_TYPE = 1418 new CssMetaData<Text,TextBoundsType>( 1419 "-fx-bounds-type", 1420 new EnumConverter<TextBoundsType>(TextBoundsType.class), 1421 DEFAULT_BOUNDS_TYPE) { 1422 1423 @Override 1424 public boolean isSettable(Text node) { 1425 return node.boundsType == null || !node.boundsType.isBound(); 1426 } 1427 1428 @Override 1429 public StyleableProperty<TextBoundsType> getStyleableProperty(Text node) { 1430 return (StyleableProperty<TextBoundsType>)node.boundsTypeProperty(); 1431 } 1432 }; 1433 1434 private final static List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 1435 static { 1436 final List<CssMetaData<? extends Styleable, ?>> styleables = 1437 new ArrayList<CssMetaData<? extends Styleable, ?>>(Shape.getClassCssMetaData()); 1438 styleables.add(FONT); 1439 styleables.add(UNDERLINE); 1440 styleables.add(STRIKETHROUGH); 1441 styleables.add(TEXT_ALIGNMENT); 1442 styleables.add(TEXT_ORIGIN); 1443 styleables.add(FONT_SMOOTHING_TYPE); 1444 styleables.add(LINE_SPACING); 1445 styleables.add(BOUNDS_TYPE); 1446 STYLEABLES = Collections.unmodifiableList(styleables); 1447 } 1448 } 1449 1450 /** 1451 * @return The CssMetaData associated with this class, which may include the 1452 * CssMetaData of its super classes. 1453 */ 1454 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 1455 return StyleableProperties.STYLEABLES; 1456 } 1457 1458 /** 1459 * {@inheritDoc} 1460 * 1461 */ 1462 1463 1464 @Override 1465 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 1466 return getClassCssMetaData(); 1467 } 1468 1469 @SuppressWarnings("deprecation") 1470 private void updatePGText() { 1471 PGText peer = getPGText(); 1472 if (impl_isDirty(DirtyBits.TEXT_ATTRS)) { 1473 peer.setUnderline(isUnderline()); 1474 peer.setStrikethrough(isStrikethrough()); 1475 FontSmoothingType smoothing = getFontSmoothingType(); 1476 if (smoothing == null) smoothing = FontSmoothingType.GRAY; 1477 peer.setFontSmoothingType(smoothing.ordinal()); 1478 } 1479 if (impl_isDirty(DirtyBits.TEXT_FONT)) { 1480 peer.setFont(getFontInternal()); 1481 } 1482 if (impl_isDirty(DirtyBits.NODE_CONTENTS)) { 1483 peer.setGlyphs(getRuns()); 1484 } 1485 if (impl_isDirty(DirtyBits.NODE_GEOMETRY)) { 1486 if (isSpan()) { 1487 BaseBounds spanBounds = getSpanBounds(); 1488 peer.setLayoutLocation(spanBounds.getMinX(), spanBounds.getMinY()); 1489 } else { 1490 float x = (float)getX(); 1491 float y = (float)getY(); 1492 float yadj = getYRendering(); 1493 peer.setLayoutLocation(-x, yadj - y); 1494 } 1495 } 1496 if (impl_isDirty(DirtyBits.TEXT_SELECTION)) { 1497 Object fillObj = null; 1498 int start = getImpl_selectionStart(); 1499 int end = getImpl_selectionEnd(); 1500 int length = getTextInternal().length(); 1501 if (0 <= start && start < end && end <= length) { 1502 Paint fill = impl_selectionFillProperty().get(); 1503 fillObj = fill != null ? Toolkit.getPaintAccessor().getPlatformPaint(fill) : null; 1504 } 1505 peer.setSelection(start, end, fillObj); 1506 } 1507 } 1508 1509 /** 1510 * @treatAsPrivate implementation detail 1511 * @deprecated This is an internal API that is not intended 1512 * for use and will be removed in the next version 1513 */ 1514 @Deprecated 1515 @Override 1516 public final void impl_updatePG() { 1517 super.impl_updatePG(); 1518 updatePGText(); 1519 } 1520 1521 private AccessibleNode accText ; 1522 /** 1523 * @treatAsPrivate implementation detail 1524 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 1525 */ 1526 @Deprecated public AccessibleProvider impl_getAccessible() { 1527 if( accText == null) 1528 accText = new AccessibleText(this); 1529 return (AccessibleProvider)accText ; 1530 } 1531 1532 /*************************************************************************** 1533 * * 1534 * Seldom Used Properties * 1535 * * 1536 **************************************************************************/ 1537 1538 private TextAttribute attributes; 1539 1540 private TextAttribute getTextAttribute() { 1541 if (attributes == null) { 1542 attributes = new TextAttribute(); 1543 } 1544 return attributes; 1545 } 1546 1547 private static final VPos DEFAULT_TEXT_ORIGIN = VPos.BASELINE; 1548 private static final TextBoundsType DEFAULT_BOUNDS_TYPE = TextBoundsType.LOGICAL; 1549 private static final boolean DEFAULT_UNDERLINE = false; 1550 private static final boolean DEFAULT_STRIKETHROUGH = false; 1551 private static final TextAlignment DEFAULT_TEXT_ALIGNMENT = TextAlignment.LEFT; 1552 private static final double DEFAULT_LINE_SPACING = 0; 1553 private static final int DEFAULT_CARET_POSITION = -1; 1554 private static final int DEFAULT_SELECTION_START = -1; 1555 private static final int DEFAULT_SELECTION_END = -1; 1556 private static final Color DEFAULT_SELECTION_FILL= Color.WHITE; 1557 private static final boolean DEFAULT_CARET_BIAS = true; 1558 1559 private final class TextAttribute { 1560 1561 private ObjectProperty<VPos> textOrigin; 1562 1563 public final VPos getTextOrigin() { 1564 return textOrigin == null ? DEFAULT_TEXT_ORIGIN : textOrigin.get(); 1565 } 1566 1567 public final ObjectProperty<VPos> textOriginProperty() { 1568 if (textOrigin == null) { 1569 textOrigin = new StyleableObjectProperty<VPos>(DEFAULT_TEXT_ORIGIN) { 1570 @Override public Object getBean() { return Text.this; } 1571 @Override public String getName() { return "textOrigin"; } 1572 @Override public CssMetaData getCssMetaData() { 1573 return StyleableProperties.TEXT_ORIGIN; 1574 } 1575 @Override public void invalidated() { 1576 impl_geomChanged(); 1577 } 1578 }; 1579 } 1580 return textOrigin; 1581 } 1582 1583 private BooleanProperty underline; 1584 1585 public final boolean isUnderline() { 1586 return underline == null ? DEFAULT_UNDERLINE : underline.get(); 1587 } 1588 1589 public final BooleanProperty underlineProperty() { 1590 if (underline == null) { 1591 underline = new StyleableBooleanProperty() { 1592 @Override public Object getBean() { return Text.this; } 1593 @Override public String getName() { return "underline"; } 1594 @Override public CssMetaData getCssMetaData() { 1595 return StyleableProperties.UNDERLINE; 1596 } 1597 @Override public void invalidated() { 1598 impl_markDirty(DirtyBits.TEXT_ATTRS); 1599 } 1600 }; 1601 } 1602 return underline; 1603 } 1604 1605 private BooleanProperty strikethrough; 1606 1607 public final boolean isStrikethrough() { 1608 return strikethrough == null ? DEFAULT_STRIKETHROUGH : strikethrough.get(); 1609 } 1610 1611 public final BooleanProperty strikethroughProperty() { 1612 if (strikethrough == null) { 1613 strikethrough = new StyleableBooleanProperty() { 1614 @Override public Object getBean() { return Text.this; } 1615 @Override public String getName() { return "strikethrough"; } 1616 @Override public CssMetaData getCssMetaData() { 1617 return StyleableProperties.STRIKETHROUGH; 1618 } 1619 @Override public void invalidated() { 1620 impl_markDirty(DirtyBits.TEXT_ATTRS); 1621 } 1622 }; 1623 } 1624 return strikethrough; 1625 } 1626 1627 private ObjectProperty<TextAlignment> textAlignment; 1628 1629 public final TextAlignment getTextAlignment() { 1630 return textAlignment == null ? DEFAULT_TEXT_ALIGNMENT : textAlignment.get(); 1631 } 1632 1633 public final ObjectProperty<TextAlignment> textAlignmentProperty() { 1634 if (textAlignment == null) { 1635 textAlignment = 1636 new StyleableObjectProperty<TextAlignment>(DEFAULT_TEXT_ALIGNMENT) { 1637 @Override public Object getBean() { return Text.this; } 1638 @Override public String getName() { return "textAlignment"; } 1639 @Override public CssMetaData getCssMetaData() { 1640 return StyleableProperties.TEXT_ALIGNMENT; 1641 } 1642 @Override public void invalidated() { 1643 if (!isSpan()) { 1644 TextAlignment alignment = get(); 1645 if (alignment == null) { 1646 alignment = DEFAULT_TEXT_ALIGNMENT; 1647 } 1648 TextLayout layout = getTextLayout(); 1649 if (layout.setAlignment(alignment.ordinal())) { 1650 needsTextLayout(); 1651 } 1652 } 1653 } 1654 }; 1655 } 1656 return textAlignment; 1657 } 1658 1659 private DoubleProperty lineSpacing; 1660 1661 public final double getLineSpacing() { 1662 return lineSpacing == null ? DEFAULT_LINE_SPACING : lineSpacing.get(); 1663 } 1664 1665 public final DoubleProperty lineSpacingProperty() { 1666 if (lineSpacing == null) { 1667 lineSpacing = 1668 new StyleableDoubleProperty(DEFAULT_LINE_SPACING) { 1669 @Override public Object getBean() { return Text.this; } 1670 @Override public String getName() { return "lineSpacing"; } 1671 @Override public CssMetaData getCssMetaData() { 1672 return StyleableProperties.LINE_SPACING; 1673 } 1674 @Override public void invalidated() { 1675 if (!isSpan()) { 1676 TextLayout layout = getTextLayout(); 1677 if (layout.setLineSpacing((float)get())) { 1678 needsTextLayout(); 1679 } 1680 } 1681 } 1682 }; 1683 } 1684 return lineSpacing; 1685 } 1686 1687 private ReadOnlyDoubleWrapper baselineOffset; 1688 1689 public final ReadOnlyDoubleProperty baselineOffsetProperty() { 1690 if (baselineOffset == null) { 1691 baselineOffset = new ReadOnlyDoubleWrapper(Text.this, "baselineOffset") { 1692 {bind(new DoubleBinding() { 1693 {bind(fontProperty());} 1694 @Override protected double computeValue() { 1695 /* This method should never be used for spans. 1696 * If it is, it will still returns the ascent 1697 * for the first line in the layout */ 1698 BaseBounds bounds = getLogicalBounds(); 1699 return -bounds.getMinY(); 1700 } 1701 });} 1702 }; 1703 } 1704 return baselineOffset.getReadOnlyProperty(); 1705 } 1706 1707 @Deprecated 1708 private ObjectProperty<PathElement[]> impl_selectionShape; 1709 private ObjectBinding<PathElement[]> impl_selectionBinding; 1710 1711 @Deprecated 1712 public final ReadOnlyObjectProperty<PathElement[]> impl_selectionShapeProperty() { 1713 if (impl_selectionShape == null) { 1714 impl_selectionBinding = new ObjectBinding<PathElement[]>() { 1715 {bind(impl_selectionStartProperty(), impl_selectionEndProperty());} 1716 @Override protected PathElement[] computeValue() { 1717 int start = getImpl_selectionStart(); 1718 int end = getImpl_selectionEnd(); 1719 return getRange(start, end, TextLayout.TYPE_TEXT); 1720 } 1721 }; 1722 impl_selectionShape = new SimpleObjectProperty<PathElement[]>(Text.this, "impl_selectionShape"); 1723 impl_selectionShape.bind(impl_selectionBinding); 1724 } 1725 return impl_selectionShape; 1726 } 1727 1728 private ObjectProperty<Paint> selectionFill; 1729 1730 @Deprecated 1731 public final ObjectProperty<Paint> impl_selectionFillProperty() { 1732 if (selectionFill == null) { 1733 selectionFill = 1734 new ObjectPropertyBase<Paint>(DEFAULT_SELECTION_FILL) { 1735 @Override public Object getBean() { return Text.this; } 1736 @Override public String getName() { return "impl_selectionFill"; } 1737 @Override protected void invalidated() { 1738 impl_markDirty(DirtyBits.TEXT_SELECTION); 1739 } 1740 }; 1741 } 1742 return selectionFill; 1743 } 1744 1745 @Deprecated 1746 private IntegerProperty impl_selectionStart; 1747 1748 @Deprecated 1749 public final int getImpl_selectionStart() { 1750 return impl_selectionStart == null ? DEFAULT_SELECTION_START : impl_selectionStart.get(); 1751 } 1752 1753 @Deprecated 1754 public final IntegerProperty impl_selectionStartProperty() { 1755 if (impl_selectionStart == null) { 1756 impl_selectionStart = 1757 new IntegerPropertyBase(DEFAULT_SELECTION_START) { 1758 @Override public Object getBean() { return Text.this; } 1759 @Override public String getName() { return "impl_selectionStart"; } 1760 @Override protected void invalidated() { 1761 impl_markDirty(DirtyBits.TEXT_SELECTION); 1762 } 1763 }; 1764 } 1765 return impl_selectionStart; 1766 } 1767 1768 @Deprecated 1769 private IntegerProperty impl_selectionEnd; 1770 1771 @Deprecated 1772 public final int getImpl_selectionEnd() { 1773 return impl_selectionEnd == null ? DEFAULT_SELECTION_END : impl_selectionEnd.get(); 1774 } 1775 1776 @Deprecated 1777 public final IntegerProperty impl_selectionEndProperty() { 1778 if (impl_selectionEnd == null) { 1779 impl_selectionEnd = 1780 new IntegerPropertyBase(DEFAULT_SELECTION_END) { 1781 @Override public Object getBean() { return Text.this; } 1782 @Override public String getName() { return "impl_selectionEnd"; } 1783 @Override protected void invalidated() { 1784 impl_markDirty(DirtyBits.TEXT_SELECTION); 1785 } 1786 }; 1787 } 1788 return impl_selectionEnd; 1789 } 1790 1791 @Deprecated 1792 private ObjectProperty<PathElement[]> impl_caretShape; 1793 private ObjectBinding<PathElement[]> impl_caretBinding; 1794 1795 @Deprecated 1796 public final ReadOnlyObjectProperty<PathElement[]> impl_caretShapeProperty() { 1797 if (impl_caretShape == null) { 1798 impl_caretBinding = new ObjectBinding<PathElement[]>() { 1799 {bind(impl_caretPositionProperty(), impl_caretBiasProperty());} 1800 @Override protected PathElement[] computeValue() { 1801 int pos = getImpl_caretPosition(); 1802 int length = getTextInternal().length(); 1803 if (0 <= pos && pos <= length) { 1804 boolean bias = isImpl_caretBias(); 1805 float x = (float)getX(); 1806 float y = (float)getY() - getYRendering(); 1807 TextLayout layout = getTextLayout(); 1808 return layout.getCaretShape(pos, bias, x, y); 1809 } 1810 return EMPTY_PATH_ELEMENT_ARRAY; 1811 } 1812 }; 1813 impl_caretShape = new SimpleObjectProperty<PathElement[]>(Text.this, "impl_caretShape"); 1814 impl_caretShape.bind(impl_caretBinding); 1815 } 1816 return impl_caretShape; 1817 } 1818 1819 @Deprecated 1820 private IntegerProperty impl_caretPosition; 1821 1822 @Deprecated 1823 public final int getImpl_caretPosition() { 1824 return impl_caretPosition == null ? DEFAULT_CARET_POSITION : impl_caretPosition.get(); 1825 } 1826 1827 @Deprecated 1828 public final IntegerProperty impl_caretPositionProperty() { 1829 if (impl_caretPosition == null) { 1830 impl_caretPosition = 1831 new SimpleIntegerProperty(Text.this, "impl_caretPosition", DEFAULT_CARET_POSITION); 1832 } 1833 return impl_caretPosition; 1834 } 1835 1836 @Deprecated 1837 private BooleanProperty impl_caretBias; 1838 1839 @Deprecated 1840 public final boolean isImpl_caretBias() { 1841 return impl_caretBias == null ? DEFAULT_CARET_BIAS : impl_caretBias.get(); 1842 } 1843 1844 @Deprecated 1845 public final BooleanProperty impl_caretBiasProperty() { 1846 if (impl_caretBias == null) { 1847 impl_caretBias = 1848 new SimpleBooleanProperty(Text.this, "impl_caretBias", DEFAULT_CARET_BIAS); 1849 } 1850 return impl_caretBias; 1851 } 1852 } 1853 1854 /** 1855 * Returns a string representation of this {@code Text} object. 1856 * @return a string representation of this {@code Text} object. 1857 */ 1858 @Override 1859 public String toString() { 1860 final StringBuilder sb = new StringBuilder("Text["); 1861 1862 String id = getId(); 1863 if (id != null) { 1864 sb.append("id=").append(id).append(", "); 1865 } 1866 1867 sb.append("text=\"").append(getText()).append("\""); 1868 sb.append(", x=").append(getX()); 1869 sb.append(", y=").append(getY()); 1870 sb.append(", alignment=").append(getTextAlignment()); 1871 sb.append(", origin=").append(getTextOrigin()); 1872 sb.append(", boundsType=").append(getBoundsType()); 1873 1874 double spacing = getLineSpacing(); 1875 if (spacing != DEFAULT_LINE_SPACING) { 1876 sb.append(", lineSpacing=").append(spacing); 1877 } 1878 1879 double wrap = getWrappingWidth(); 1880 if (wrap != 0) { 1881 sb.append(", wrappingWidth=").append(wrap); 1882 } 1883 1884 sb.append(", font=").append(getFont()); 1885 sb.append(", fontSmoothingType=").append(getFontSmoothingType()); 1886 1887 if (isStrikethrough()) { 1888 sb.append(", strikethrough"); 1889 } 1890 if (isUnderline()) { 1891 sb.append(", underline"); 1892 } 1893 1894 sb.append(", fill=").append(getFill()); 1895 1896 Paint stroke = getStroke(); 1897 if (stroke != null) { 1898 sb.append(", stroke=").append(stroke); 1899 sb.append(", strokeWidth=").append(getStrokeWidth()); 1900 } 1901 1902 return sb.append("]").toString(); 1903 } 1904}