Reputation: 36703
I have an application which sometimes shows a modal dialog, however after receiving an external message I wish to remove that dialog and replace it with another one, as the situation has changed and the first dialog no longer applies.
However the second dialog does not repaint correctly and neither does the parent stage.
I've seen this issue on jdk-8u11-windows-x64 with controls fx controlsfx-8.0.6 and also controls-fx-8.20.8. I've managed to recreate this issue outside of my application
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import org.controlsfx.dialog.Dialog;
public class NestedEventLoop extends Application {
private Dialog firstDialog;
private Object stage;
@Override
public void start(Stage stage) throws Exception {
Button button = new Button("press");
button.setOnAction((e) -> {
firstDialog = new Dialog(stage, "dialog", false);
firstDialog.setContent("Content...");
openAnotherDialogLater();
firstDialog.show();
});
stage.setScene(new Scene(button));
stage.show();
this.stage = stage;
}
private void openAnotherDialogLater() {
Runnable openDialog = () -> {
firstDialog.hide();
Dialog anotherDialog = new Dialog(stage, "anotherDialog", false);
anotherDialog.show();
};
Executors.newScheduledThreadPool(1).schedule(() -> {
Platform.runLater(openDialog);
}, 2, TimeUnit.SECONDS);
}
public static void main(String[] args) {
launch(args);
}
}
My analysis of what is happening.
My question
Stack trace from jconsole whilst second dialog open.
com.sun.glass.ui.win.WinApplication._enterNestedEventLoopImpl(Native Method)
com.sun.glass.ui.win.WinApplication._enterNestedEventLoop(WinApplication.java:142)
com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:500)
com.sun.glass.ui.EventLoop.enter(EventLoop.java:107)
com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:542)
javafx.stage.Stage.showAndWait(Stage.java:455)
org.controlsfx.dialog.HeavyweightDialog$1.showAndWait(HeavyweightDialog.java:87)
org.controlsfx.dialog.HeavyweightDialog.show(HeavyweightDialog.java:284)
org.controlsfx.dialog.Dialog.show(Dialog.java:384)
NestedEventLoop.lambda$1(NestedEventLoop.java:37)
NestedEventLoop$$Lambda$7/7730735.run(Unknown Source)
com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:301)
com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:298)
java.security.AccessController.doPrivileged(Native Method)
com.sun.javafx.application.PlatformImpl$6.run(PlatformImpl.java:298)
com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
com.sun.glass.ui.win.WinApplication._enterNestedEventLoopImpl(Native Method)
com.sun.glass.ui.win.WinApplication._enterNestedEventLoop(WinApplication.java:142)
com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:500)
com.sun.glass.ui.EventLoop.enter(EventLoop.java:107)
com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:542)
javafx.stage.Stage.showAndWait(Stage.java:455)
org.controlsfx.dialog.HeavyweightDialog$1.showAndWait(HeavyweightDialog.java:87)
org.controlsfx.dialog.HeavyweightDialog.show(HeavyweightDialog.java:284)
org.controlsfx.dialog.Dialog.show(Dialog.java:384)
NestedEventLoop.lambda$0(NestedEventLoop.java:24)
NestedEventLoop$$Lambda$1/12269754.handle(Unknown Source)
com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
javafx.event.Event.fireEvent(Event.java:204)
javafx.scene.Node.fireEvent(Node.java:8175)
javafx.scene.control.Button.fire(Button.java:185)
com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
javafx.event.Event.fireEvent(Event.java:204)
javafx.scene.Scene$MouseHandler.process(Scene.java:3746)
javafx.scene.Scene$MouseHandler.access$1800(Scene.java:3471)
javafx.scene.Scene.impl_processMouseEvent(Scene.java:1695)
javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2486)
com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:314)
com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:243)
java.security.AccessController.doPrivileged(Native Method)
com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:345)
com.sun.glass.ui.View.handleMouseEvent(View.java:526)
com.sun.glass.ui.View.notifyMouse(View.java:898)
com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
com.sun.glass.ui.win.WinApplication.access$300(WinApplication.java:39)
com.sun.glass.ui.win.WinApplication$4$1.run(WinApplication.java:112)
java.lang.Thread.run(Thread.java:745)
Update: since writing up this question I've discovered a workaround by closing the first dialog using runLater() and then opening the new dialog with another runLater(). I'm still interested on other peoples ideas on this though.
private void openAnotherDialogLater() {
Runnable closeDialog = () -> {
firstDialog.hide();
};
Runnable openDialog = () -> {
Dialog anotherDialog = new Dialog(stage, "anotherDialog", false);
anotherDialog.show();
};
Executors.newScheduledThreadPool(1).schedule(() -> {
Platform.runLater(closeDialog);
Platform.runLater(openDialog);
}, 2, TimeUnit.SECONDS);
}
Upvotes: 1
Views: 1401
Reputation: 6537
Regardless of my comment, I suggest using the new Dialogs API in JDK 8u40. You can represent the arrival of an external message in the result type of the first dialog, and if the message arrived, open the second dialog as part of handling the first dialog's result:
enum Result { MSG_ARRIVED, ... }
Dialog<Result> firstDialog = ...;
firstDialog.showAndWait().ifPresent(res -> {
if(res == Result.MSG_ARRIVED) {
anotherDialog = ...;
anotherDialog.show();
}
});
When the message arrives, just set the result of the first dialog to MSG_ARRIVED and close the dialog:
Platform.runLater(() -> {
firstDialog.setResult(Result.MSG_ARRIVED);
firstDialog.hide();
});
Upvotes: 2