Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.control; 027 028import javafx.collections.ObservableSet; 029import javafx.css.CssMetaData; 030import javafx.beans.property.BooleanProperty; 031import javafx.beans.property.ObjectProperty; 032import javafx.beans.property.ObjectPropertyBase; 033import javafx.beans.property.SimpleBooleanProperty; 034import javafx.beans.property.SimpleObjectProperty; 035import javafx.beans.property.SimpleStringProperty; 036import javafx.beans.property.StringProperty; 037import javafx.collections.FXCollections; 038import javafx.collections.ObservableList; 039import javafx.css.PseudoClass; 040import javafx.event.Event; 041import javafx.event.EventDispatchChain; 042import javafx.event.EventHandler; 043import javafx.event.EventTarget; 044import javafx.event.EventType; 045import javafx.scene.Node; 046 047import javafx.css.Styleable; 048import com.sun.javafx.event.EventHandlerManager; 049 050import java.lang.ref.WeakReference; 051import java.util.Collections; 052import java.util.HashMap; 053import java.util.List; 054 055import javafx.beans.DefaultProperty; 056import javafx.beans.InvalidationListener; 057import javafx.beans.Observable; 058import javafx.beans.property.BooleanPropertyBase; 059import javafx.beans.property.ReadOnlyBooleanProperty; 060import javafx.beans.property.ReadOnlyBooleanWrapper; 061import javafx.beans.property.ReadOnlyObjectProperty; 062import javafx.beans.property.ReadOnlyObjectWrapper; 063import javafx.collections.ObservableMap; 064 065/** 066 * <p>Tabs are placed within a {@link TabPane}, where each tab represents a single 067 * 'page'.</p> 068 * <p>Tabs can contain any {@link Node} such as UI controls or groups 069 * of nodes added to a layout container.</p> 070 * <p>When the user clicks 071 * on a Tab in the TabPane the Tab content becomes visible to the user.</p> 072 */ 073@DefaultProperty("content") 074public class Tab implements EventTarget, Styleable { 075 076 /*************************************************************************** 077 * * 078 * Constructors * 079 * * 080 **************************************************************************/ 081 082 /** 083 * Creates a tab with no title. 084 */ 085 public Tab() { 086 this(null); 087 } 088 089 /** 090 * Creates a tab with a text title. 091 * 092 * @param text The title of the tab. 093 */ 094 public Tab(String text) { 095 setText(text); 096 styleClass.addAll(DEFAULT_STYLE_CLASS); 097 } 098 099 100 /*************************************************************************** 101 * * 102 * Properties * 103 * * 104 **************************************************************************/ 105 106 private StringProperty id; 107 108 /** 109 * Sets the id of this tab. This simple string identifier is useful for 110 * finding a specific Tab within the {@code TabPane}. The default value is {@code null}. 111 */ 112 public final void setId(String value) { idProperty().set(value); } 113 114 /** 115 * The id of this tab. 116 * 117 * @return The id of the tab. 118 */ 119 @Override 120 public final String getId() { return id == null ? null : id.get(); } 121 122 /** 123 * The id of this tab. 124 */ 125 public final StringProperty idProperty() { 126 if (id == null) { 127 id = new SimpleStringProperty(this, "id"); 128 } 129 return id; 130 } 131 132 private StringProperty style; 133 134 /** 135 * A string representation of the CSS style associated with this 136 * tab. This is analogous to the "style" attribute of an 137 * HTML element. Note that, like the HTML style attribute, this 138 * variable contains style properties and values and not the 139 * selector portion of a style rule. 140 * <p> 141 * Parsing this style might not be supported on some limited 142 * platforms. It is recommended to use a standalone CSS file instead. 143 * 144 */ 145 public final void setStyle(String value) { styleProperty().set(value); } 146 147 /** 148 * The CSS style string associated to this tab. 149 * 150 * @return The CSS style string associated to this tab. 151 */ 152 @Override 153 public final String getStyle() { return style == null ? null : style.get(); } 154 155 /** 156 * The CSS style string associated to this tab. 157 */ 158 public final StringProperty styleProperty() { 159 if (style == null) { 160 style = new SimpleStringProperty(this, "style"); 161 } 162 return style; 163 } 164 165 private ReadOnlyBooleanWrapper selected; 166 167 final void setSelected(boolean value) { 168 selectedPropertyImpl().set(value); 169 } 170 171 /** 172 * <p>Represents whether this tab is the currently selected tab, 173 * To change the selected Tab use {@code tabPane.getSelectionModel().select()} 174 * </p> 175 */ 176 public final boolean isSelected() { 177 return selected == null ? false : selected.get(); 178 } 179 180 /** 181 * The currently selected tab. 182 */ 183 public final ReadOnlyBooleanProperty selectedProperty() { 184 return selectedPropertyImpl().getReadOnlyProperty(); 185 } 186 187 private ReadOnlyBooleanWrapper selectedPropertyImpl() { 188 if (selected == null) { 189 selected = new ReadOnlyBooleanWrapper() { 190 @Override protected void invalidated() { 191 if (getOnSelectionChanged() != null) { 192 Event.fireEvent(Tab.this, new Event(SELECTION_CHANGED_EVENT)); 193 } 194 } 195 196 @Override 197 public Object getBean() { 198 return Tab.this; 199 } 200 201 @Override 202 public String getName() { 203 return "selected"; 204 } 205 }; 206 } 207 return selected; 208 } 209 210 private ReadOnlyObjectWrapper<TabPane> tabPane; 211 212 final void setTabPane(TabPane value) { 213 tabPanePropertyImpl().set(value); 214 } 215 216 /** 217 * <p>A reference to the TabPane that contains this tab instance.</p> 218 */ 219 public final TabPane getTabPane() { 220 return tabPane == null ? null : tabPane.get(); 221 } 222 223 /** 224 * The TabPane that contains this tab. 225 */ 226 public final ReadOnlyObjectProperty<TabPane> tabPaneProperty() { 227 return tabPanePropertyImpl().getReadOnlyProperty(); 228 } 229 230 private ReadOnlyObjectWrapper<TabPane> tabPanePropertyImpl() { 231 if (tabPane == null) { 232 tabPane = new ReadOnlyObjectWrapper<TabPane>(this, "tabPane") { 233 private WeakReference<TabPane> oldParent; 234 235 @Override protected void invalidated() { 236 if(oldParent != null && oldParent.get() != null) { 237 oldParent.get().disabledProperty().removeListener(parentDisabledChangedListener); 238 } 239 updateDisabled(); 240 TabPane newParent = get(); 241 if (newParent != null) { 242 newParent.disabledProperty().addListener(parentDisabledChangedListener); 243 } 244 oldParent = new WeakReference<TabPane>(newParent); 245 super.invalidated(); 246 } 247 }; 248 } 249 return tabPane; 250 } 251 252 private final InvalidationListener parentDisabledChangedListener = new InvalidationListener() { 253 @Override public void invalidated(Observable valueModel) { 254 updateDisabled(); 255 } 256 }; 257 258 private StringProperty text; 259 260 /** 261 * <p>Sets the text to show in the tab to allow the user to differentiate between 262 * the function of each tab. The text is always visible 263 * </p> 264 */ 265 public final void setText(String value) { 266 textProperty().set(value); 267 } 268 269 /** 270 * The text shown in the tab. 271 * 272 * @return The text shown in the tab. 273 */ 274 public final String getText() { 275 return text == null ? null : text.get(); 276 } 277 278 /** 279 * The text shown in the tab. 280 */ 281 public final StringProperty textProperty() { 282 if (text == null) { 283 text = new SimpleStringProperty(this, "text"); 284 } 285 return text; 286 } 287 288 private ObjectProperty<Node> graphic; 289 290 /** 291 * <p>Sets the graphic to show in the tab to allow the user to differentiate 292 * between the function of each tab. By default the graphic does not rotate 293 * based on the TabPane.tabPosition value, but it can be set to rotate by 294 * setting TabPane.rotateGraphic to true.</p> 295 */ 296 public final void setGraphic(Node value) { 297 graphicProperty().set(value); 298 } 299 300 /** 301 * The graphic shown in the tab. 302 * 303 * @return The graphic shown in the tab. 304 */ 305 public final Node getGraphic() { 306 return graphic == null ? null : graphic.get(); 307 } 308 309 /** 310 * The graphic in the tab. 311 * 312 * @return The graphic in the tab. 313 */ 314 public final ObjectProperty<Node> graphicProperty() { 315 if (graphic == null) { 316 graphic = new SimpleObjectProperty<Node>(this, "graphic"); 317 } 318 return graphic; 319 } 320 321 private ObjectProperty<Node> content; 322 323 /** 324 * <p>The content to show within the main TabPane area. The content 325 * can be any Node such as UI controls or groups of nodes added 326 * to a layout container.</p> 327 */ 328 public final void setContent(Node value) { 329 contentProperty().set(value); 330 } 331 332 /** 333 * <p>The content associated with the tab.</p> 334 * 335 * @return The content associated with the tab. 336 */ 337 public final Node getContent() { 338 return content == null ? null : content.get(); 339 } 340 341 /** 342 * <p>The content associated with the tab.</p> 343 */ 344 public final ObjectProperty<Node> contentProperty() { 345 if (content == null) { 346 content = new SimpleObjectProperty<Node>(this, "content"); 347 } 348 return content; 349 } 350 351 352 private ObjectProperty<ContextMenu> contextMenu; 353 354 /** 355 * <p>Specifies the context menu to show when the user right-clicks on the tab. 356 * </p> 357 */ 358 public final void setContextMenu(ContextMenu value) { 359 contextMenuProperty().set(value); 360 } 361 362 /** 363 * The context menu associated with the tab. 364 * @return The context menu associated with the tab. 365 */ 366 public final ContextMenu getContextMenu() { 367 return contextMenu == null ? null : contextMenu.get(); 368 } 369 370 /** 371 * The context menu associated with the tab. 372 */ 373 public final ObjectProperty<ContextMenu> contextMenuProperty() { 374 if (contextMenu == null) { 375 contextMenu = new SimpleObjectProperty<ContextMenu>(this, "contextMenu"); 376 } 377 return contextMenu; 378 } 379 380 private BooleanProperty closable; 381 382 /** 383 * <p>Sets {@code true} if the tab is closable. If this is set to {@code false}, 384 * then regardless of the TabClosingPolicy, it will not be 385 * possible for the user to close this tab. Therefore, when this 386 * property is {@code false}, no 'close' button will be shown on the tab. 387 * The default is {@code true}.</p> 388 * 389 */ 390 public final void setClosable(boolean value) { 391 closableProperty().set(value); 392 } 393 394 /** 395 * Returns {@code true} if this tab is closable. 396 * 397 * @return {@code true} if the tab is closable. 398 */ 399 public final boolean isClosable() { 400 return closable == null ? true : closable.get(); 401 } 402 403 /** 404 * The closable state for this tab. 405 */ 406 public final BooleanProperty closableProperty() { 407 if (closable == null) { 408 closable = new SimpleBooleanProperty(this, "closable", true); 409 } 410 return closable; 411 } 412 413 414 /** 415 * <p>Called when the tab becomes selected or unselected.</p> 416 */ 417 public static final EventType<Event> SELECTION_CHANGED_EVENT = 418 new EventType<Event> (Event.ANY, "SELECTION_CHANGED_EVENT"); 419 private ObjectProperty<EventHandler<Event>> onSelectionChanged; 420 421 /** 422 * Defines a function to be called when a selection changed has occurred on the tab. 423 */ 424 public final void setOnSelectionChanged(EventHandler<Event> value) { 425 onSelectionChangedProperty().set(value); 426 } 427 428 /** 429 * The event handler that is associated with a selection on the tab. 430 * 431 * @return The event handler that is associated with a tab selection. 432 */ 433 public final EventHandler<Event> getOnSelectionChanged() { 434 return onSelectionChanged == null ? null : onSelectionChanged.get(); 435 } 436 437 /** 438 * The event handler that is associated with a selection on the tab. 439 */ 440 public final ObjectProperty<EventHandler<Event>> onSelectionChangedProperty() { 441 if (onSelectionChanged == null) { 442 onSelectionChanged = new ObjectPropertyBase<EventHandler<Event>>() { 443 @Override protected void invalidated() { 444 setEventHandler(SELECTION_CHANGED_EVENT, get()); 445 } 446 447 @Override 448 public Object getBean() { 449 return Tab.this; 450 } 451 452 @Override 453 public String getName() { 454 return "onSelectionChanged"; 455 } 456 }; 457 } 458 return onSelectionChanged; 459 } 460 461 /** 462 * <p>Called when a user closes this tab. This is useful for freeing up memory.</p> 463 */ 464 public static final EventType<Event> CLOSED_EVENT = new EventType<Event>(Event.ANY, "TAB_CLOSED"); 465 private ObjectProperty<EventHandler<Event>> onClosed; 466 467 /** 468 * Defines a function to be called when the tab is closed. 469 */ 470 public final void setOnClosed(EventHandler<Event> value) { 471 onClosedProperty().set(value); 472 } 473 474 /** 475 * The event handler that is associated with the tab when the tab is closed. 476 * 477 * @return The event handler that is associated with the tab when the tab is closed. 478 */ 479 public final EventHandler<Event> getOnClosed() { 480 return onClosed == null ? null : onClosed.get(); 481 } 482 483 /** 484 * The event handler that is associated with the tab when the tab is closed. 485 */ 486 public final ObjectProperty<EventHandler<Event>> onClosedProperty() { 487 if (onClosed == null) { 488 onClosed = new ObjectPropertyBase<EventHandler<Event>>() { 489 @Override protected void invalidated() { 490 setEventHandler(CLOSED_EVENT, get()); 491 } 492 493 @Override 494 public Object getBean() { 495 return Tab.this; 496 } 497 498 @Override 499 public String getName() { 500 return "onClosed"; 501 } 502 }; 503 } 504 return onClosed; 505 } 506 507 private ObjectProperty<Tooltip> tooltip; 508 509 /** 510 * <p>Specifies the tooltip to show when the user hovers over the tab.</p> 511 */ 512 public final void setTooltip(Tooltip value) { tooltipProperty().setValue(value); } 513 514 /** 515 * The tooltip associated with this tab. 516 * @return The tooltip associated with this tab. 517 */ 518 public final Tooltip getTooltip() { return tooltip == null ? null : tooltip.getValue(); } 519 520 /** 521 * The tooltip associated with this tab. 522 */ 523 public final ObjectProperty<Tooltip> tooltipProperty() { 524 if (tooltip == null) { 525 tooltip = new SimpleObjectProperty<Tooltip>(this, "tooltip"); 526 } 527 return tooltip; 528 } 529 530 private final ObservableList<String> styleClass = FXCollections.observableArrayList(); 531 532 private BooleanProperty disable; 533 534 /** 535 * Sets the disabled state of this tab. 536 * 537 * @param value the state to set this tab 538 * 539 * @defaultValue false 540 * @since 2.2 541 */ 542 public final void setDisable(boolean value) { 543 disableProperty().set(value); 544 } 545 546 /** 547 * Returns {@code true} if this tab is disable. 548 * @since 2.2 549 */ 550 public final boolean isDisable() { return disable == null ? false : disable.get(); } 551 552 /** 553 * Sets the disabled state of this tab. A disable tab is no longer interactive 554 * or traversable, but the contents remain interactive. A disable tab 555 * can be selected using {@link TabPane#getSelectionModel()}. 556 * 557 * @defaultValue false 558 * @since 2.2 559 */ 560 public final BooleanProperty disableProperty() { 561 if (disable == null) { 562 disable = new BooleanPropertyBase(false) { 563 @Override 564 protected void invalidated() { 565 updateDisabled(); 566 } 567 568 @Override 569 public Object getBean() { 570 return Tab.this; 571 } 572 573 @Override 574 public String getName() { 575 return "disable"; 576 } 577 }; 578 } 579 return disable; 580 } 581 582 private ReadOnlyBooleanWrapper disabled; 583 584 private final void setDisabled(boolean value) { 585 disabledPropertyImpl().set(value); 586 } 587 588 /** 589 * Returns true when the {@code Tab} {@link #disableProperty disable} is set to 590 * {@code true} or if the {@code TabPane} is disabled. 591 * @since 2.2 592 */ 593 public final boolean isDisabled() { 594 return disabled == null ? false : disabled.get(); 595 } 596 597 /** 598 * Indicates whether or not this {@code Tab} is disabled. A {@code Tab} 599 * will become disabled if {@link #disableProperty disable} is set to {@code true} on either 600 * itself or if the {@code TabPane} is disabled. 601 * 602 * @defaultValue false 603 * @since 2.2 604 */ 605 public final ReadOnlyBooleanProperty disabledProperty() { 606 return disabledPropertyImpl().getReadOnlyProperty(); 607 } 608 609 private ReadOnlyBooleanWrapper disabledPropertyImpl() { 610 if (disabled == null) { 611 disabled = new ReadOnlyBooleanWrapper() { 612 @Override 613 public Object getBean() { 614 return Tab.this; 615 } 616 617 @Override 618 public String getName() { 619 return "disabled"; 620 } 621 }; 622 } 623 return disabled; 624 } 625 626 private void updateDisabled() { 627 setDisabled(isDisable() || (getTabPane() != null && getTabPane().isDisabled())); 628 } 629 630 /** 631 * Called when there is an external request to close this {@code Tab}. 632 * The installed event handler can prevent tab closing by consuming the 633 * received event. 634 */ 635 public static final EventType<Event> TAB_CLOSE_REQUEST_EVENT = new EventType<Event> (Event.ANY, "TAB_CLOSE_REQUEST_EVENT"); 636 637 /** 638 * Called when there is an external request to close this {@code Tab}. 639 * The installed event handler can prevent tab closing by consuming the 640 * received event. 641 */ 642 private ObjectProperty<EventHandler<Event>> onCloseRequest; 643 public final ObjectProperty<EventHandler<Event>> onCloseRequestProperty() { 644 if (onCloseRequest == null) { 645 onCloseRequest = new ObjectPropertyBase<EventHandler<Event>>() { 646 @Override protected void invalidated() { 647 setEventHandler(TAB_CLOSE_REQUEST_EVENT, get()); 648 } 649 650 @Override public Object getBean() { 651 return Tab.this; 652 } 653 654 @Override public String getName() { 655 return "onCloseRequest"; 656 } 657 }; 658 } 659 return onCloseRequest; 660 } 661 662 public EventHandler<Event> getOnCloseRequest() { 663 if( onCloseRequest == null ) { 664 return null; 665 } 666 return onCloseRequest.get(); 667 } 668 669 public void setOnCloseRequest(EventHandler<Event> value) { 670 onCloseRequestProperty().set(value); 671 } 672 673 674 // --- Properties 675 private static final Object USER_DATA_KEY = new Object(); 676 677 // A map containing a set of properties for this Tab 678 private ObservableMap<Object, Object> properties; 679 680 /** 681 * Returns an observable map of properties on this Tab for use primarily 682 * by application developers. 683 * 684 * @return an observable map of properties on this Tab for use primarily 685 * by application developers 686 * @since 2.2 687 */ 688 public final ObservableMap<Object, Object> getProperties() { 689 if (properties == null) { 690 properties = FXCollections.observableMap(new HashMap<Object, Object>()); 691 } 692 return properties; 693 } 694 695 /** 696 * Tests if this Tab has properties. 697 * @return true if this tab has properties. 698 * @since 2.2 699 */ 700 public boolean hasProperties() { 701 return properties != null && !properties.isEmpty(); 702 } 703 704 705 // --- UserData 706 /** 707 * Convenience method for setting a single Object property that can be 708 * retrieved at a later date. This is functionally equivalent to calling 709 * the getProperties().put(Object key, Object value) method. This can later 710 * be retrieved by calling {@link Tab#getUserData()}. 711 * 712 * @param value The value to be stored - this can later be retrieved by calling 713 * {@link Tab#getUserData()}. 714 * @since 2.2 715 */ 716 public void setUserData(Object value) { 717 getProperties().put(USER_DATA_KEY, value); 718 } 719 720 /** 721 * Returns a previously set Object property, or null if no such property 722 * has been set using the {@link Tab#setUserData(java.lang.Object)} method. 723 * 724 * @return The Object that was previously set, or null if no property 725 * has been set or if null was set. 726 * @since 2.2 727 */ 728 public Object getUserData() { 729 return getProperties().get(USER_DATA_KEY); 730 } 731 732 /** 733 * A list of String identifiers which can be used to logically group 734 * Nodes, specifically for an external style engine. This variable is 735 * analogous to the "class" attribute on an HTML element and, as such, 736 * each element of the list is a style class to which this Node belongs. 737 * 738 * @see <a href="http://www.w3.org/TR/css3-selectors/#class-html">CSS3 class selectors</a> 739 */ 740 @Override 741 public ObservableList<String> getStyleClass() { 742 return styleClass; 743 } 744 745 private final EventHandlerManager eventHandlerManager = 746 new EventHandlerManager(this); 747 748 /** 749 * @treatAsPrivate implementation detail 750 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 751 */ 752 @Override 753 public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) { 754 return tail.prepend(eventHandlerManager); 755 } 756 757 /** 758 * @treatAsPrivate implementation detail 759 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 760 */ 761 protected <E extends Event> void setEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) { 762 eventHandlerManager.setEventHandler(eventType, eventHandler); 763 } 764 765 /*************************************************************************** 766 * * 767 * Stylesheet Handling * 768 * * 769 **************************************************************************/ 770 771 private static final String DEFAULT_STYLE_CLASS = "tab"; 772 773 /** 774 * {@inheritDoc} 775 * @return "Tab" 776 */ 777 @Override 778 public String getTypeSelector() { 779 return "Tab"; 780 } 781 782 /** 783 * {@inheritDoc} 784 * @return {@code getTabPane()} 785 */ 786 @Override 787 public Styleable getStyleableParent() { 788 return getTabPane(); 789 } 790 791 /** 792 * {@inheritDoc} 793 */ 794 public final ObservableSet<PseudoClass> getPseudoClassStates() { 795 return FXCollections.emptyObservableSet(); 796 } 797 798 /** 799 * {@inheritDoc} 800 */ 801 @Override 802 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 803 return getClassCssMetaData(); 804 } 805 806 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 807 return Collections.emptyList(); 808 } 809 810}