Skip to content

Commit 8763e8b

Browse files
karthikpandeluarapte
authored andcommitted
8190411: NPE in SliderSkin:140 if Slider.Tooltip.autohide is true
Reviewed-by: arapte, angorya, kcr
1 parent dadfcaf commit 8763e8b

File tree

2 files changed

+180
-0
lines changed

2 files changed

+180
-0
lines changed

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

+22
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import javafx.scene.control.Control;
3636
import javafx.scene.control.SkinBase;
3737
import javafx.scene.control.Slider;
38+
import javafx.scene.control.Tooltip;
3839
import javafx.scene.layout.StackPane;
3940
import javafx.util.Duration;
4041
import javafx.util.StringConverter;
@@ -61,6 +62,9 @@ public class SliderSkin extends SkinBase<Slider> {
6162
private double trackToTickGap = 2;
6263

6364
private boolean showTickMarks;
65+
66+
// Used to save and restore consumeAutoHidingEvents property of the tooltip to original value.
67+
private boolean tooltipConsumeAutoHidingEvents;
6468
private double thumbWidth;
6569
private double thumbHeight;
6670

@@ -389,6 +393,24 @@ public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object...
389393
cur.getX() - dragStart.getX() : -(cur.getY() - dragStart.getY());
390394
behavior.thumbDragged(me, preDragThumbPos + dragPos / trackLength);
391395
});
396+
397+
thumb.setOnMouseEntered(me -> {
398+
Tooltip t = getSkinnable().getTooltip();
399+
if (t != null && t.isAutoHide() && !t.consumeAutoHidingEventsProperty().isBound()) {
400+
tooltipConsumeAutoHidingEvents = t.getConsumeAutoHidingEvents();
401+
// Temporarily disable consuming auto hiding events from tooltip.
402+
// This is done to receive mouse pressed event on thumb and
403+
// inturn to detect start of drag.
404+
t.setConsumeAutoHidingEvents(false);
405+
}
406+
});
407+
408+
thumb.setOnMouseExited(me -> {
409+
Tooltip t = getSkinnable().getTooltip();
410+
if (t != null && t.isAutoHide() && !t.consumeAutoHidingEventsProperty().isBound()) {
411+
t.setConsumeAutoHidingEvents(tooltipConsumeAutoHidingEvents);
412+
}
413+
});
392414
}
393415

394416
private void setShowTickMarks(boolean ticksVisible, boolean labelsVisible) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package test.robot.javafx.scene;
27+
28+
import java.util.concurrent.CountDownLatch;
29+
30+
import javafx.application.Application;
31+
import javafx.scene.input.MouseButton;
32+
import javafx.application.Platform;
33+
import javafx.scene.robot.Robot;
34+
import javafx.scene.Scene;
35+
import javafx.scene.control.Slider;
36+
import javafx.stage.Stage;
37+
import javafx.stage.StageStyle;
38+
import javafx.scene.control.Tooltip;
39+
import javafx.stage.WindowEvent;
40+
41+
import org.junit.AfterClass;
42+
import org.junit.Assert;
43+
import org.junit.BeforeClass;
44+
import org.junit.Test;
45+
46+
import test.util.Util;
47+
48+
/*
49+
* Test for verifying Slider NPE error.
50+
*
51+
* There is 1 test in this file.
52+
* Steps for testSliderTooltipNPE()
53+
* 1. Create a slider and tooltip.
54+
* 2. Make setAutoHide of tooltip as true and add tooltip to slider.
55+
* 3. Hover over slider thumb and wait for tooltip. Drag the thumb.
56+
* 4. Verify that NullPointerException is not thrown.
57+
*/
58+
59+
public class SliderTooltipNPETest {
60+
static CountDownLatch startupLatch = new CountDownLatch(1);
61+
static CountDownLatch tooltipLatch = new CountDownLatch(1);
62+
static Robot robot;
63+
static Slider slider;
64+
65+
static volatile Throwable exception;
66+
static volatile Stage stage;
67+
static volatile Scene scene;
68+
69+
static final int SCENE_WIDTH = 250;
70+
static final int SCENE_HEIGHT = SCENE_WIDTH;
71+
static final int DRAG_DISTANCE = 10;
72+
73+
@Test
74+
public void testSliderTooltipNPE() throws Throwable {
75+
Assert.assertTrue(slider.getTooltip().getConsumeAutoHidingEvents());
76+
dragSliderAfterTooltipDisplayed(DRAG_DISTANCE);
77+
if (exception != null) {
78+
exception.printStackTrace();
79+
throw exception;
80+
}
81+
Assert.assertTrue(slider.getTooltip().getConsumeAutoHidingEvents());
82+
}
83+
84+
private void dragSliderAfterTooltipDisplayed(int dragDistance) throws Exception {
85+
Thread.sleep(1000); // Wait for slider to layout
86+
87+
Util.runAndWait(() -> {
88+
robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() +
89+
slider.getLayoutX() + slider.getLayoutBounds().getWidth()/2),
90+
(int)(scene.getWindow().getY() + scene.getY() +
91+
slider.getLayoutY() + slider.getLayoutBounds().getHeight()/2));
92+
});
93+
94+
Util.waitForLatch(tooltipLatch, 5, "Timeout waiting for tooltip to display");
95+
Thread.sleep(2000); // Wait for tooltip to display
96+
97+
Util.runAndWait(() -> {
98+
robot.mousePress(MouseButton.PRIMARY);
99+
});
100+
101+
for (int i = 0; i < dragDistance; i++) {
102+
final int c = i;
103+
Util.runAndWait(() -> {
104+
robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() +
105+
slider.getLayoutX() + slider.getLayoutBounds().getWidth()/2) + c,
106+
(int)(scene.getWindow().getY() + scene.getY() +
107+
slider.getLayoutY() + slider.getLayoutBounds().getHeight()/2));
108+
});
109+
}
110+
111+
Util.runAndWait(() -> {
112+
robot.mouseRelease(MouseButton.PRIMARY);
113+
});
114+
115+
// Move the cursor away from the slider
116+
Util.runAndWait(() -> {
117+
robot.mouseMove((int)(scene.getWindow().getX() + scene.getX()),
118+
(int)(scene.getWindow().getY() + scene.getY()));
119+
});
120+
}
121+
122+
public static class TestApp extends Application {
123+
124+
@Override
125+
public void start(Stage primaryStage) {
126+
robot = new Robot();
127+
stage = primaryStage;
128+
129+
Tooltip tooltip = new Tooltip("Autohide tooltip");
130+
tooltip.setAutoHide(true);
131+
tooltip.setOnShown(event -> Platform.runLater(tooltipLatch::countDown));
132+
133+
slider = new Slider(0, 100, 50);
134+
slider.setTooltip(tooltip);
135+
136+
scene = new Scene(slider, SCENE_WIDTH, SCENE_HEIGHT);
137+
stage.setScene(scene);
138+
stage.initStyle(StageStyle.UNDECORATED);
139+
stage.setAlwaysOnTop(true);
140+
stage.setOnShown(event -> Platform.runLater(startupLatch::countDown));
141+
stage.show();
142+
143+
Thread.currentThread().setUncaughtExceptionHandler((t2, e) -> {
144+
exception = e;
145+
});
146+
}
147+
}
148+
149+
@BeforeClass
150+
public static void initFX() {
151+
Util.launch(startupLatch, TestApp.class);
152+
}
153+
154+
@AfterClass
155+
public static void exit() {
156+
Util.shutdown(stage);
157+
}
158+
}

0 commit comments

Comments
 (0)