Skip to content

Commit 1d670f1

Browse files
FlorianKirmaierkevinrushforth
authored andcommitted
8200224: Multiple press event when JFXPanel gains focus
Reviewed-by: kcr, psadhukhan
1 parent 83eb0a7 commit 1d670f1

File tree

2 files changed

+146
-7
lines changed

2 files changed

+146
-7
lines changed

modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -446,13 +446,11 @@ protected void processMouseEvent(MouseEvent e) {
446446
(e.getButton() == MouseEvent.BUTTON1)) {
447447
if (isFocusable() && !hasFocus()) {
448448
requestFocus();
449-
// this focus request event goes to eventqueue and will be
450-
// asynchronously handled so MOUSE_PRESSED event will not be
451-
// honoured by FX immediately due to lack of focus in fx
452-
// component. Fire the same MOUSE_PRESSED event after
453-
// requestFocus() so that 2nd mouse press will be honoured
454-
// since now fx have focus
455-
jfxPanelIOP.postEvent(this, e);
449+
// The extra simulated mouse pressed event is removed by making the JavaFX scene focused.
450+
// It is safe, because in JavaFX only the method "setFocused(true)" is called,
451+
// which doesn't have any side-effects when called multiple times.
452+
int focusCause = AbstractEvents.FOCUSEVENT_ACTIVATED;
453+
stagePeer.setFocused(true, focusCause);
456454
}
457455
}
458456

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright (c) 2019, 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+
package test.javafx.embed.swing;
26+
27+
import com.sun.javafx.PlatformUtil;
28+
import org.junit.Assume;
29+
import org.junit.Assert;
30+
import org.junit.BeforeClass;
31+
import org.junit.AfterClass;
32+
import org.junit.Test;
33+
import junit.framework.AssertionFailedError;
34+
35+
import javax.swing.JFrame;
36+
import javax.swing.JMenu;
37+
import javax.swing.JMenuItem;
38+
import javax.swing.JMenuBar;
39+
import javax.swing.JPanel;
40+
import javax.swing.SwingUtilities;
41+
import javafx.application.Application;
42+
import javafx.application.Platform;
43+
import javafx.embed.swing.JFXPanel;
44+
import javafx.stage.Stage;
45+
import javafx.scene.Group;
46+
import javafx.scene.Scene;
47+
import java.awt.Dimension;
48+
import java.awt.event.InputEvent;
49+
import java.awt.event.MouseEvent;
50+
import java.util.concurrent.CountDownLatch;
51+
import java.util.concurrent.TimeUnit;
52+
53+
public class JFXPanelTest {
54+
// Used to launch the application before running any test
55+
private static final CountDownLatch launchLatch = new CountDownLatch(1);
56+
57+
// Application class. An instance is created and initialized before running
58+
// the first test, and it lives through the execution of all tests.
59+
public static class MyApp extends Application {
60+
@Override
61+
public void start(Stage primaryStage) throws Exception {
62+
Platform.setImplicitExit(false);
63+
Assert.assertTrue(Platform.isFxApplicationThread());
64+
Assert.assertNotNull(primaryStage);
65+
66+
launchLatch.countDown();
67+
}
68+
}
69+
70+
@BeforeClass
71+
public static void doSetupOnce() throws Exception {
72+
// Start the Application
73+
new Thread(() -> Application.launch(MyApp.class, (String[]) null)).start();
74+
75+
if (!launchLatch.await(5000, TimeUnit.MILLISECONDS)) {
76+
throw new AssertionFailedError("Timeout waiting for Application to launch");
77+
}
78+
79+
Assert.assertEquals(0, launchLatch.getCount());
80+
}
81+
82+
@AfterClass
83+
public static void doTeardownOnce() {
84+
Platform.exit();
85+
}
86+
87+
static class TestFXPanel extends JFXPanel {
88+
protected void processMouseEventPublic(MouseEvent e) {
89+
processMouseEvent(e);
90+
}
91+
};
92+
93+
@Test
94+
public void testNoDoubleClickOnFirstClick() throws Exception {
95+
96+
CountDownLatch firstPressedEventLatch = new CountDownLatch(1);
97+
98+
// It's an array, so we can mutate it inside of lambda statement
99+
int[] pressedEventCounter = {0};
100+
101+
SwingUtilities.invokeLater(() -> {
102+
TestFXPanel dummyFXPanel = new TestFXPanel();
103+
dummyFXPanel.setPreferredSize(new Dimension(100, 100));
104+
TestFXPanel fxPnl = new TestFXPanel();
105+
fxPnl.setPreferredSize(new Dimension(100, 100));
106+
JFrame jframe = new JFrame();
107+
JPanel jpanel = new JPanel();
108+
jpanel.add(dummyFXPanel);
109+
jpanel.add(fxPnl);
110+
jframe.setContentPane(jpanel);
111+
jframe.pack();
112+
jframe.setVisible(true);
113+
114+
Platform.runLater(() -> {
115+
Scene dummyScene = new Scene(new Group());
116+
dummyFXPanel.setScene(dummyScene);
117+
Scene scene = new Scene(new Group());
118+
fxPnl.setScene(scene);
119+
120+
scene.addEventHandler(javafx.scene.input.MouseEvent.MOUSE_PRESSED, (event -> {
121+
pressedEventCounter[0] += 1;
122+
firstPressedEventLatch.countDown();
123+
}));
124+
125+
SwingUtilities.invokeLater(() -> {
126+
MouseEvent e = new MouseEvent(fxPnl, MouseEvent.MOUSE_PRESSED, 0, MouseEvent.BUTTON1_DOWN_MASK,
127+
5, 5, 1, false, MouseEvent.BUTTON1);
128+
129+
fxPnl.processMouseEventPublic(e);
130+
});
131+
});
132+
});
133+
134+
Assert.assertTrue(firstPressedEventLatch.await(5000, TimeUnit.MILLISECONDS));
135+
136+
Thread.sleep(500); // there should be no pressed event after the initial one. Let's wait for 0.5s and check again.
137+
138+
Assert.assertEquals(1, pressedEventCounter[0]);
139+
}
140+
}
141+

0 commit comments

Comments
 (0)