Skip to content

Commit ee52d14

Browse files
Marius HanlJeanette Winzenburg
authored andcommitted
8277122: SplitPane divider drag can hang the layout
Reviewed-by: fastegal, aghaisas
1 parent 78d9227 commit ee52d14

File tree

2 files changed

+83
-8
lines changed

2 files changed

+83
-8
lines changed

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,6 @@
3636
import javafx.geometry.VPos;
3737
import javafx.scene.Cursor;
3838
import javafx.scene.Node;
39-
import javafx.scene.control.Accordion;
4039
import javafx.scene.control.Control;
4140
import javafx.scene.control.SkinBase;
4241
import javafx.scene.control.SplitPane;
@@ -65,6 +64,13 @@ public class SplitPaneSkin extends SkinBase<SplitPane> {
6564
private ObservableList<Content> contentRegions;
6665
private ObservableList<ContentDivider> contentDividers;
6766
private boolean horizontal;
67+
/**
68+
* Flag which is used to determine whether we need to request layout when a divider position changed or not.
69+
* E.g. We don't want to request layout when we are changing the divider position in
70+
* {@link #layoutChildren(double, double, double, double)} since we are currently doing the layout.
71+
* See also: JDK-8277122
72+
*/
73+
private boolean duringLayout;
6874

6975

7076

@@ -216,8 +222,8 @@ public SplitPaneSkin(final SplitPane control) {
216222
previousSize = horizontal ? sw : sh;
217223
}
218224

219-
// If the window is less than the min size we want to resize
220-
// proportionally
225+
duringLayout = true;
226+
// If the window is less than the min size we want to resize proportionally
221227
double minSize = totalMinSize();
222228
if (minSize > (horizontal ? w : h)) {
223229
double percentage = 0;
@@ -235,6 +241,7 @@ public SplitPaneSkin(final SplitPane control) {
235241
setupContentAndDividerForLayout();
236242
layoutDividersAndContent(w, h);
237243
resize = false;
244+
duringLayout = false;
238245
return;
239246
}
240247

@@ -411,6 +418,7 @@ public SplitPaneSkin(final SplitPane control) {
411418
}
412419

413420
layoutDividersAndContent(w, h);
421+
duringLayout = false;
414422
resize = false;
415423
}
416424

@@ -617,7 +625,7 @@ private void addDivider(SplitPane.Divider d) {
617625
ChangeListener<Number> posPropertyListener = new PosPropertyListener(c);
618626
c.setPosPropertyListener(posPropertyListener);
619627
d.positionProperty().addListener(posPropertyListener);
620-
initializeDivderEventHandlers(c);
628+
initializeDividerEventHandlers(c);
621629
contentDividers.add(c);
622630
getChildren().add(c);
623631
}
@@ -633,7 +641,7 @@ private void removeAllDividers() {
633641
lastDividerUpdate = 0;
634642
}
635643

636-
private void initializeDivderEventHandlers(final ContentDivider divider) {
644+
private void initializeDividerEventHandlers(final ContentDivider divider) {
637645
// TODO: do we need to consume all mouse events?
638646
// they only bubble to the skin which consumes them by default
639647
divider.addEventHandler(MouseEvent.ANY, event -> {
@@ -954,7 +962,9 @@ public PosPropertyListener(ContentDivider divider) {
954962
// When checking is enforced, we know that the position was set explicitly
955963
divider.posExplicit = true;
956964
}
957-
getSkinnable().requestLayout();
965+
if (!duringLayout) {
966+
getSkinnable().requestLayout();
967+
}
958968
}
959969
}
960970

modules/javafx.controls/src/test/java/test/javafx/scene/control/SplitPaneTest.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,11 @@
2525

2626
package test.javafx.scene.control;
2727

28+
import javafx.collections.FXCollections;
29+
import javafx.scene.control.ComboBox;
30+
import javafx.scene.control.Tab;
31+
import javafx.scene.control.TabPane;
32+
import org.junit.After;
2833
import test.com.sun.javafx.scene.control.infrastructure.StageLoader;
2934
import javafx.css.CssMetaData;
3035
import static test.com.sun.javafx.scene.control.infrastructure.ControlTestUtils.*;
@@ -53,6 +58,8 @@
5358
import org.junit.Before;
5459
import org.junit.Test;
5560

61+
import java.util.concurrent.atomic.AtomicInteger;
62+
5663
/**
5764
*
5865
* @author srikalyc
@@ -65,6 +72,7 @@ public class SplitPaneTest {
6572
private Scene scene;
6673
private Stage stage;
6774
private StackPane root;
75+
private StageLoader stageLoader;
6876

6977
@Before public void setup() {
7078
tk = (StubToolkit)Toolkit.getToolkit();//This step is not needed (Just to make sure StubToolkit is loaded into VM)
@@ -79,6 +87,11 @@ public class SplitPaneTest {
7987
stage.setScene(scene);
8088
}
8189

90+
@After
91+
public void cleanup() {
92+
if (stageLoader != null) stageLoader.dispose();
93+
}
94+
8295
/*********************************************************************
8396
* Helper methods (NOTE TESTS) *
8497
********************************************************************/
@@ -1329,4 +1342,56 @@ private double convertDividerPostionToAbsolutePostion(double pos, double edge) {
13291342

13301343
sl.dispose();
13311344
}
1345+
1346+
/**
1347+
* Verifies that a divider position change of the {@link SplitPane} does not hang the layout.
1348+
* Previously, this may happen when the divider position changed to a large number (>1),
1349+
* which can hang the layout as it resulted in multiple layout requests (through SplitPaneSkin.layoutChildren).
1350+
* See also: JDK-8277122
1351+
*/
1352+
@Test
1353+
public void testDividerOverOneDoesNotHangLayout() {
1354+
testSetDividerPositionDoesNotHangLayout(10);
1355+
}
1356+
1357+
/**
1358+
* Verifies that a divider position change of the {@link SplitPane} does not hang the layout.
1359+
* Previously, this may happen when the divider position changed to a negative number (<1),
1360+
* which can hang the layout as it resulted in multiple layout requests (through SplitPaneSkin.layoutChildren).
1361+
* See also: JDK-8277122
1362+
*/
1363+
@Test
1364+
public void testDividerUnderZeroDoesNotHangLayout() {
1365+
testSetDividerPositionDoesNotHangLayout(-1);
1366+
}
1367+
1368+
private void testSetDividerPositionDoesNotHangLayout(double dividerPosition) {
1369+
AtomicInteger layoutCounter = new AtomicInteger();
1370+
ComboBox<String> cbx = new ComboBox<>(FXCollections.observableArrayList("1", "2", "3")) {
1371+
@Override
1372+
protected void layoutChildren() {
1373+
layoutCounter.incrementAndGet();
1374+
super.layoutChildren();
1375+
}
1376+
};
1377+
SplitPane pane = new SplitPane(new Label("AAAAA"), new TabPane(new Tab("Test", cbx)));
1378+
StackPane root = new StackPane(pane);
1379+
1380+
stageLoader = new StageLoader(root);
1381+
1382+
Toolkit.getToolkit().firePulse();
1383+
1384+
pane.setDividerPosition(0, dividerPosition);
1385+
1386+
Toolkit.getToolkit().firePulse();
1387+
1388+
// Reset layout counter
1389+
layoutCounter.set(0);
1390+
1391+
cbx.getSelectionModel().select(0);
1392+
Toolkit.getToolkit().firePulse();
1393+
1394+
assertTrue(layoutCounter.get() > 0);
1395+
}
1396+
13321397
}

0 commit comments

Comments
 (0)