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