Support the Project! |
---|
This project is open-source and free to use, both commercially and non-commercially, which is why we need your help in its development. If you like it, please give it a star β on GitHub β it helps others discover the project and increases its visibility. You can also contribute, for example, by fixing bugs π or suggesting improvements π‘, see Contributing. If you can, financial support π° is always appreciated, see Support Us. Thank you! |
- Overview
- Demo
- Features
- Dependencies
- Usage
- Code building
- Running Demo
- License
- Contributing
- π Support Us
Techsenger TabPanePro is a lightweight JavaFX library that extends the standard TabPane with practical features such as custom control areas, custom tab shapes, tab drag-and-drop with edge scrolling, a tab scrollbar, and more. The library is built on top of the standard TabPaneSkin, carefully extracted from the OpenJFX project along with a minimal set of required classes.
Key features include:
- Two additional areas placed before and after all tabs, available for any custom purpose.
- A sticky area, located between the tabs and the trailing area, typically used for a New Tab button.
- Support for custom tab shapes with control over view order and gaps between tabs.
- Ability to show the tab header area even when there are no tabs.
- Support for both the standard and custom tab menus.
- Supports tab scrolling via a ScrollBar, with four CSS-configurable positions per side.
- API for programmatic tab scrolling with scroll state tracking.
- Tab drag-and-drop with edge auto-scrolling.
- A demo application showcasing all library features.
- Comprehensive documentation.
This project will be available on Maven Central in a few weeks:
<dependency>
<groupId>com.techsenger.tabpanepro</groupId>
<artifactId>tabpanepro-core</artifactId>
<version>${tabpanepro.version}</version>
</dependency>
The library provides three optional areas that can be used if needed. You can access these areas through the skin:
TabPanePro tabPane = new TabPanePro();
TabPaneProSkin skin = (TabPaneProSkin) tabPane.getSkin();
TabHeaderArea tabHeaderArea = skin.getTabHeaderArea();
StackPane firstArea = tabHeaderArea.getFirstArea(); // CSS: .tab-pane-pro > .tab-header-area > .first-area {}
StackPane stickyArea = tabHeaderArea.getStickyArea(); // CSS: .tab-pane-pro > .tab-header-area > .sticky-area {}
StackPane lastArea = tabHeaderArea.getLastArea(); // CSS: .tab-pane-pro > .tab-header-area > .last-area {}
Note that these areas will rotate when the side of the pane is set to RIGHT
, BOTTOM
, or LEFT
. However, the
library does not apply any automatic transformation or layout logic to adapt their content. This is intentional, as
there are multiple possible layout strategies, and enforcing a single approach could cause limitations or conflicts.
See demo.css
for styling examples across different sides.
The standard TabPane
hides its tab header area when there are no tabs. However, this behavior is not suitable if you
need to display the areas, which may contain various controls. To address this, use
TabHeaderArea#policyProperty()
:
tabHeaderArea.setPolicy(TabHeaderAreaPolicy.ALWAYS_VISIBLE);
The library allows you to create virtually any custom shape for tabs. To achieve this, the following properties are provided:
TabHeaderArea#tabHeaderFactoryProperty()
β holds the factory used to create customTabHeaderSkin
instances. Although this class is public, its implementation is mostly hidden. Developers are given access only to theTabHeaderContext
, which is sufficient for adding new nodes and overridinglayoutChildren()
. A key method here isTabHeaderSkin#layoutChildren(Insets)
, which allows you to use the default layout for child nodes with additional padding. For example, if you want to create a custom shape with a 5-pixel gap between it and.tab-container
, you can callskin.layoutChildren(new Insets(5));
. This will add the necessary spacing without having to rewrite CSS styles.TabHeaderArea#tabGapProperty()
β defines the spacing between tab headers. If the value is negative, the headers will overlap each other.TabHeaderArea#tabViewOrderResolverProperty()
β holds a resolver that defines the view order for eachTabHeaderSkin
.
The Tabs Menu allows quick selection of a specific tab. This menu is typically used when not all tabs are visible to
the user. The library supports both custom menus and the standard menu. To show the standard menu, use the method
TabHeaderArea#showTabsMenu(Node)
. To determine whether this menu should be shown, use
TabHeaderArea#scrollBarNeededProperty()
.
To enable scrolling of tabs using a ScrollBar
, first set the TabHeaderArea#scrollBarEnabledProperty()
property
to true
. As a result, the ScrollBar
will be added to the tab header area, but it will remain invisible by
default. It only becomes visible when the tabs do not fit and the user hovers the cursor over the header area.
The "vertical" position of the ScrollBar
can be configured using two CSS properties:
-tpp-header-position
accepts the valuesabove_tabs
andbelow_tabs
and determines whether theScrollBar
is placed above or below the tabs.-tpp-stick-to-edge
accepts the valuestrue
orfalse
and indicates whether theScrollBar
is pinned to the edge. When set to true, the tab header area's padding is ignored. When set to false, theScrollBar
's position depends on the parent's padding.
Example:
.tab-pane-pro > .tab-header-area > .tab-scroll-bar {
-tpp-header-position: above_tabs;
-tpp-stick-to-edge: false;
}
The library provides all necessary APIs for scrolling tabs and obtaining information about the current scroll state.
To scroll the tabs, use the method TabHeaderArea#scrollTabHeadersBy(double)
. To determine the scroll state, use the
three read-only properties: TabHeaderArea#headersRegionWidthProperty()
, TabHeaderArea#headersRegionOffsetProperty()
,
and TabHeaderArea#headersClipWidthProperty()
. See the demo for an example implementation.
TabPanePro supports drag-and-drop with automatic edge scrolling and flexible configuration options. Tabs can be dragged either within a single TabPane or between different TabPane instances. Below is an example configuration for two components: source and target. If the drag-and-drop operation occurs within the same TabPane, apply both source and target settings to the same instance.
To enable drag and drop, follow these steps:
- Set a shared context for all TabPane instances involved in the operation:
DragAndDropContext context = new DragAndDropContext();
sourceTabPane.setDragAndDropContext(context);
targetTabPane.setDragAndDropContext(context);
- Configure the source TabPane:
sourceTabPane.setTabDragEnabled(true);
// Optionally, restrict which tabs can be dragged using a predicate:
sourceTabPane.setTabDragPredicate(...);
// Optionally, provide a handler that's called when dragging starts:
sourceTabPane.setTabDragHandler(...);
// Optionally, provide a handler that's called when the drag operation ends:
sourceTabPane.setTabDropHandler(...);
// Retrieve the TabHeaderArea from the source skin
TabPaneProSkin sourceSkin = (TabPaneProSkin) sourceTabPane.getSkin();
TabHeaderArea sourceTabHeaderArea = sourceSkin.getTabHeaderArea();
// Provide the view of the tab being dragged β it will be shown in a popup:
sourceTabHeaderArea.setTabDragContentFactory(...);
// Set the cursor for the drag operation:
sourceTabHeaderArea.setTabDragCursor(...);
- Configure the target TabPane:
targetTabPane.setTabDropEnabled(true);
// Optionally, restrict which tabs can be dropped using a predicate:
targetTabPane.setTabDropPredicate(...);
// Retrieve the TabHeaderArea from the target skin
TabPaneProSkin targetSkin = (TabPaneProSkin) targetTabPane.getSkin();
TabHeaderArea targetTabHeaderArea = targetSkin.getTabHeaderArea();
// Set the scroll step for edge auto-scrolling:
targetTabHeaderArea.setTabDragScrollStep(...);
// Configure the drop position area either via CSS or in code.
// It's recommended to use an even width for the drop area, as its position is calculated as (width / 2).
// You can style the drop position in various ways, including adding nodes with arrow icons, etc.
TabDropPosition dropPosition = targetSkin.getTabDropPosition(); // CSS: .tab-pane-pro > .tab-header-area > .tab-drop-position {}
// If you use custom tab shapes, you may need to adjust the drop position
dropPosition.setOffset(...);
For a complete example and visual demonstration of these features, see the demo application included with the library.
To build the library use standard Git and Maven commands:
git clone https://github.com/techsenger/tabpanepro
cd tabpanepro
mvn clean install
To run the demo execute the following commands in the root of the project:
cd tabpanepro-demo
mvn javafx:run
Please note, that debugger settings are in tabpanepro-demo/pom.xml
file.
Techsenger TabPanePro is licensed under the GNU General Public License version 2, with the Classpath Exception.
We welcome all contributions. You can help by reporting bugs, suggesting improvements, or submitting pull requests with fixes and new features. If you have any questions, feel free to reach out β weβll be happy to assist you.
You can support us financially through GitHub Sponsors. Your contribution directly helps us keep our open-source projects active, improve their features, and offer ongoing support. Besides, we offer multiple sponsorship tiers, with different rewards.