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