1 /* 2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.control; 27 28 29 import com.sun.javafx.css.StyleManager; 30 import com.sun.javafx.scene.NodeHelper; 31 import javafx.css.converter.BooleanConverter; 32 import javafx.css.converter.EnumConverter; 33 import javafx.css.converter.InsetsConverter; 34 import javafx.css.converter.PaintConverter; 35 import javafx.css.converter.SizeConverter; 36 import javafx.css.converter.StringConverter; 37 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.List; 41 42 import javafx.beans.property.BooleanProperty; 43 import javafx.beans.property.DoubleProperty; 44 import javafx.beans.property.ObjectProperty; 45 import javafx.beans.property.ReadOnlyObjectProperty; 46 import javafx.beans.property.SimpleBooleanProperty; 47 import javafx.beans.property.SimpleStringProperty; 48 import javafx.beans.property.StringProperty; 49 import javafx.beans.value.WritableValue; 50 import javafx.geometry.Insets; 51 import javafx.geometry.Orientation; 52 import javafx.geometry.Pos; 53 import javafx.scene.Node; 54 import javafx.scene.image.Image; 55 import javafx.scene.image.ImageView; 56 import javafx.scene.paint.Color; 57 import javafx.scene.paint.Paint; 58 import javafx.scene.text.Font; 59 import javafx.scene.text.TextAlignment; 60 import javafx.beans.DefaultProperty; 61 import javafx.css.CssMetaData; 62 import javafx.css.FontCssMetaData; 63 import javafx.css.StyleOrigin; 64 import javafx.css.Styleable; 65 import javafx.css.StyleableBooleanProperty; 66 import javafx.css.StyleableDoubleProperty; 67 import javafx.css.StyleableObjectProperty; 68 import javafx.css.StyleableProperty; 69 import javafx.css.StyleableStringProperty; 70 71 72 /** 73 * A Labeled {@link Control} is one which has as part of its user interface 74 * a textual content associated with it. For example, a {@link Button} displays 75 * {@code text}, as does a {@link Label}, a {@link Tooltip}, and many 76 * other controls. 77 * <p> 78 * Labeled is also a convenient base class from which to extend when building 79 * new Controls which, as part of their UI, display read-only textual content. 80 * </p> 81 * 82 * <p>Example of how to place a graphic above the text: 83 * <pre><code> 84 * Image image = new Image(getClass().getResourceAsStream("image.png")); 85 * ImageView imageView = new ImageView(); 86 * imageView.setImage(image); 87 * Label label = new Label("text", imageView); 88 * label.setContentDisplay(ContentDisplay.TOP); 89 * </code></pre> 90 * 91 * @see Button 92 * @see Label 93 * @see ToggleButton 94 * @since JavaFX 2.0 95 */ 96 @DefaultProperty("text") 97 public abstract class Labeled extends Control { 98 99 private final static String DEFAULT_ELLIPSIS_STRING = "..."; 100 101 102 /*************************************************************************** 103 * * 104 * Constructors * 105 * * 106 **************************************************************************/ 107 108 /** 109 * Creates a Label with no text and graphic 110 */ 111 public Labeled() { } 112 113 /** 114 * Creates a Label with text 115 * @param text The text for the label. 116 */ 117 public Labeled(String text) { 118 setText(text); 119 } 120 121 /** 122 * Creates a Label with text and a graphic 123 * @param text The text for the label. 124 * @param graphic The graphic for the label. 125 */ 126 public Labeled(String text, Node graphic) { 127 setText(text); 128 ((StyleableProperty<Node>)(WritableValue<Node>)graphicProperty()).applyStyle(null, graphic); 129 } 130 131 /*************************************************************************** 132 * * 133 * Properties * 134 * * 135 **************************************************************************/ 136 /** 137 * The text to display in the label. The text may be null. 138 * @return the text to display in the label 139 */ 140 public final StringProperty textProperty() { 141 if (text == null) { 142 text = new SimpleStringProperty(this, "text", ""); 143 } 144 return text; 145 } 146 private StringProperty text; 147 public final void setText(String value) { textProperty().setValue(value); } 148 public final String getText() { return text == null ? "" : text.getValue(); } 149 150 /** 151 * Specifies how the text and graphic within the Labeled should be 152 * aligned when there is empty space within the Labeled. 153 * @return the alignment within this labeled 154 */ 155 public final ObjectProperty<Pos> alignmentProperty() { 156 if (alignment == null) { 157 alignment = new StyleableObjectProperty<Pos>(Pos.CENTER_LEFT) { 158 159 @Override public CssMetaData<Labeled,Pos> getCssMetaData() { 160 return StyleableProperties.ALIGNMENT; 161 } 162 163 @Override 164 public Object getBean() { 165 return Labeled.this; 166 } 167 168 @Override 169 public String getName() { 170 return "alignment"; 171 } 172 }; 173 } 174 return alignment; 175 } 176 private ObjectProperty<Pos> alignment; 177 public final void setAlignment(Pos value) { alignmentProperty().set(value); } 178 public final Pos getAlignment() { return alignment == null ? Pos.CENTER_LEFT : alignment.get(); } 179 180 181 /** 182 * Specifies the behavior for lines of text <em>when text is multiline</em> 183 * Unlike {@link #contentDisplayProperty} which affects the graphic and text, this setting 184 * only affects multiple lines of text relative to the text bounds. 185 * @return the alignment of lines of text within this labeled 186 */ 187 public final ObjectProperty<TextAlignment> textAlignmentProperty() { 188 if (textAlignment == null) { 189 textAlignment = new StyleableObjectProperty<TextAlignment>(TextAlignment.LEFT) { 190 191 @Override 192 public CssMetaData<Labeled,TextAlignment> getCssMetaData() { 193 return StyleableProperties.TEXT_ALIGNMENT; 194 } 195 196 @Override 197 public Object getBean() { 198 return Labeled.this; 199 } 200 201 @Override 202 public String getName() { 203 return "textAlignment"; 204 } 205 }; 206 } 207 return textAlignment; 208 } 209 private ObjectProperty<TextAlignment> textAlignment; 210 public final void setTextAlignment(TextAlignment value) { textAlignmentProperty().setValue(value); } 211 public final TextAlignment getTextAlignment() { return textAlignment == null ? TextAlignment.LEFT : textAlignment.getValue(); } 212 213 /** 214 * Specifies the behavior to use if the text of the {@code Labeled} 215 * exceeds the available space for rendering the text. 216 * @return the overrun behavior if the text exceeds the available space 217 */ 218 public final ObjectProperty<OverrunStyle> textOverrunProperty() { 219 if (textOverrun == null) { 220 textOverrun = new StyleableObjectProperty<OverrunStyle>(OverrunStyle.ELLIPSIS) { 221 222 @Override 223 public CssMetaData<Labeled,OverrunStyle> getCssMetaData() { 224 return StyleableProperties.TEXT_OVERRUN; 225 } 226 227 @Override 228 public Object getBean() { 229 return Labeled.this; 230 } 231 232 @Override 233 public String getName() { 234 return "textOverrun"; 235 } 236 }; 237 } 238 return textOverrun; 239 } 240 private ObjectProperty<OverrunStyle> textOverrun; 241 public final void setTextOverrun(OverrunStyle value) { textOverrunProperty().setValue(value); } 242 public final OverrunStyle getTextOverrun() { return textOverrun == null ? OverrunStyle.ELLIPSIS : textOverrun.getValue(); } 243 244 /** 245 * Specifies the string to display for the ellipsis when text is truncated. 246 * 247 * <table summary="" border="0" cellpadding="0" cellspacing="0"><tr><th>Examples</th></tr> 248 * <tr class="altColor"><td align="right">"..."</td> <td>- Default value for most locales</td> 249 * <tr class="rowColor"><td align="right">" . . . "</td> <td></td> 250 * <tr class="altColor"><td align="right">" [...] "</td> <td></td> 251 * <tr class="rowColor"><td align="right">"\u2026"</td> <td>- The Unicode ellipsis character '…'</td> 252 * <tr class="altColor"><td align="right">""</td> <td>- No ellipsis, just display the truncated string</td> 253 * </table> 254 * 255 * <p>Note that not all fonts support all Unicode characters. 256 * 257 * @return the ellipsis property on the string to display for the ellipsis 258 * when text is truncated 259 * @see <a href="http://en.wikipedia.org/wiki/Ellipsis#Computer_representations">Wikipedia:ellipsis</a> 260 * @since JavaFX 2.2 261 */ 262 public final StringProperty ellipsisStringProperty() { 263 if (ellipsisString == null) { 264 ellipsisString = new StyleableStringProperty(DEFAULT_ELLIPSIS_STRING) { 265 @Override public Object getBean() { 266 return Labeled.this; 267 } 268 269 @Override public String getName() { 270 return "ellipsisString"; 271 } 272 273 @Override public CssMetaData<Labeled,String> getCssMetaData() { 274 return StyleableProperties.ELLIPSIS_STRING; 275 } 276 }; 277 } 278 return ellipsisString; 279 } 280 private StringProperty ellipsisString; 281 public final void setEllipsisString(String value) { ellipsisStringProperty().set((value == null) ? "" : value); } 282 public final String getEllipsisString() { return ellipsisString == null ? DEFAULT_ELLIPSIS_STRING : ellipsisString.get(); } 283 284 285 /** 286 * If a run of text exceeds the width of the Labeled, then this variable 287 * indicates whether the text should wrap onto another line. 288 * @return the wrap property if a run of text exceeds the width of the Labeled 289 */ 290 public final BooleanProperty wrapTextProperty() { 291 if (wrapText == null) { 292 wrapText = new StyleableBooleanProperty() { 293 294 @Override 295 public CssMetaData<Labeled,Boolean> getCssMetaData() { 296 return StyleableProperties.WRAP_TEXT; 297 } 298 299 @Override 300 public Object getBean() { 301 return Labeled.this; 302 } 303 304 @Override 305 public String getName() { 306 return "wrapText"; 307 } 308 }; 309 } 310 return wrapText; 311 } 312 private BooleanProperty wrapText; 313 public final void setWrapText(boolean value) { wrapTextProperty().setValue(value); } 314 public final boolean isWrapText() { return wrapText == null ? false : wrapText.getValue(); } 315 316 /** 317 * If wrapText is true, then contentBias will be HORIZONTAL, otherwise it is null. 318 * @return orientation of width/height dependency or null if there is none 319 */ 320 @Override public Orientation getContentBias() { 321 return isWrapText()? Orientation.HORIZONTAL : null; 322 } 323 324 /** 325 * The default font to use for text in the Labeled. If the Label's text is 326 * rich text then this font may or may not be used depending on the font 327 * information embedded in the rich text, but in any case where a default 328 * font is required, this font will be used. 329 * @return the default font to use for text in this labeled 330 */ 331 public final ObjectProperty<Font> fontProperty() { 332 333 if (font == null) { 334 font = new StyleableObjectProperty<Font>(Font.getDefault()) { 335 336 private boolean fontSetByCss = false; 337 338 @Override 339 public void applyStyle(StyleOrigin newOrigin, Font value) { 340 341 // 342 // RT-20727 - if CSS is setting the font, then make sure invalidate doesn't call NodeHelper.reapplyCSS 343 // 344 try { 345 // super.applyStyle calls set which might throw if value is bound. 346 // Have to make sure fontSetByCss is reset. 347 fontSetByCss = true; 348 super.applyStyle(newOrigin, value); 349 } catch(Exception e) { 350 throw e; 351 } finally { 352 fontSetByCss = false; 353 } 354 } 355 356 @Override 357 public void set(Font value) { 358 359 final Font oldValue = get(); 360 if (value != null ? !value.equals(oldValue) : oldValue != null) { 361 super.set(value); 362 } 363 364 } 365 366 @Override 367 protected void invalidated() { 368 // RT-20727 - if font is changed by calling setFont, then 369 // css might need to be reapplied since font size affects 370 // calculated values for styles with relative values 371 if(fontSetByCss == false) { 372 NodeHelper.reapplyCSS(Labeled.this); 373 } 374 } 375 376 @Override 377 public CssMetaData<Labeled,Font> getCssMetaData() { 378 return StyleableProperties.FONT; 379 } 380 381 @Override 382 public Object getBean() { 383 return Labeled.this; 384 } 385 386 @Override 387 public String getName() { 388 return "font"; 389 } 390 }; 391 } 392 return font; 393 } 394 private ObjectProperty<Font> font; 395 public final void setFont(Font value) { fontProperty().setValue(value); } 396 public final Font getFont() { return font == null ? Font.getDefault() : font.getValue(); } 397 398 399 /** 400 * An optional icon for the Labeled. This can be positioned relative to the 401 * text by using {@link #setContentDisplay}. The node specified for this 402 * variable cannot appear elsewhere in the scene graph, otherwise 403 * the {@code IllegalArgumentException} is thrown. See the class 404 * description of {@link javafx.scene.Node Node} for more detail. 405 * @return the optional icon for this labeled 406 */ 407 public final ObjectProperty<Node> graphicProperty() { 408 if (graphic == null) { 409 graphic = new StyleableObjectProperty<Node>() { 410 411 // The graphic is styleable by css, but it is the 412 // imageUrlProperty that handles the style value. 413 @Override 414 public CssMetaData getCssMetaData() { 415 return StyleableProperties.GRAPHIC; 416 } 417 418 @Override 419 public Object getBean() { 420 return Labeled.this; 421 } 422 423 @Override 424 public String getName() { 425 return "graphic"; 426 } 427 }; 428 } 429 return graphic; 430 } 431 private ObjectProperty<Node> graphic; 432 public final void setGraphic(Node value) { 433 graphicProperty().setValue(value); 434 } 435 public final Node getGraphic() { return graphic == null ? null : graphic.getValue(); } 436 437 private StyleableStringProperty imageUrl = null; 438 /** 439 * The imageUrl property is set from CSS and then the graphic property is 440 * set from the invalidated method. This ensures that the same image isn't 441 * reloaded. 442 */ 443 private StyleableStringProperty imageUrlProperty() { 444 if (imageUrl == null) { 445 imageUrl = new StyleableStringProperty() { 446 447 // 448 // If imageUrlProperty is invalidated, this is the origin of the style that 449 // triggered the invalidation. This is used in the invaildated() method where the 450 // value of super.getStyleOrigin() is not valid until after the call to set(v) returns, 451 // by which time invalidated will have been called. 452 // This value is initialized to USER in case someone calls set on the imageUrlProperty, which 453 // is possible: 454 // CssMetaData metaData = ((StyleableProperty)labeled.graphicProperty()).getCssMetaData(); 455 // StyleableProperty prop = metaData.getStyleableProperty(labeled); 456 // prop.set(someUrl); 457 // 458 // TODO: Note that prop != labeled, which violates the contract between StyleableProperty and CssMetaData. 459 // 460 StyleOrigin origin = StyleOrigin.USER; 461 462 @Override 463 public void applyStyle(StyleOrigin origin, String v) { 464 465 this.origin = origin; 466 467 // Don't want applyStyle to throw an exception which would leave this.origin set to the wrong value 468 if (graphic == null || graphic.isBound() == false) super.applyStyle(origin, v); 469 470 // Origin is only valid for this invocation of applyStyle, so reset it to USER in case someone calls set. 471 this.origin = StyleOrigin.USER; 472 } 473 474 @Override 475 protected void invalidated() { 476 477 // need to call super.get() here since get() is overridden to return the graphicProperty's value 478 final String url = super.get(); 479 480 if (url == null) { 481 ((StyleableProperty<Node>)(WritableValue<Node>)graphicProperty()).applyStyle(origin, null); 482 } else { 483 // RT-34466 - if graphic's url is the same as this property's value, then don't overwrite. 484 final Node graphicNode = Labeled.this.getGraphic(); 485 if (graphicNode instanceof ImageView) { 486 final ImageView imageView = (ImageView)graphicNode; 487 final Image image = imageView.getImage(); 488 if (image != null) { 489 final String imageViewUrl = image.getUrl(); 490 if (url.equals(imageViewUrl)) return; 491 } 492 493 } 494 495 final Image img = StyleManager.getInstance().getCachedImage(url); 496 497 if (img != null) { 498 // 499 // Note that it is tempting to try to re-use existing ImageView simply by setting 500 // the image on the current ImageView, if there is one. This would effectively change 501 // the image, but not the ImageView which means that no graphicProperty listeners would 502 // be notified. This is probably not what we want. 503 // 504 505 // 506 // Have to call applyStyle on graphicProperty so that the graphicProperty's 507 // origin matches the imageUrlProperty's origin. 508 // 509 ((StyleableProperty<Node>)(WritableValue<Node>)graphicProperty()).applyStyle(origin, new ImageView(img)); 510 } 511 } 512 } 513 514 @Override 515 public String get() { 516 517 // 518 // The value of the imageUrlProperty is that of the graphicProperty. 519 // Return the value in a way that doesn't expand the graphicProperty. 520 // 521 final Node graphic = getGraphic(); 522 if (graphic instanceof ImageView) { 523 final Image image = ((ImageView)graphic).getImage(); 524 if (image != null) { 525 return image.getUrl(); 526 } 527 } 528 return null; 529 } 530 531 @Override 532 public StyleOrigin getStyleOrigin() { 533 534 // 535 // The origin of the imageUrlProperty is that of the graphicProperty. 536 // Return the origin in a way that doesn't expand the graphicProperty. 537 // 538 return graphic != null ? ((StyleableProperty<Node>)(WritableValue<Node>)graphic).getStyleOrigin() : null; 539 } 540 541 @Override 542 public Object getBean() { 543 return Labeled.this; 544 } 545 546 @Override 547 public String getName() { 548 return "imageUrl"; 549 } 550 551 @Override 552 public CssMetaData<Labeled,String> getCssMetaData() { 553 return StyleableProperties.GRAPHIC; 554 } 555 556 }; 557 } 558 return imageUrl; 559 } 560 561 /** 562 * Whether all text should be underlined. 563 * @return the underline property of all text in this labeled 564 */ 565 public final BooleanProperty underlineProperty() { 566 if (underline == null) { 567 underline = new StyleableBooleanProperty(false) { 568 569 @Override 570 public CssMetaData<Labeled, Boolean> getCssMetaData() { 571 return StyleableProperties.UNDERLINE; 572 } 573 574 @Override 575 public Object getBean() { 576 return Labeled.this; 577 } 578 579 @Override 580 public String getName() { 581 return "underline"; 582 } 583 }; 584 } 585 return underline; 586 } 587 private BooleanProperty underline; 588 public final void setUnderline(boolean value) { underlineProperty().setValue(value); } 589 public final boolean isUnderline() { return underline == null ? false : underline.getValue(); } 590 591 /** 592 * Specifies the space in pixel between lines. 593 * @return the line spacing property between lines in this labeled 594 * @since JavaFX 8.0 595 */ 596 public final DoubleProperty lineSpacingProperty() { 597 if (lineSpacing == null) { 598 lineSpacing = new StyleableDoubleProperty(0) { 599 600 @Override 601 public CssMetaData<Labeled,Number> getCssMetaData() { 602 return StyleableProperties.LINE_SPACING; 603 } 604 605 @Override 606 public Object getBean() { 607 return Labeled.this; 608 } 609 610 @Override 611 public String getName() { 612 return "lineSpacing"; 613 } 614 }; 615 } 616 return lineSpacing; 617 } 618 private DoubleProperty lineSpacing; 619 public final void setLineSpacing(double value) { lineSpacingProperty().setValue(value); } 620 public final double getLineSpacing() { return lineSpacing == null ? 0 : lineSpacing.getValue(); } 621 622 /** 623 * Specifies the positioning of the graphic relative to the text. 624 * @return content display property of this labeled 625 */ 626 public final ObjectProperty<ContentDisplay> contentDisplayProperty() { 627 if (contentDisplay == null) { 628 contentDisplay = new StyleableObjectProperty<ContentDisplay>(ContentDisplay.LEFT) { 629 630 @Override 631 public CssMetaData<Labeled,ContentDisplay> getCssMetaData() { 632 return StyleableProperties.CONTENT_DISPLAY; 633 } 634 635 @Override 636 public Object getBean() { 637 return Labeled.this; 638 } 639 640 @Override 641 public String getName() { 642 return "contentDisplay"; 643 } 644 }; 645 } 646 return contentDisplay; 647 } 648 private ObjectProperty<ContentDisplay> contentDisplay; 649 public final void setContentDisplay(ContentDisplay value) { contentDisplayProperty().setValue(value); } 650 public final ContentDisplay getContentDisplay() { return contentDisplay == null ? ContentDisplay.LEFT : contentDisplay.getValue(); } 651 652 /** 653 * The padding around the Labeled's text and graphic content. 654 * By default labelPadding is Insets.EMPTY and cannot be set to null. 655 * Subclasses may add nodes outside this padding and inside the Labeled's padding. 656 * 657 * This property can only be set from CSS. 658 * @return the label padding property of this labeled 659 */ 660 public final ReadOnlyObjectProperty<Insets> labelPaddingProperty() { 661 return labelPaddingPropertyImpl(); 662 } 663 private ObjectProperty<Insets> labelPaddingPropertyImpl() { 664 if (labelPadding == null) { 665 labelPadding = new StyleableObjectProperty<Insets>(Insets.EMPTY) { 666 private Insets lastValidValue = Insets.EMPTY; 667 668 @Override 669 public void invalidated() { 670 final Insets newValue = get(); 671 if (newValue == null) { 672 set(lastValidValue); 673 throw new NullPointerException("cannot set labelPadding to null"); 674 } 675 lastValidValue = newValue; 676 requestLayout(); 677 } 678 679 @Override 680 public CssMetaData<Labeled,Insets> getCssMetaData() { 681 return StyleableProperties.LABEL_PADDING; 682 } 683 684 @Override 685 public Object getBean() { 686 return Labeled.this; 687 } 688 689 @Override 690 public String getName() { 691 return "labelPadding"; 692 } 693 }; 694 } 695 return labelPadding; 696 } 697 private ObjectProperty<Insets> labelPadding; 698 private void setLabelPadding(Insets value) { labelPaddingPropertyImpl().set(value); } 699 public final Insets getLabelPadding() { return labelPadding == null ? Insets.EMPTY : labelPadding.get(); } 700 701 /** 702 * The amount of space between the graphic and text 703 * @return the graphics text gap property of this labeled 704 */ 705 public final DoubleProperty graphicTextGapProperty() { 706 if (graphicTextGap == null) { 707 graphicTextGap = new StyleableDoubleProperty(4) { 708 709 @Override 710 public CssMetaData<Labeled,Number> getCssMetaData() { 711 return StyleableProperties.GRAPHIC_TEXT_GAP; 712 } 713 714 @Override 715 public Object getBean() { 716 return Labeled.this; 717 } 718 719 @Override 720 public String getName() { 721 return "graphicTextGap"; 722 } 723 }; 724 } 725 return graphicTextGap; 726 } 727 private DoubleProperty graphicTextGap; 728 public final void setGraphicTextGap(double value) { graphicTextGapProperty().setValue(value); } 729 public final double getGraphicTextGap() { return graphicTextGap == null ? 4 : graphicTextGap.getValue(); } 730 731 732 /** 733 * The {@link Paint} used to fill the text. 734 */ 735 private ObjectProperty<Paint> textFill; // TODO for now change this 736 737 public final void setTextFill(Paint value) { 738 textFillProperty().set(value); 739 } 740 741 public final Paint getTextFill() { 742 return textFill == null ? Color.BLACK : textFill.get(); 743 } 744 745 public final ObjectProperty<Paint> textFillProperty() { 746 if (textFill == null) { 747 textFill = new StyleableObjectProperty<Paint>(Color.BLACK) { 748 749 @Override 750 public CssMetaData<Labeled,Paint> getCssMetaData() { 751 return StyleableProperties.TEXT_FILL; 752 } 753 754 @Override 755 public Object getBean() { 756 return Labeled.this; 757 } 758 759 @Override 760 public String getName() { 761 return "textFill"; 762 } 763 }; 764 } 765 return textFill; 766 } 767 768 769 /** 770 * MnemonicParsing property to enable/disable text parsing. 771 * If this is set to true, then the Label text will be 772 * parsed to see if it contains the mnemonic parsing character '_'. 773 * When a mnemonic is detected the key combination will 774 * be determined based on the succeeding character, and the mnemonic 775 * added. 776 * 777 * <p> 778 * The default value for Labeled is false, but it 779 * is enabled by default on some Controls. 780 * </p> 781 */ 782 private BooleanProperty mnemonicParsing; 783 public final void setMnemonicParsing(boolean value) { 784 mnemonicParsingProperty().set(value); 785 } 786 public final boolean isMnemonicParsing() { 787 return mnemonicParsing == null ? false : mnemonicParsing.get(); 788 } 789 public final BooleanProperty mnemonicParsingProperty() { 790 if (mnemonicParsing == null) { 791 mnemonicParsing = new SimpleBooleanProperty(this, "mnemonicParsing"); 792 } 793 return mnemonicParsing; 794 } 795 796 // /** 797 // * This is the symbol that is searched for in the text and used as 798 // * a mnemonic. You can change what symbol is used. Using the symbol 799 // * more than once will cause the symbol to be escaped. Thus, if "_" 800 // * (the default) is used, then the string "H_ello World" will use 801 // * "e" as the mnemonic. If "H__ello World" is used, then no mnemonic 802 // * will be used and the text will be rendered as "H_ello World". 803 // * TODO: Have i18n review this part of the API to confirm proper 804 // * externalization will work as expected 805 // */ 806 807 @Override public String toString() { 808 StringBuilder builder = 809 new StringBuilder(super.toString()) 810 .append("'").append(getText()).append("'"); 811 return builder.toString(); 812 } 813 814 /*************************************************************************** 815 * * 816 * Stylesheet Handling * 817 * * 818 **************************************************************************/ 819 820 /** 821 * Returns the initial alignment state of this control, for use 822 * by the JavaFX CSS engine to correctly set its initial value. This method 823 * is overridden to use Pos.CENTER_LEFT initially. 824 * 825 * @return the initial alignment state of this control 826 * @since 9 827 */ 828 protected Pos getInitialAlignment() { 829 return Pos.CENTER_LEFT; 830 } 831 832 private static class StyleableProperties { 833 private static final FontCssMetaData<Labeled> FONT = 834 new FontCssMetaData<Labeled>("-fx-font", Font.getDefault()) { 835 836 @Override 837 public boolean isSettable(Labeled n) { 838 return n.font == null || !n.font.isBound(); 839 } 840 841 @Override 842 public StyleableProperty<Font> getStyleableProperty(Labeled n) { 843 return (StyleableProperty<Font>)(WritableValue<Font>)n.fontProperty(); 844 } 845 }; 846 847 private static final CssMetaData<Labeled,Pos> ALIGNMENT = 848 new CssMetaData<Labeled,Pos>("-fx-alignment", 849 new EnumConverter<Pos>(Pos.class), Pos.CENTER_LEFT ) { 850 851 @Override 852 public boolean isSettable(Labeled n) { 853 return n.alignment == null || !n.alignment.isBound(); 854 } 855 856 @Override 857 public StyleableProperty<Pos> getStyleableProperty(Labeled n) { 858 return (StyleableProperty<Pos>)(WritableValue<Pos>)n.alignmentProperty(); 859 } 860 861 @Override 862 public Pos getInitialValue(Labeled n) { 863 return n.getInitialAlignment(); 864 } 865 }; 866 867 private static final CssMetaData<Labeled,TextAlignment> TEXT_ALIGNMENT = 868 new CssMetaData<Labeled,TextAlignment>("-fx-text-alignment", 869 new EnumConverter<TextAlignment>(TextAlignment.class), 870 TextAlignment.LEFT) { 871 872 @Override 873 public boolean isSettable(Labeled n) { 874 return n.textAlignment == null || !n.textAlignment.isBound(); 875 } 876 877 @Override 878 public StyleableProperty<TextAlignment> getStyleableProperty(Labeled n) { 879 return (StyleableProperty<TextAlignment>)(WritableValue<TextAlignment>)n.textAlignmentProperty(); 880 } 881 }; 882 883 private static final CssMetaData<Labeled,Paint> TEXT_FILL = 884 new CssMetaData<Labeled,Paint>("-fx-text-fill", 885 PaintConverter.getInstance(), Color.BLACK) { 886 887 @Override 888 public boolean isSettable(Labeled n) { 889 return n.textFill == null || !n.textFill.isBound(); 890 } 891 892 @Override 893 public StyleableProperty<Paint> getStyleableProperty(Labeled n) { 894 return (StyleableProperty<Paint>)(WritableValue<Paint>)n.textFillProperty(); 895 } 896 }; 897 898 private static final CssMetaData<Labeled,OverrunStyle> TEXT_OVERRUN = 899 new CssMetaData<Labeled,OverrunStyle>("-fx-text-overrun", 900 new EnumConverter<OverrunStyle>(OverrunStyle.class), 901 OverrunStyle.ELLIPSIS) { 902 903 @Override 904 public boolean isSettable(Labeled n) { 905 return n.textOverrun == null || !n.textOverrun.isBound(); 906 } 907 908 @Override 909 public StyleableProperty<OverrunStyle> getStyleableProperty(Labeled n) { 910 return (StyleableProperty<OverrunStyle>)(WritableValue<OverrunStyle>)n.textOverrunProperty(); 911 } 912 }; 913 914 private static final CssMetaData<Labeled,String> ELLIPSIS_STRING = 915 new CssMetaData<Labeled,String>("-fx-ellipsis-string", 916 StringConverter.getInstance(), DEFAULT_ELLIPSIS_STRING) { 917 918 @Override public boolean isSettable(Labeled n) { 919 return n.ellipsisString == null || !n.ellipsisString.isBound(); 920 } 921 922 @Override public StyleableProperty<String> getStyleableProperty(Labeled n) { 923 return (StyleableProperty<String>)(WritableValue<String>)n.ellipsisStringProperty(); 924 } 925 }; 926 927 private static final CssMetaData<Labeled,Boolean> WRAP_TEXT = 928 new CssMetaData<Labeled,Boolean>("-fx-wrap-text", 929 BooleanConverter.getInstance(), false) { 930 931 @Override 932 public boolean isSettable(Labeled n) { 933 return n.wrapText == null || !n.wrapText.isBound(); 934 } 935 936 @Override 937 public StyleableProperty<Boolean> getStyleableProperty(Labeled n) { 938 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.wrapTextProperty(); 939 } 940 }; 941 942 private static final CssMetaData<Labeled,String> GRAPHIC = 943 new CssMetaData<Labeled,String>("-fx-graphic", 944 StringConverter.getInstance()) { 945 946 @Override 947 public boolean isSettable(Labeled n) { 948 // Note that we care about the graphic, not imageUrl 949 return n.graphic == null || !n.graphic.isBound(); 950 } 951 952 @Override 953 public StyleableProperty<String> getStyleableProperty(Labeled n) { 954 return n.imageUrlProperty(); 955 } 956 }; 957 958 private static final CssMetaData<Labeled,Boolean> UNDERLINE = 959 new CssMetaData<Labeled,Boolean>("-fx-underline", 960 BooleanConverter.getInstance(), Boolean.FALSE) { 961 962 @Override 963 public boolean isSettable(Labeled n) { 964 return n.underline == null || !n.underline.isBound(); 965 } 966 967 @Override 968 public StyleableProperty<Boolean> getStyleableProperty(Labeled n) { 969 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.underlineProperty(); 970 } 971 }; 972 973 private static final CssMetaData<Labeled,Number> LINE_SPACING = 974 new CssMetaData<Labeled,Number>("-fx-line-spacing", 975 SizeConverter.getInstance(), 0) { 976 977 @Override 978 public boolean isSettable(Labeled n) { 979 return n.lineSpacing == null || !n.lineSpacing.isBound(); 980 } 981 982 @Override 983 public StyleableProperty<Number> getStyleableProperty(Labeled n) { 984 return (StyleableProperty<Number>)(WritableValue<Number>)n.lineSpacingProperty(); 985 } 986 }; 987 988 private static final CssMetaData<Labeled,ContentDisplay> CONTENT_DISPLAY = 989 new CssMetaData<Labeled,ContentDisplay>("-fx-content-display", 990 new EnumConverter<ContentDisplay>(ContentDisplay.class), 991 ContentDisplay.LEFT) { 992 993 @Override 994 public boolean isSettable(Labeled n) { 995 return n.contentDisplay == null || !n.contentDisplay.isBound(); 996 } 997 998 @Override 999 public StyleableProperty<ContentDisplay> getStyleableProperty(Labeled n) { 1000 return (StyleableProperty<ContentDisplay>)(WritableValue<ContentDisplay>)n.contentDisplayProperty(); 1001 } 1002 }; 1003 1004 private static final CssMetaData<Labeled,Insets> LABEL_PADDING = 1005 new CssMetaData<Labeled,Insets>("-fx-label-padding", 1006 InsetsConverter.getInstance(), Insets.EMPTY) { 1007 1008 @Override 1009 public boolean isSettable(Labeled n) { 1010 return n.labelPadding == null || !n.labelPadding.isBound(); 1011 } 1012 1013 @Override 1014 public StyleableProperty<Insets> getStyleableProperty(Labeled n) { 1015 return (StyleableProperty<Insets>)(WritableValue<Insets>)n.labelPaddingPropertyImpl(); 1016 } 1017 }; 1018 1019 private static final CssMetaData<Labeled,Number> GRAPHIC_TEXT_GAP = 1020 new CssMetaData<Labeled,Number>("-fx-graphic-text-gap", 1021 SizeConverter.getInstance(), 4.0) { 1022 1023 @Override 1024 public boolean isSettable(Labeled n) { 1025 return n.graphicTextGap == null || !n.graphicTextGap.isBound(); 1026 } 1027 1028 @Override 1029 public StyleableProperty<Number> getStyleableProperty(Labeled n) { 1030 return (StyleableProperty<Number>)(WritableValue<Number>)n.graphicTextGapProperty(); 1031 } 1032 }; 1033 1034 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 1035 static { 1036 final List<CssMetaData<? extends Styleable, ?>> styleables = 1037 new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData()); 1038 Collections.addAll(styleables, 1039 FONT, 1040 ALIGNMENT, 1041 TEXT_ALIGNMENT, 1042 TEXT_FILL, 1043 TEXT_OVERRUN, 1044 ELLIPSIS_STRING, 1045 WRAP_TEXT, 1046 GRAPHIC, 1047 UNDERLINE, 1048 LINE_SPACING, 1049 CONTENT_DISPLAY, 1050 LABEL_PADDING, 1051 GRAPHIC_TEXT_GAP 1052 ); 1053 STYLEABLES = Collections.unmodifiableList(styleables); 1054 } 1055 } 1056 1057 /** 1058 * @return The CssMetaData associated with this class, which may include the 1059 * CssMetaData of its superclasses. 1060 * @since JavaFX 8.0 1061 */ 1062 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 1063 return StyleableProperties.STYLEABLES; 1064 } 1065 1066 /** 1067 * {@inheritDoc} 1068 * @since JavaFX 8.0 1069 */ 1070 @Override 1071 public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { 1072 return getClassCssMetaData(); 1073 } 1074 }