Skip to content

Commit 5b42b64

Browse files
author
Jeanette Winzenburg
committed
8252236: TabPane: must keep header of selected tab visible
Reviewed-by: arapte, kcr
1 parent 77a183e commit 5b42b64

File tree

3 files changed

+517
-10
lines changed

3 files changed

+517
-10
lines changed

modules/javafx.controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ private enum TabAnimationState {
152152
private Rectangle clipRect;
153153
private Rectangle tabHeaderAreaClipRect;
154154
private Tab selectedTab;
155-
private boolean isSelectingTab;
156155

157156
private final TabPaneBehavior behavior;
158157

@@ -200,8 +199,14 @@ public TabPaneSkin(TabPane control) {
200199

201200
registerChangeListener(control.selectionModelProperty(), e -> updateSelectionModel());
202201
registerChangeListener(control.sideProperty(), e -> updateTabPosition());
203-
registerChangeListener(control.widthProperty(), e -> clipRect.setWidth(getSkinnable().getWidth()));
204-
registerChangeListener(control.heightProperty(), e -> clipRect.setHeight(getSkinnable().getHeight()));
202+
registerChangeListener(control.widthProperty(), e -> {
203+
tabHeaderArea.invalidateScrollOffset();
204+
clipRect.setWidth(getSkinnable().getWidth());
205+
});
206+
registerChangeListener(control.heightProperty(), e -> {
207+
tabHeaderArea.invalidateScrollOffset();
208+
clipRect.setHeight(getSkinnable().getHeight());
209+
});
205210

206211
selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
207212
// Could not find the selected tab try and get the selected tab using the selected index
@@ -214,7 +219,6 @@ public TabPaneSkin(TabPane control) {
214219
getSkinnable().getSelectionModel().selectFirst();
215220
}
216221
selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
217-
isSelectingTab = false;
218222

219223
initializeSwipeHandlers();
220224
}
@@ -432,7 +436,7 @@ public TabPaneSkin(TabPane control) {
432436

433437
private SelectionModel<Tab> selectionModel;
434438
private InvalidationListener selectionChangeListener = observable -> {
435-
isSelectingTab = true;
439+
tabHeaderArea.invalidateScrollOffset();
436440
selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
437441
getSkinnable().requestLayout();
438442
};
@@ -678,7 +682,7 @@ private void removeTabContent(Tab tab) {
678682
}
679683

680684
private void updateTabPosition() {
681-
tabHeaderArea.setScrollOffset(0.0F);
685+
tabHeaderArea.invalidateScrollOffset();
682686
getSkinnable().applyCss();
683687
getSkinnable().requestLayout();
684688
}
@@ -809,6 +813,7 @@ class TabHeaderArea extends StackPane {
809813
private boolean measureClosingTabs = false;
810814

811815
private double scrollOffset;
816+
private boolean scrollOffsetDirty = true;
812817

813818
public TabHeaderArea() {
814819
getStyleClass().setAll("tab-header-area");
@@ -842,13 +847,13 @@ public TabHeaderArea() {
842847
if (tabsFit()) {
843848
setScrollOffset(0.0);
844849
} else {
845-
if (isSelectingTab) {
850+
if (scrollOffsetDirty) {
846851
ensureSelectedTabIsVisible();
847-
} else {
848-
validateScrollOffset();
852+
scrollOffsetDirty = false;
849853
}
854+
// ensure there's no gap between last visible tab and trailing edge
855+
validateScrollOffset();
850856
}
851-
isSelectingTab = false;
852857

853858
Side tabPosition = getSkinnable().getSide();
854859
double tabBackgroundHeight = snapSize(prefHeight(-1));
@@ -981,20 +986,23 @@ private void updateHeaderClip() {
981986
private void addTab(Tab tab, int addToIndex) {
982987
TabHeaderSkin tabHeaderSkin = new TabHeaderSkin(tab);
983988
headersRegion.getChildren().add(addToIndex, tabHeaderSkin);
989+
invalidateScrollOffset();
984990
}
985991

986992
private void removeTab(Tab tab) {
987993
TabHeaderSkin tabHeaderSkin = getTabHeaderSkin(tab);
988994
if (tabHeaderSkin != null) {
989995
headersRegion.getChildren().remove(tabHeaderSkin);
990996
}
997+
invalidateScrollOffset();
991998
}
992999

9931000
private void moveTab(int moveToIndex, TabHeaderSkin tabHeaderSkin) {
9941001
if (moveToIndex != headersRegion.getChildren().indexOf(tabHeaderSkin)) {
9951002
headersRegion.getChildren().remove(tabHeaderSkin);
9961003
headersRegion.getChildren().add(moveToIndex, tabHeaderSkin);
9971004
}
1005+
invalidateScrollOffset();
9981006
}
9991007

10001008
private TabHeaderSkin getTabHeaderSkin(Tab tab) {
@@ -1053,6 +1061,10 @@ public double getScrollOffset() {
10531061
return scrollOffset;
10541062
}
10551063

1064+
public void invalidateScrollOffset() {
1065+
scrollOffsetDirty = true;
1066+
}
1067+
10561068
private void validateScrollOffset() {
10571069
setScrollOffset(getScrollOffset());
10581070
}
@@ -2241,6 +2253,7 @@ private void stopDrag() {
22412253
dragHeaderTransitionX = dragHeaderDestX - dragHeaderSourceX;
22422254
dragHeaderAnim.playFromStart();
22432255
}
2256+
tabHeaderArea.invalidateScrollOffset();
22442257
}
22452258

22462259
private void reorderTabs() {
@@ -2294,4 +2307,16 @@ void test_disableAnimations() {
22942307
closeTabAnimation.set(TabAnimation.NONE);
22952308
openTabAnimation.set(TabAnimation.NONE);
22962309
}
2310+
2311+
double test_getHeaderAreaScrollOffset() {
2312+
return tabHeaderArea.getScrollOffset();
2313+
}
2314+
2315+
void test_setHeaderAreaScrollOffset(double offset) {
2316+
tabHeaderArea.setScrollOffset(offset);
2317+
}
2318+
2319+
boolean test_isTabsFit() {
2320+
return tabHeaderArea.tabsFit();
2321+
}
22972322
}

modules/javafx.controls/src/shims/java/javafx/scene/control/skin/TabPaneSkinShim.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,20 @@ public static List<Node> getTabHeaders(TabPane tabPane) {
4646
StackPane headersRegion = (StackPane) tabPane.lookup(".headers-region");
4747
return headersRegion.getChildren();
4848
}
49+
50+
public static double getHeaderAreaScrollOffset(TabPane tabPane) {
51+
TabPaneSkin skin = (TabPaneSkin) tabPane.getSkin();
52+
return skin.test_getHeaderAreaScrollOffset();
53+
}
54+
55+
public static void setHeaderAreaScrollOffset(TabPane tabPane, double offset) {
56+
TabPaneSkin skin = (TabPaneSkin) tabPane.getSkin();
57+
skin.test_setHeaderAreaScrollOffset(offset);
58+
}
59+
60+
public static boolean isTabsFit(TabPane tabPane) {
61+
TabPaneSkin skin = (TabPaneSkin) tabPane.getSkin();
62+
return skin.test_isTabsFit();
63+
}
64+
4965
}

0 commit comments

Comments
 (0)