/*
 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.scene.control;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import com.sun.javafx.collections.UnmodifiableListSet;
import com.sun.javafx.scene.control.TabObservableList;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.WritableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Side;
import javafx.scene.AccessibleAttribute;
import javafx.scene.AccessibleRole;
import javafx.css.StyleableDoubleProperty;
import javafx.css.CssMetaData;
import javafx.css.PseudoClass;

import javafx.css.converter.SizeConverter;
import javafx.scene.control.skin.TabPaneSkin;

import javafx.beans.DefaultProperty;
import javafx.css.Styleable;
import javafx.css.StyleableProperty;
import javafx.scene.Node;

A control that allows switching between a group of Tabs. Only one tab is visible at a time. Tabs are added to the TabPane by using the getTabs.

Tabs in a TabPane can be positioned at any of the four sides by specifying the Side.

A TabPane has two modes floating or recessed. Applying the styleclass STYLE_CLASS_FLOATING will change the TabPane mode to floating.

The tabs width and height can be set to a specific size by setting the min and max for height and width. TabPane default width will be determined by the largest content width in the TabPane. This is the same for the height. If a different size is desired the width and height of the TabPane can be overridden by setting the min, pref and max size.

When the number of tabs do not fit the TabPane a menu button will appear on the right. The menu button is used to select the tabs that are currently not visible.

Example:


TabPane tabPane = new TabPane();
Tab tab = new Tab();
tab.setText("new tab");
tab.setContent(new Rectangle(200,200, Color.LIGHTSTEELBLUE));
tabPane.getTabs().add(tab);
See Also:
Since:JavaFX 2.0
/** * <p>A control that allows switching between a group of {@link Tab Tabs}. Only one tab * is visible at a time. Tabs are added to the TabPane by using the {@link #getTabs}.</p> * * <p>Tabs in a TabPane can be positioned at any of the four sides by specifying the * {@link Side}. </p> * * <p>A TabPane has two modes floating or recessed. Applying the styleclass STYLE_CLASS_FLOATING * will change the TabPane mode to floating.</p> * * <p>The tabs width and height can be set to a specific size by * setting the min and max for height and width. TabPane default width will be * determined by the largest content width in the TabPane. This is the same for the height. * If a different size is desired the width and height of the TabPane can * be overridden by setting the min, pref and max size.</p> * * <p>When the number of tabs do not fit the TabPane a menu button will appear on the right. * The menu button is used to select the tabs that are currently not visible. * </p> * * <p>Example:</p> * <pre><code> * TabPane tabPane = new TabPane(); * Tab tab = new Tab(); * tab.setText("new tab"); * tab.setContent(new Rectangle(200,200, Color.LIGHTSTEELBLUE)); * tabPane.getTabs().add(tab); * </code></pre> * * @see Tab * @since JavaFX 2.0 */
@DefaultProperty("tabs") public class TabPane extends Control { private static final double DEFAULT_TAB_MIN_WIDTH = 0; private static final double DEFAULT_TAB_MAX_WIDTH = Double.MAX_VALUE; private static final double DEFAULT_TAB_MIN_HEIGHT = 0; private static final double DEFAULT_TAB_MAX_HEIGHT = Double.MAX_VALUE;
TabPane mode will be changed to floating allowing the TabPane to be placed alongside other control.
/** * TabPane mode will be changed to floating allowing the TabPane * to be placed alongside other control. */
public static final String STYLE_CLASS_FLOATING = "floating";
Constructs a new TabPane.
/** * Constructs a new TabPane. */
public TabPane() { this((Tab[])null); }
Constructs a new TabPane with the given tabs set to show.
Params:
  • tabs – The tabs to display inside the TabPane.
Since:JavaFX 8u40
/** * Constructs a new TabPane with the given tabs set to show. * * @param tabs The {@link Tab tabs} to display inside the TabPane. * @since JavaFX 8u40 */
public TabPane(Tab... tabs) { getStyleClass().setAll("tab-pane"); setAccessibleRole(AccessibleRole.TAB_PANE); setSelectionModel(new TabPaneSelectionModel(this)); this.tabs.addListener((ListChangeListener<Tab>) c -> { while (c.next()) { for (Tab tab : c.getRemoved()) { if (tab != null && !getTabs().contains(tab)) { tab.setTabPane(null); } } for (Tab tab : c.getAddedSubList()) { if (tab != null) { tab.setTabPane(TabPane.this); } } } }); if (tabs != null) { getTabs().addAll(tabs); } // initialize pseudo-class state Side edge = getSide(); pseudoClassStateChanged(TOP_PSEUDOCLASS_STATE, (edge == Side.TOP)); pseudoClassStateChanged(RIGHT_PSEUDOCLASS_STATE, (edge == Side.RIGHT)); pseudoClassStateChanged(BOTTOM_PSEUDOCLASS_STATE, (edge == Side.BOTTOM)); pseudoClassStateChanged(LEFT_PSEUDOCLASS_STATE, (edge == Side.LEFT)); } private ObservableList<Tab> tabs = new TabObservableList<>(new ArrayList<>());

The tabs to display in this TabPane. Changing this ObservableList will immediately result in the TabPane updating to display the new contents of this ObservableList.

If the tabs ObservableList changes, the selected tab will remain the previously selected tab, if it remains within this ObservableList. If the previously selected tab is no longer in the tabs ObservableList, the selected tab will become the first tab in the ObservableList.

Returns:the list of tabs
/** * <p>The tabs to display in this TabPane. Changing this ObservableList will * immediately result in the TabPane updating to display the new contents * of this ObservableList.</p> * * <p>If the tabs ObservableList changes, the selected tab will remain the previously * selected tab, if it remains within this ObservableList. If the previously * selected tab is no longer in the tabs ObservableList, the selected tab will * become the first tab in the ObservableList.</p> * @return the list of tabs */
public final ObservableList<Tab> getTabs() { return tabs; } private ObjectProperty<SingleSelectionModel<Tab>> selectionModel = new SimpleObjectProperty<SingleSelectionModel<Tab>>(this, "selectionModel");

Sets the model used for tab selection. By changing the model you can alter how the tabs are selected and which tabs are first or last.

Params:
  • value – the selection model
/** * <p>Sets the model used for tab selection. By changing the model you can alter * how the tabs are selected and which tabs are first or last.</p> * @param value the selection model */
public final void setSelectionModel(SingleSelectionModel<Tab> value) { selectionModel.set(value); }

Gets the model used for tab selection.

Returns:the model used for tab selection
/** * <p>Gets the model used for tab selection.</p> * @return the model used for tab selection */
public final SingleSelectionModel<Tab> getSelectionModel() { return selectionModel.get(); }
The selection model used for selecting tabs.
Returns:selection model property
/** * The selection model used for selecting tabs. * @return selection model property */
public final ObjectProperty<SingleSelectionModel<Tab>> selectionModelProperty() { return selectionModel; } private ObjectProperty<Side> side;

The position to place the tabs in this TabPane. Whenever this changes the TabPane will immediately update the location of the tabs to reflect this.

Params:
  • value – the side
/** * <p>The position to place the tabs in this TabPane. Whenever this changes * the TabPane will immediately update the location of the tabs to reflect * this.</p> * * @param value the side */
public final void setSide(Side value) { sideProperty().set(value); }
The current position of the tabs in the TabPane. The default position for the tabs is Side.Top.
Returns:The current position of the tabs in the TabPane.
/** * The current position of the tabs in the TabPane. The default position * for the tabs is Side.Top. * * @return The current position of the tabs in the TabPane. */
public final Side getSide() { return side == null ? Side.TOP : side.get(); }
The position of the tabs in the TabPane.
Returns:the side property
/** * The position of the tabs in the TabPane. * @return the side property */
public final ObjectProperty<Side> sideProperty() { if (side == null) { side = new ObjectPropertyBase<Side>(Side.TOP) { private Side oldSide; @Override protected void invalidated() { oldSide = get(); pseudoClassStateChanged(TOP_PSEUDOCLASS_STATE, (oldSide == Side.TOP || oldSide == null)); pseudoClassStateChanged(RIGHT_PSEUDOCLASS_STATE, (oldSide == Side.RIGHT)); pseudoClassStateChanged(BOTTOM_PSEUDOCLASS_STATE, (oldSide == Side.BOTTOM)); pseudoClassStateChanged(LEFT_PSEUDOCLASS_STATE, (oldSide == Side.LEFT)); } @Override public Object getBean() { return TabPane.this; } @Override public String getName() { return "side"; } }; } return side; } private ObjectProperty<TabClosingPolicy> tabClosingPolicy;

Specifies how the TabPane handles tab closing from an end-users perspective. The options are:

  • TabClosingPolicy.UNAVAILABLE: Tabs can not be closed by the user
  • TabClosingPolicy.SELECTED_TAB: Only the currently selected tab will have the option to be closed, with a graphic next to the tab text being shown. The graphic will disappear when a tab is no longer selected.
  • TabClosingPolicy.ALL_TABS: All tabs will have the option to be closed.

Refer to the TabClosingPolicy enumeration for further details.

The default closing policy is TabClosingPolicy.SELECTED_TAB
Params:
  • value – the closing policy
/** * <p>Specifies how the TabPane handles tab closing from an end-users * perspective. The options are:</p> * * <ul> * <li> TabClosingPolicy.UNAVAILABLE: Tabs can not be closed by the user * <li> TabClosingPolicy.SELECTED_TAB: Only the currently selected tab will * have the option to be closed, with a graphic next to the tab * text being shown. The graphic will disappear when a tab is no * longer selected. * <li> TabClosingPolicy.ALL_TABS: All tabs will have the option to be * closed. * </ul> * * <p>Refer to the {@link TabClosingPolicy} enumeration for further details.</p> * * The default closing policy is TabClosingPolicy.SELECTED_TAB * @param value the closing policy */
public final void setTabClosingPolicy(TabClosingPolicy value) { tabClosingPolicyProperty().set(value); }
The closing policy for the tabs.
Returns:The closing policy for the tabs.
/** * The closing policy for the tabs. * * @return The closing policy for the tabs. */
public final TabClosingPolicy getTabClosingPolicy() { return tabClosingPolicy == null ? TabClosingPolicy.SELECTED_TAB : tabClosingPolicy.get(); }
The closing policy for the tabs.
Returns:the closing policy property
/** * The closing policy for the tabs. * @return the closing policy property */
public final ObjectProperty<TabClosingPolicy> tabClosingPolicyProperty() { if (tabClosingPolicy == null) { tabClosingPolicy = new SimpleObjectProperty<TabClosingPolicy>(this, "tabClosingPolicy", TabClosingPolicy.SELECTED_TAB); } return tabClosingPolicy; } private BooleanProperty rotateGraphic;

Specifies whether the graphic inside a Tab is rotated or not, such that it is always upright, or rotated in the same way as the Tab text is.

By default rotateGraphic is set to false, to represent the fact that the graphic isn't rotated, resulting in it always appearing upright. If rotateGraphic is set to true, the graphic will rotate such that it rotates with the tab text.

Params:
  • value – a flag indicating whether to rotate the graphic
/** * <p>Specifies whether the graphic inside a Tab is rotated or not, such * that it is always upright, or rotated in the same way as the Tab text is.</p> * * <p>By default rotateGraphic is set to false, to represent the fact that * the graphic isn't rotated, resulting in it always appearing upright. If * rotateGraphic is set to {@code true}, the graphic will rotate such that it * rotates with the tab text.</p> * * @param value a flag indicating whether to rotate the graphic */
public final void setRotateGraphic(boolean value) { rotateGraphicProperty().set(value); }
Returns true if the graphic inside a Tab is rotated. The default is false
Returns:the rotatedGraphic state.
/** * Returns {@code true} if the graphic inside a Tab is rotated. The * default is {@code false} * * @return the rotatedGraphic state. */
public final boolean isRotateGraphic() { return rotateGraphic == null ? false : rotateGraphic.get(); }
The rotateGraphic state of the tabs in the TabPane.
Returns:the rotateGraphic property
/** * The rotateGraphic state of the tabs in the TabPane. * @return the rotateGraphic property */
public final BooleanProperty rotateGraphicProperty() { if (rotateGraphic == null) { rotateGraphic = new SimpleBooleanProperty(this, "rotateGraphic", false); } return rotateGraphic; } private DoubleProperty tabMinWidth;

The minimum width of the tabs in the TabPane. This can be used to limit the length of text in tabs to prevent truncation. Setting the min equal to the max will fix the width of the tab. By default the min equals to the max. This value can also be set via CSS using -fx-tab-min-width

Params:
  • value – the minimum width of the tabs
/** * <p>The minimum width of the tabs in the TabPane. This can be used to limit * the length of text in tabs to prevent truncation. Setting the min equal * to the max will fix the width of the tab. By default the min equals to the max. * * This value can also be set via CSS using {@code -fx-tab-min-width} * * </p> * @param value the minimum width of the tabs */
public final void setTabMinWidth(double value) { tabMinWidthProperty().setValue(value); }
The minimum width of the tabs in the TabPane.
Returns:The minimum width of the tabs
/** * The minimum width of the tabs in the TabPane. * * @return The minimum width of the tabs */
public final double getTabMinWidth() { return tabMinWidth == null ? DEFAULT_TAB_MIN_WIDTH : tabMinWidth.getValue(); }
The minimum width of the tabs in the TabPane.
Returns:the minimum width property
/** * The minimum width of the tabs in the TabPane. * @return the minimum width property */
public final DoubleProperty tabMinWidthProperty() { if (tabMinWidth == null) { tabMinWidth = new StyleableDoubleProperty(DEFAULT_TAB_MIN_WIDTH) { @Override public CssMetaData<TabPane,Number> getCssMetaData() { return StyleableProperties.TAB_MIN_WIDTH; } @Override public Object getBean() { return TabPane.this; } @Override public String getName() { return "tabMinWidth"; } }; } return tabMinWidth; }

Specifies the maximum width of a tab. This can be used to limit the length of text in tabs. If the tab text is longer than the maximum width the text will be truncated. Setting the max equal to the min will fix the width of the tab. By default the min equals to the max This value can also be set via CSS using -fx-tab-max-width.

/** * <p>Specifies the maximum width of a tab. This can be used to limit * the length of text in tabs. If the tab text is longer than the maximum * width the text will be truncated. Setting the max equal * to the min will fix the width of the tab. By default the min equals to the max * * This value can also be set via CSS using {@code -fx-tab-max-width}.</p> */
private DoubleProperty tabMaxWidth; public final void setTabMaxWidth(double value) { tabMaxWidthProperty().setValue(value); }
The maximum width of the tabs in the TabPane.
Returns:The maximum width of the tabs
/** * The maximum width of the tabs in the TabPane. * * @return The maximum width of the tabs */
public final double getTabMaxWidth() { return tabMaxWidth == null ? DEFAULT_TAB_MAX_WIDTH : tabMaxWidth.getValue(); }
The maximum width of the tabs in the TabPane.
Returns:the maximum width property
/** * The maximum width of the tabs in the TabPane. * @return the maximum width property */
public final DoubleProperty tabMaxWidthProperty() { if (tabMaxWidth == null) { tabMaxWidth = new StyleableDoubleProperty(DEFAULT_TAB_MAX_WIDTH) { @Override public CssMetaData<TabPane,Number> getCssMetaData() { return StyleableProperties.TAB_MAX_WIDTH; } @Override public Object getBean() { return TabPane.this; } @Override public String getName() { return "tabMaxWidth"; } }; } return tabMaxWidth; } private DoubleProperty tabMinHeight;

The minimum height of the tabs in the TabPane. This can be used to limit the height in tabs. Setting the min equal to the max will fix the height of the tab. By default the min equals to the max. This value can also be set via CSS using -fx-tab-min-height

Params:
  • value – the minimum height of the tabs
/** * <p>The minimum height of the tabs in the TabPane. This can be used to limit * the height in tabs. Setting the min equal to the max will fix the height * of the tab. By default the min equals to the max. * * This value can also be set via CSS using {@code -fx-tab-min-height} * </p> * @param value the minimum height of the tabs */
public final void setTabMinHeight(double value) { tabMinHeightProperty().setValue(value); }
The minimum height of the tabs in the TabPane.
Returns:the minimum height of the tabs
/** * The minimum height of the tabs in the TabPane. * * @return the minimum height of the tabs */
public final double getTabMinHeight() { return tabMinHeight == null ? DEFAULT_TAB_MIN_HEIGHT : tabMinHeight.getValue(); }
The minimum height of the tab.
Returns:the minimum height property
/** * The minimum height of the tab. * @return the minimum height property */
public final DoubleProperty tabMinHeightProperty() { if (tabMinHeight == null) { tabMinHeight = new StyleableDoubleProperty(DEFAULT_TAB_MIN_HEIGHT) { @Override public CssMetaData<TabPane,Number> getCssMetaData() { return StyleableProperties.TAB_MIN_HEIGHT; } @Override public Object getBean() { return TabPane.this; } @Override public String getName() { return "tabMinHeight"; } }; } return tabMinHeight; }

The maximum height if the tabs in the TabPane. This can be used to limit the height in tabs. Setting the max equal to the min will fix the height of the tab. By default the min equals to the max. This value can also be set via CSS using -fx-tab-max-height

/** * <p>The maximum height if the tabs in the TabPane. This can be used to limit * the height in tabs. Setting the max equal to the min will fix the height * of the tab. By default the min equals to the max. * * This value can also be set via CSS using -fx-tab-max-height * </p> */
private DoubleProperty tabMaxHeight; public final void setTabMaxHeight(double value) { tabMaxHeightProperty().setValue(value); }
The maximum height of the tabs in the TabPane.
Returns:The maximum height of the tabs
/** * The maximum height of the tabs in the TabPane. * * @return The maximum height of the tabs */
public final double getTabMaxHeight() { return tabMaxHeight == null ? DEFAULT_TAB_MAX_HEIGHT : tabMaxHeight.getValue(); }

The maximum height of the tabs in the TabPane.

Returns:the maximum height of the tabs
/** * <p>The maximum height of the tabs in the TabPane.</p> * @return the maximum height of the tabs */
public final DoubleProperty tabMaxHeightProperty() { if (tabMaxHeight == null) { tabMaxHeight = new StyleableDoubleProperty(DEFAULT_TAB_MAX_HEIGHT) { @Override public CssMetaData<TabPane,Number> getCssMetaData() { return StyleableProperties.TAB_MAX_HEIGHT; } @Override public Object getBean() { return TabPane.this; } @Override public String getName() { return "tabMaxHeight"; } }; } return tabMaxHeight; }
{@inheritDoc}
/** {@inheritDoc} */
@Override protected Skin<?> createDefaultSkin() { return new TabPaneSkin(this); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public Node lookup(String selector) { Node n = super.lookup(selector); if (n == null) { for(Tab tab : tabs) { n = tab.lookup(selector); if (n != null) break; } } return n; }
{@inheritDoc}
/** {@inheritDoc} */
public Set<Node> lookupAll(String selector) { if (selector == null) return null; final List<Node> results = new ArrayList<>(); results.addAll(super.lookupAll(selector)); for(Tab tab : tabs) { results.addAll(tab.lookupAll(selector)); } return new UnmodifiableListSet<Node>(results); }
* Stylesheet Handling * *
/*************************************************************************** * * * Stylesheet Handling * * * **************************************************************************/
private static class StyleableProperties { private static final CssMetaData<TabPane,Number> TAB_MIN_WIDTH = new CssMetaData<TabPane,Number>("-fx-tab-min-width", SizeConverter.getInstance(), DEFAULT_TAB_MIN_WIDTH) { @Override public boolean isSettable(TabPane n) { return n.tabMinWidth == null || !n.tabMinWidth.isBound(); } @Override public StyleableProperty<Number> getStyleableProperty(TabPane n) { return (StyleableProperty<Number>)(WritableValue<Number>)n.tabMinWidthProperty(); } }; private static final CssMetaData<TabPane,Number> TAB_MAX_WIDTH = new CssMetaData<TabPane,Number>("-fx-tab-max-width", SizeConverter.getInstance(), DEFAULT_TAB_MAX_WIDTH) { @Override public boolean isSettable(TabPane n) { return n.tabMaxWidth == null || !n.tabMaxWidth.isBound(); } @Override public StyleableProperty<Number> getStyleableProperty(TabPane n) { return (StyleableProperty<Number>)(WritableValue<Number>)n.tabMaxWidthProperty(); } }; private static final CssMetaData<TabPane,Number> TAB_MIN_HEIGHT = new CssMetaData<TabPane,Number>("-fx-tab-min-height", SizeConverter.getInstance(), DEFAULT_TAB_MIN_HEIGHT) { @Override public boolean isSettable(TabPane n) { return n.tabMinHeight == null || !n.tabMinHeight.isBound(); } @Override public StyleableProperty<Number> getStyleableProperty(TabPane n) { return (StyleableProperty<Number>)(WritableValue<Number>)n.tabMinHeightProperty(); } }; private static final CssMetaData<TabPane,Number> TAB_MAX_HEIGHT = new CssMetaData<TabPane,Number>("-fx-tab-max-height", SizeConverter.getInstance(), DEFAULT_TAB_MAX_HEIGHT) { @Override public boolean isSettable(TabPane n) { return n.tabMaxHeight == null || !n.tabMaxHeight.isBound(); } @Override public StyleableProperty<Number> getStyleableProperty(TabPane n) { return (StyleableProperty<Number>)(WritableValue<Number>)n.tabMaxHeightProperty(); } }; private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; static { final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData()); styleables.add(TAB_MIN_WIDTH); styleables.add(TAB_MAX_WIDTH); styleables.add(TAB_MIN_HEIGHT); styleables.add(TAB_MAX_HEIGHT); STYLEABLES = Collections.unmodifiableList(styleables); } }
Returns:The CssMetaData associated with this class, which may include the CssMetaData of its superclasses.
Since:JavaFX 8.0
/** * @return The CssMetaData associated with this class, which may include the * CssMetaData of its superclasses. * @since JavaFX 8.0 */
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { return StyleableProperties.STYLEABLES; }
{@inheritDoc}
Since:JavaFX 8.0
/** * {@inheritDoc} * @since JavaFX 8.0 */
@Override public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { return getClassCssMetaData(); } private static final PseudoClass TOP_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("top"); private static final PseudoClass BOTTOM_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("bottom"); private static final PseudoClass LEFT_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("left"); private static final PseudoClass RIGHT_PSEUDOCLASS_STATE = PseudoClass.getPseudoClass("right");
* Support classes * *
/*************************************************************************** * * * Support classes * * * **************************************************************************/
static class TabPaneSelectionModel extends SingleSelectionModel<Tab> { private final TabPane tabPane; public TabPaneSelectionModel(final TabPane t) { if (t == null) { throw new NullPointerException("TabPane can not be null"); } this.tabPane = t; // watching for changes to the items list content final ListChangeListener<Tab> itemsContentObserver = c -> { while (c.next()) { for (Tab tab : c.getRemoved()) { if (tab != null && !tabPane.getTabs().contains(tab)) { if (tab.isSelected()) { tab.setSelected(false); final int tabIndex = c.getFrom(); // we always try to select the nearest, non-disabled // tab from the position of the closed tab. findNearestAvailableTab(tabIndex, true); } } } if (c.wasAdded() || c.wasRemoved()) { // The selected tab index can be out of sync with the list of tab if // we add or remove tabs before the selected tab. if (getSelectedIndex() != tabPane.getTabs().indexOf(getSelectedItem())) { clearAndSelect(tabPane.getTabs().indexOf(getSelectedItem())); } } } if (getSelectedIndex() == -1 && getSelectedItem() == null && tabPane.getTabs().size() > 0) { // we go looking for the first non-disabled tab, as opposed to // just selecting the first tab (fix for RT-36908) findNearestAvailableTab(0, true); } else if (tabPane.getTabs().isEmpty()) { clearSelection(); } }; if (this.tabPane.getTabs() != null) { this.tabPane.getTabs().addListener(itemsContentObserver); } } // API Implementation @Override public void select(int index) { if (index < 0 || (getItemCount() > 0 && index >= getItemCount()) || (index == getSelectedIndex() && getModelItem(index).isSelected())) { return; } // Unselect the old tab if (getSelectedIndex() >= 0 && getSelectedIndex() < tabPane.getTabs().size()) { tabPane.getTabs().get(getSelectedIndex()).setSelected(false); } setSelectedIndex(index); Tab tab = getModelItem(index); if (tab != null) { setSelectedItem(tab); } // Select the new tab if (getSelectedIndex() >= 0 && getSelectedIndex() < tabPane.getTabs().size()) { tabPane.getTabs().get(getSelectedIndex()).setSelected(true); } /* Does this get all the change events */ tabPane.notifyAccessibleAttributeChanged(AccessibleAttribute.FOCUS_ITEM); } @Override public void select(Tab tab) { final int itemCount = getItemCount(); for (int i = 0; i < itemCount; i++) { final Tab value = getModelItem(i); if (value != null && value.equals(tab)) { select(i); return; } } } @Override protected Tab getModelItem(int index) { final ObservableList<Tab> items = tabPane.getTabs(); if (items == null) return null; if (index < 0 || index >= items.size()) return null; return items.get(index); } @Override protected int getItemCount() { final ObservableList<Tab> items = tabPane.getTabs(); return items == null ? 0 : items.size(); } private Tab findNearestAvailableTab(int tabIndex, boolean doSelect) { // we always try to select the nearest, non-disabled // tab from the position of the closed tab. final int tabCount = getItemCount(); int i = 1; Tab bestTab = null; while (true) { // look leftwards int downPos = tabIndex - i; if (downPos >= 0) { Tab _tab = getModelItem(downPos); if (_tab != null && ! _tab.isDisable()) { bestTab = _tab; break; } } // look rightwards. We subtract one as we need // to take into account that a tab has been removed // and if we don't do this we'll miss the tab // to the right of the tab (as it has moved into // the removed tabs position). int upPos = tabIndex + i - 1; if (upPos < tabCount) { Tab _tab = getModelItem(upPos); if (_tab != null && ! _tab.isDisable()) { bestTab = _tab; break; } } if (downPos < 0 && upPos >= tabCount) { break; } i++; } if (doSelect && bestTab != null) { select(bestTab); } return bestTab; } }

This specifies how the TabPane handles tab closing from an end-users perspective. The options are:

  • TabClosingPolicy.UNAVAILABLE: Tabs can not be closed by the user
  • TabClosingPolicy.SELECTED_TAB: Only the currently selected tab will have the option to be closed, with a graphic next to the tab text being shown. The graphic will disappear when a tab is no longer selected.
  • TabClosingPolicy.ALL_TABS: All tabs will have the option to be closed.
Since:JavaFX 2.0
/** * <p>This specifies how the TabPane handles tab closing from an end-users * perspective. The options are:</p> * * <ul> * <li> TabClosingPolicy.UNAVAILABLE: Tabs can not be closed by the user * <li> TabClosingPolicy.SELECTED_TAB: Only the currently selected tab will * have the option to be closed, with a graphic next to the tab * text being shown. The graphic will disappear when a tab is no * longer selected. * <li> TabClosingPolicy.ALL_TABS: All tabs will have the option to be * closed. * </ul> * @since JavaFX 2.0 */
public enum TabClosingPolicy {
Only the currently selected tab will have the option to be closed, with a graphic next to the tab text being shown. The graphic will disappear when a tab is no longer selected.
/** * Only the currently selected tab will have the option to be closed, with a * graphic next to the tab text being shown. The graphic will disappear when * a tab is no longer selected. */
SELECTED_TAB,
All tabs will have the option to be closed.
/** * All tabs will have the option to be closed. */
ALL_TABS,
Tabs can not be closed by the user.
/** * Tabs can not be closed by the user. */
UNAVAILABLE } // TabDragPolicy // private ObjectProperty<TabDragPolicy> tabDragPolicy;
The drag policy for the tabs. The policy can be changed dynamically.
@defaultValueTabDragPolicy.FIXED
Returns:The tab drag policy property
Since:10
/** * The drag policy for the tabs. The policy can be changed dynamically. * * @defaultValue TabDragPolicy.FIXED * @return The tab drag policy property * @since 10 */
public final ObjectProperty<TabDragPolicy> tabDragPolicyProperty() { if (tabDragPolicy == null) { tabDragPolicy = new SimpleObjectProperty<TabDragPolicy>(this, "tabDragPolicy", TabDragPolicy.FIXED); } return tabDragPolicy; } public final void setTabDragPolicy(TabDragPolicy value) { tabDragPolicyProperty().set(value); } public final TabDragPolicy getTabDragPolicy() { return tabDragPolicyProperty().get(); }
This enum specifies drag policies for tabs in a TabPane.
Since:10
/** * This enum specifies drag policies for tabs in a TabPane. * * @since 10 */
public enum TabDragPolicy {
The tabs remain fixed in their positions and cannot be dragged.
/** * The tabs remain fixed in their positions and cannot be dragged. */
FIXED,
The tabs can be dragged to reorder them within the same TabPane. Users can perform the simple mouse press-drag-release gesture on a tab header to drag it to a new position. A tab can not be detached from its parent TabPane.

After a tab is reordered, the tabs list is permuted to reflect the updated order. A permutation change event is fired to indicate which tabs were reordered. This reordering is done after the mouse button is released. While a tab is being dragged, the list of tabs is unchanged.

/** * The tabs can be dragged to reorder them within the same TabPane. * Users can perform the simple mouse press-drag-release gesture on a * tab header to drag it to a new position. A tab can not be detached * from its parent TabPane. * <p>After a tab is reordered, the {@link #getTabs() tabs} list is * permuted to reflect the updated order. * A {@link javafx.collections.ListChangeListener.Change permutation * change} event is fired to indicate which tabs were reordered. This * reordering is done after the mouse button is released. While a tab * is being dragged, the list of tabs is unchanged.</p> */
REORDER } }