1 /* 2 * Copyright (c) 2011, 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 import com.sun.javafx.beans.IDProperty; 29 import com.sun.javafx.scene.control.ControlAcceleratorSupport; 30 import javafx.collections.ObservableSet; 31 import javafx.css.CssMetaData; 32 import javafx.beans.property.BooleanProperty; 33 import javafx.beans.property.ObjectProperty; 34 import javafx.beans.property.ObjectPropertyBase; 35 import javafx.beans.property.SimpleBooleanProperty; 36 import javafx.beans.property.SimpleObjectProperty; 37 import javafx.beans.property.SimpleStringProperty; 38 import javafx.beans.property.StringProperty; 39 import javafx.collections.FXCollections; 40 import javafx.collections.ObservableList; 41 import javafx.css.PseudoClass; 42 import javafx.event.Event; 43 import javafx.event.EventDispatchChain; 44 import javafx.event.EventHandler; 45 import javafx.event.EventTarget; 46 import javafx.event.EventType; 47 import javafx.scene.Node; 48 49 import javafx.css.Styleable; 50 import com.sun.javafx.event.EventHandlerManager; 51 52 import java.lang.ref.WeakReference; 53 import java.util.ArrayList; 54 import java.util.Collections; 55 import java.util.HashMap; 56 import java.util.List; 57 import java.util.Set; 58 59 import javafx.beans.DefaultProperty; 60 import javafx.beans.InvalidationListener; 61 import javafx.beans.property.BooleanPropertyBase; 62 import javafx.beans.property.ReadOnlyBooleanProperty; 63 import javafx.beans.property.ReadOnlyBooleanWrapper; 64 import javafx.beans.property.ReadOnlyObjectProperty; 65 import javafx.beans.property.ReadOnlyObjectWrapper; 66 import javafx.collections.ObservableMap; 67 68 /** 69 * <p>Tabs are placed within a {@link TabPane}, where each tab represents a single 70 * 'page'.</p> 71 * <p>Tabs can contain any {@link Node} such as UI controls or groups 72 * of nodes added to a layout container.</p> 73 * <p>When the user clicks 74 * on a Tab in the TabPane the Tab content becomes visible to the user.</p> 75 * @since JavaFX 2.0 76 */ 77 @DefaultProperty("content") 78 @IDProperty("id") 79 public class Tab implements EventTarget, Styleable { 80 81 /*************************************************************************** 82 * * 83 * Constructors * 84 * * 85 **************************************************************************/ 86 87 /** 88 * Creates a tab with no title. 89 */ 90 public Tab() { 91 this(null); 92 } 93 94 /** 95 * Creates a tab with a text title. 96 * 97 * @param text The title of the tab. 98 */ 99 public Tab(String text) { 100 this(text, null); 101 } 102 103 /** 104 * Creates a tab with a text title and the specified content node. 105 * 106 * @param text The title of the tab. 107 * @param content The content of the tab. 108 * @since JavaFX 8u40 109 */ 110 public Tab(String text, Node content) { 111 setText(text); 112 setContent(content); 113 styleClass.addAll(DEFAULT_STYLE_CLASS); 114 } 115 116 117 /*************************************************************************** 118 * * 119 * Properties * 120 * * 121 **************************************************************************/ 122 123 private StringProperty id; 124 125 /** 126 * Sets the id of this tab. This simple string identifier is useful for 127 * finding a specific Tab within the {@code TabPane}. The default value is {@code null}. 128 */ 129 public final void setId(String value) { idProperty().set(value); } 130 131 /** 132 * The id of this tab. 133 * 134 * @return The id of the tab. 135 */ 136 @Override 137 public final String getId() { return id == null ? null : id.get(); } 138 139 /** 140 * The id of this tab. 141 */ 142 public final StringProperty idProperty() { 143 if (id == null) { 144 id = new SimpleStringProperty(this, "id"); 145 } 146 return id; 147 } 148 149 private StringProperty style; 150 151 /** 152 * A string representation of the CSS style associated with this 153 * tab. This is analogous to the "style" attribute of an 154 * HTML element. Note that, like the HTML style attribute, this 155 * variable contains style properties and values and not the 156 * selector portion of a style rule. 157 * <p> 158 * Parsing this style might not be supported on some limited 159 * platforms. It is recommended to use a standalone CSS file instead. 160 * 161 */ 162 public final void setStyle(String value) { styleProperty().set(value); } 163 164 /** 165 * The CSS style string associated to this tab. 166 * 167 * @return The CSS style string associated to this tab. 168 */ 169 @Override 170 public final String getStyle() { return style == null ? null : style.get(); } 171 172 /** 173 * The CSS style string associated to this tab. 174 */ 175 public final StringProperty styleProperty() { 176 if (style == null) { 177 style = new SimpleStringProperty(this, "style"); 178 } 179 return style; 180 } 181 182 private ReadOnlyBooleanWrapper selected; 183 184 final void setSelected(boolean value) { 185 selectedPropertyImpl().set(value); 186 } 187 188 /** 189 * <p>Represents whether this tab is the currently selected tab, 190 * To change the selected Tab use {@code tabPane.getSelectionModel().select()} 191 * </p> 192 */ 193 public final boolean isSelected() { 194 return selected == null ? false : selected.get(); 195 } 196 197 /** 198 * The currently selected tab. 199 */ 200 public final ReadOnlyBooleanProperty selectedProperty() { 201 return selectedPropertyImpl().getReadOnlyProperty(); 202 } 203 204 private ReadOnlyBooleanWrapper selectedPropertyImpl() { 205 if (selected == null) { 206 selected = new ReadOnlyBooleanWrapper() { 207 @Override protected void invalidated() { 208 if (getOnSelectionChanged() != null) { 209 Event.fireEvent(Tab.this, new Event(SELECTION_CHANGED_EVENT)); 210 } 211 } 212 213 @Override 214 public Object getBean() { 215 return Tab.this; 216 } 217 218 @Override 219 public String getName() { 220 return "selected"; 221 } 222 }; 223 } 224 return selected; 225 } 226 227 private ReadOnlyObjectWrapper<TabPane> tabPane; 228 229 final void setTabPane(TabPane value) { 230 tabPanePropertyImpl().set(value); 231 } 232 233 /** 234 * <p>A reference to the TabPane that contains this tab instance.</p> 235 */ 236 public final TabPane getTabPane() { 237 return tabPane == null ? null : tabPane.get(); 238 } 239 240 /** 241 * The TabPane that contains this tab. 242 */ 243 public final ReadOnlyObjectProperty<TabPane> tabPaneProperty() { 244 return tabPanePropertyImpl().getReadOnlyProperty(); 245 } 246 247 private ReadOnlyObjectWrapper<TabPane> tabPanePropertyImpl() { 248 if (tabPane == null) { 249 tabPane = new ReadOnlyObjectWrapper<TabPane>(this, "tabPane") { 250 private WeakReference<TabPane> oldParent; 251 252 @Override protected void invalidated() { 253 if(oldParent != null && oldParent.get() != null) { 254 oldParent.get().disabledProperty().removeListener(parentDisabledChangedListener); 255 } 256 updateDisabled(); 257 TabPane newParent = get(); 258 if (newParent != null) { 259 newParent.disabledProperty().addListener(parentDisabledChangedListener); 260 } 261 oldParent = new WeakReference<TabPane>(newParent); 262 super.invalidated(); 263 } 264 }; 265 } 266 return tabPane; 267 } 268 269 private final InvalidationListener parentDisabledChangedListener = valueModel -> { 270 updateDisabled(); 271 }; 272 273 private StringProperty text; 274 275 /** 276 * <p>Sets the text to show in the tab to allow the user to differentiate between 277 * the function of each tab. The text is always visible 278 * </p> 279 */ 280 public final void setText(String value) { 281 textProperty().set(value); 282 } 283 284 /** 285 * The text shown in the tab. 286 * 287 * @return The text shown in the tab. 288 */ 289 public final String getText() { 290 return text == null ? null : text.get(); 291 } 292 293 /** 294 * The text shown in the tab. 295 */ 296 public final StringProperty textProperty() { 297 if (text == null) { 298 text = new SimpleStringProperty(this, "text"); 299 } 300 return text; 301 } 302 303 private ObjectProperty<Node> graphic; 304 305 /** 306 * <p>Sets the graphic to show in the tab to allow the user to differentiate 307 * between the function of each tab. By default the graphic does not rotate 308 * based on the TabPane.tabPosition value, but it can be set to rotate by 309 * setting TabPane.rotateGraphic to true.</p> 310 */ 311 public final void setGraphic(Node value) { 312 graphicProperty().set(value); 313 } 314 315 /** 316 * The graphic shown in the tab. 317 * 318 * @return The graphic shown in the tab. 319 */ 320 public final Node getGraphic() { 321 return graphic == null ? null : graphic.get(); 322 } 323 324 /** 325 * The graphic in the tab. 326 * 327 * @return The graphic in the tab. 328 */ 329 public final ObjectProperty<Node> graphicProperty() { 330 if (graphic == null) { 331 graphic = new SimpleObjectProperty<Node>(this, "graphic"); 332 } 333 return graphic; 334 } 335 336 private ObjectProperty<Node> content; 337 338 /** 339 * <p>The content to show within the main TabPane area. The content 340 * can be any Node such as UI controls or groups of nodes added 341 * to a layout container.</p> 342 */ 343 public final void setContent(Node value) { 344 contentProperty().set(value); 345 } 346 347 /** 348 * <p>The content associated with the tab.</p> 349 * 350 * @return The content associated with the tab. 351 */ 352 public final Node getContent() { 353 return content == null ? null : content.get(); 354 } 355 356 /** 357 * <p>The content associated with the tab.</p> 358 */ 359 public final ObjectProperty<Node> contentProperty() { 360 if (content == null) { 361 content = new SimpleObjectProperty<Node>(this, "content"); 362 } 363 return content; 364 } 365 366 367 private ObjectProperty<ContextMenu> contextMenu; 368 369 /** 370 * <p>Specifies the context menu to show when the user right-clicks on the tab. 371 * </p> 372 */ 373 public final void setContextMenu(ContextMenu value) { 374 contextMenuProperty().set(value); 375 } 376 377 /** 378 * The context menu associated with the tab. 379 * @return The context menu associated with the tab. 380 */ 381 public final ContextMenu getContextMenu() { 382 return contextMenu == null ? null : contextMenu.get(); 383 } 384 385 /** 386 * The context menu associated with the tab. 387 */ 388 public final ObjectProperty<ContextMenu> contextMenuProperty() { 389 if (contextMenu == null) { 390 contextMenu = new SimpleObjectProperty<ContextMenu>(this, "contextMenu") { 391 private WeakReference<ContextMenu> contextMenuRef; 392 393 @Override protected void invalidated() { 394 ContextMenu oldMenu = contextMenuRef == null ? null : contextMenuRef.get(); 395 if (oldMenu != null) { 396 ControlAcceleratorSupport.removeAcceleratorsFromScene(oldMenu.getItems(), Tab.this); 397 } 398 399 ContextMenu ctx = get(); 400 contextMenuRef = new WeakReference<>(ctx); 401 402 if (ctx != null) { 403 // if a context menu is set, we need to install any accelerators 404 // belonging to its menu items ASAP into the scene that this 405 // Control is in (if the control is not in a Scene, we will need 406 // to wait until it is and then do it). 407 ControlAcceleratorSupport.addAcceleratorsIntoScene(ctx.getItems(), Tab.this); 408 } 409 } 410 }; 411 } 412 return contextMenu; 413 } 414 415 private BooleanProperty closable; 416 417 /** 418 * <p>Sets {@code true} if the tab is closable. If this is set to {@code false}, 419 * then regardless of the TabClosingPolicy, it will not be 420 * possible for the user to close this tab. Therefore, when this 421 * property is {@code false}, no 'close' button will be shown on the tab. 422 * The default is {@code true}.</p> 423 * 424 */ 425 public final void setClosable(boolean value) { 426 closableProperty().set(value); 427 } 428 429 /** 430 * Returns {@code true} if this tab is closable. 431 * 432 * @return {@code true} if the tab is closable. 433 */ 434 public final boolean isClosable() { 435 return closable == null ? true : closable.get(); 436 } 437 438 /** 439 * The closable state for this tab. 440 */ 441 public final BooleanProperty closableProperty() { 442 if (closable == null) { 443 closable = new SimpleBooleanProperty(this, "closable", true); 444 } 445 return closable; 446 } 447 448 449 /** 450 * <p>Called when the tab becomes selected or unselected.</p> 451 */ 452 public static final EventType<Event> SELECTION_CHANGED_EVENT = 453 new EventType<Event> (Event.ANY, "SELECTION_CHANGED_EVENT"); 454 private ObjectProperty<EventHandler<Event>> onSelectionChanged; 455 456 /** 457 * Defines a function to be called when a selection changed has occurred on the tab. 458 */ 459 public final void setOnSelectionChanged(EventHandler<Event> value) { 460 onSelectionChangedProperty().set(value); 461 } 462 463 /** 464 * The event handler that is associated with a selection on the tab. 465 * 466 * @return The event handler that is associated with a tab selection. 467 */ 468 public final EventHandler<Event> getOnSelectionChanged() { 469 return onSelectionChanged == null ? null : onSelectionChanged.get(); 470 } 471 472 /** 473 * The event handler that is associated with a selection on the tab. 474 */ 475 public final ObjectProperty<EventHandler<Event>> onSelectionChangedProperty() { 476 if (onSelectionChanged == null) { 477 onSelectionChanged = new ObjectPropertyBase<EventHandler<Event>>() { 478 @Override protected void invalidated() { 479 setEventHandler(SELECTION_CHANGED_EVENT, get()); 480 } 481 482 @Override 483 public Object getBean() { 484 return Tab.this; 485 } 486 487 @Override 488 public String getName() { 489 return "onSelectionChanged"; 490 } 491 }; 492 } 493 return onSelectionChanged; 494 } 495 496 /** 497 * <p>Called when a user closes this tab. This is useful for freeing up memory.</p> 498 */ 499 public static final EventType<Event> CLOSED_EVENT = new EventType<Event>(Event.ANY, "TAB_CLOSED"); 500 private ObjectProperty<EventHandler<Event>> onClosed; 501 502 /** 503 * Defines a function to be called when the tab is closed. 504 */ 505 public final void setOnClosed(EventHandler<Event> value) { 506 onClosedProperty().set(value); 507 } 508 509 /** 510 * The event handler that is associated with the tab when the tab is closed. 511 * 512 * @return The event handler that is associated with the tab when the tab is closed. 513 */ 514 public final EventHandler<Event> getOnClosed() { 515 return onClosed == null ? null : onClosed.get(); 516 } 517 518 /** 519 * The event handler that is associated with the tab when the tab is closed. 520 */ 521 public final ObjectProperty<EventHandler<Event>> onClosedProperty() { 522 if (onClosed == null) { 523 onClosed = new ObjectPropertyBase<EventHandler<Event>>() { 524 @Override protected void invalidated() { 525 setEventHandler(CLOSED_EVENT, get()); 526 } 527 528 @Override 529 public Object getBean() { 530 return Tab.this; 531 } 532 533 @Override 534 public String getName() { 535 return "onClosed"; 536 } 537 }; 538 } 539 return onClosed; 540 } 541 542 private ObjectProperty<Tooltip> tooltip; 543 544 /** 545 * <p>Specifies the tooltip to show when the user hovers over the tab.</p> 546 */ 547 public final void setTooltip(Tooltip value) { tooltipProperty().setValue(value); } 548 549 /** 550 * The tooltip associated with this tab. 551 * @return The tooltip associated with this tab. 552 */ 553 public final Tooltip getTooltip() { return tooltip == null ? null : tooltip.getValue(); } 554 555 /** 556 * The tooltip associated with this tab. 557 */ 558 public final ObjectProperty<Tooltip> tooltipProperty() { 559 if (tooltip == null) { 560 tooltip = new SimpleObjectProperty<Tooltip>(this, "tooltip"); 561 } 562 return tooltip; 563 } 564 565 private final ObservableList<String> styleClass = FXCollections.observableArrayList(); 566 567 private BooleanProperty disable; 568 569 /** 570 * Sets the disabled state of this tab. 571 * 572 * @param value the state to set this tab 573 * 574 * @defaultValue false 575 * @since JavaFX 2.2 576 */ 577 public final void setDisable(boolean value) { 578 disableProperty().set(value); 579 } 580 581 /** 582 * Returns {@code true} if this tab is disable. 583 * @since JavaFX 2.2 584 */ 585 public final boolean isDisable() { return disable == null ? false : disable.get(); } 586 587 /** 588 * Sets the disabled state of this tab. A disable tab is no longer interactive 589 * or traversable, but the contents remain interactive. A disable tab 590 * can be selected using {@link TabPane#getSelectionModel()}. 591 * 592 * @defaultValue false 593 * @since JavaFX 2.2 594 */ 595 public final BooleanProperty disableProperty() { 596 if (disable == null) { 597 disable = new BooleanPropertyBase(false) { 598 @Override 599 protected void invalidated() { 600 updateDisabled(); 601 } 602 603 @Override 604 public Object getBean() { 605 return Tab.this; 606 } 607 608 @Override 609 public String getName() { 610 return "disable"; 611 } 612 }; 613 } 614 return disable; 615 } 616 617 private ReadOnlyBooleanWrapper disabled; 618 619 private final void setDisabled(boolean value) { 620 disabledPropertyImpl().set(value); 621 } 622 623 /** 624 * Returns true when the {@code Tab} {@link #disableProperty disable} is set to 625 * {@code true} or if the {@code TabPane} is disabled. 626 * @since JavaFX 2.2 627 */ 628 public final boolean isDisabled() { 629 return disabled == null ? false : disabled.get(); 630 } 631 632 /** 633 * Indicates whether or not this {@code Tab} is disabled. A {@code Tab} 634 * will become disabled if {@link #disableProperty disable} is set to {@code true} on either 635 * itself or if the {@code TabPane} is disabled. 636 * 637 * @defaultValue false 638 * @since JavaFX 2.2 639 */ 640 public final ReadOnlyBooleanProperty disabledProperty() { 641 return disabledPropertyImpl().getReadOnlyProperty(); 642 } 643 644 private ReadOnlyBooleanWrapper disabledPropertyImpl() { 645 if (disabled == null) { 646 disabled = new ReadOnlyBooleanWrapper() { 647 @Override 648 public Object getBean() { 649 return Tab.this; 650 } 651 652 @Override 653 public String getName() { 654 return "disabled"; 655 } 656 }; 657 } 658 return disabled; 659 } 660 661 private void updateDisabled() { 662 boolean disabled = isDisable() || (getTabPane() != null && getTabPane().isDisabled()); 663 setDisabled(disabled); 664 665 // Fix for RT-24658 - content should be disabled if the tab is disabled 666 Node content = getContent(); 667 if (content != null) { 668 content.setDisable(disabled); 669 } 670 } 671 672 /** 673 * Called when there is an external request to close this {@code Tab}. 674 * The installed event handler can prevent tab closing by consuming the 675 * received event. 676 * @since JavaFX 8.0 677 */ 678 public static final EventType<Event> TAB_CLOSE_REQUEST_EVENT = new EventType<Event> (Event.ANY, "TAB_CLOSE_REQUEST_EVENT"); 679 680 /** 681 * Called when there is an external request to close this {@code Tab}. 682 * The installed event handler can prevent tab closing by consuming the 683 * received event. 684 * @since JavaFX 8.0 685 */ 686 private ObjectProperty<EventHandler<Event>> onCloseRequest; 687 public final ObjectProperty<EventHandler<Event>> onCloseRequestProperty() { 688 if (onCloseRequest == null) { 689 onCloseRequest = new ObjectPropertyBase<EventHandler<Event>>() { 690 @Override protected void invalidated() { 691 setEventHandler(TAB_CLOSE_REQUEST_EVENT, get()); 692 } 693 694 @Override public Object getBean() { 695 return Tab.this; 696 } 697 698 @Override public String getName() { 699 return "onCloseRequest"; 700 } 701 }; 702 } 703 return onCloseRequest; 704 } 705 706 public EventHandler<Event> getOnCloseRequest() { 707 if( onCloseRequest == null ) { 708 return null; 709 } 710 return onCloseRequest.get(); 711 } 712 713 public void setOnCloseRequest(EventHandler<Event> value) { 714 onCloseRequestProperty().set(value); 715 } 716 717 718 // --- Properties 719 private static final Object USER_DATA_KEY = new Object(); 720 721 // A map containing a set of properties for this Tab 722 private ObservableMap<Object, Object> properties; 723 724 /** 725 * Returns an observable map of properties on this Tab for use primarily 726 * by application developers. 727 * 728 * @return an observable map of properties on this Tab for use primarily 729 * by application developers 730 * @since JavaFX 2.2 731 */ 732 public final ObservableMap<Object, Object> getProperties() { 733 if (properties == null) { 734 properties = FXCollections.observableMap(new HashMap<Object, Object>()); 735 } 736 return properties; 737 } 738 739 /** 740 * Tests if this Tab has properties. 741 * @return true if this tab has properties. 742 * @since JavaFX 2.2 743 */ 744 public boolean hasProperties() { 745 return properties != null && !properties.isEmpty(); 746 } 747 748 749 // --- UserData 750 /** 751 * Convenience method for setting a single Object property that can be 752 * retrieved at a later date. This is functionally equivalent to calling 753 * the getProperties().put(Object key, Object value) method. This can later 754 * be retrieved by calling {@link Tab#getUserData()}. 755 * 756 * @param value The value to be stored - this can later be retrieved by calling 757 * {@link Tab#getUserData()}. 758 * @since JavaFX 2.2 759 */ 760 public void setUserData(Object value) { 761 getProperties().put(USER_DATA_KEY, value); 762 } 763 764 /** 765 * Returns a previously set Object property, or null if no such property 766 * has been set using the {@link Tab#setUserData(java.lang.Object)} method. 767 * 768 * @return The Object that was previously set, or null if no property 769 * has been set or if null was set. 770 * @since JavaFX 2.2 771 */ 772 public Object getUserData() { 773 return getProperties().get(USER_DATA_KEY); 774 } 775 776 /** 777 * A list of String identifiers which can be used to logically group 778 * Nodes, specifically for an external style engine. This variable is 779 * analogous to the "class" attribute on an HTML element and, as such, 780 * each element of the list is a style class to which this Node belongs. 781 * 782 * @see <a href="http://www.w3.org/TR/css3-selectors/#class-html">CSS3 class selectors</a> 783 */ 784 @Override 785 public ObservableList<String> getStyleClass() { 786 return styleClass; 787 } 788 789 private final EventHandlerManager eventHandlerManager = 790 new EventHandlerManager(this); 791 792 /** {@inheritDoc} */ 793 @Override 794 public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) { 795 return tail.prepend(eventHandlerManager); 796 } 797 798 <E extends Event> void setEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) { 799 eventHandlerManager.setEventHandler(eventType, eventHandler); 800 } 801 802 /* 803 * See Node#lookup(String) 804 */ 805 Node lookup(String selector) { 806 if (selector == null) return null; 807 Node n = null; 808 if (getContent() != null) { 809 n = getContent().lookup(selector); 810 } 811 if (n == null && getGraphic() != null) { 812 n = getGraphic().lookup(selector); 813 } 814 return n; 815 } 816 817 /* 818 * See Node#lookupAll(String) 819 */ 820 List<Node> lookupAll(String selector) { 821 final List<Node> results = new ArrayList<>(); 822 if (getContent() != null) { 823 Set set = getContent().lookupAll(selector); 824 if (!set.isEmpty()) { 825 results.addAll(set); 826 } 827 } 828 if (getGraphic() != null) { 829 Set set = getGraphic().lookupAll(selector); 830 if (!set.isEmpty()) { 831 results.addAll(set); 832 } 833 } 834 return results; 835 } 836 837 838 /*************************************************************************** 839 * * 840 * Stylesheet Handling * 841 * * 842 **************************************************************************/ 843 844 private static final String DEFAULT_STYLE_CLASS = "tab"; 845 846 /** 847 * {@inheritDoc} 848 * @return "Tab" 849 * @since JavaFX 8.0 850 */ 851 @Override 852 public String getTypeSelector() { 853 return "Tab"; 854 } 855 856 /** 857 * {@inheritDoc} 858 * @return {@code getTabPane()} 859 * @since JavaFX 8.0 860 */ 861 @Override 862 public Styleable getStyleableParent() { 863 return getTabPane(); 864 } 865 866 /** 867 * {@inheritDoc} 868 * @since JavaFX 8.0 869 */ 870 public final ObservableSet<PseudoClass> getPseudoClassStates() { 871 return FXCollections.emptyObservableSet(); 872 } 873 874 /** 875 * {@inheritDoc} 876 * @since JavaFX 8.0 877 */ 878 @Override 879 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 880 return getClassCssMetaData(); 881 } 882 883 /** 884 * @return The CssMetaData associated with this class, which may include the 885 * CssMetaData of its superclasses. 886 * @since JavaFX 8.0 887 */ 888 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 889 return Collections.emptyList(); 890 } 891 }