Close SWT shell by clicking outside of it
Asked Answered
F

2

7

I have this JFace dialog:

setShellStyle(SWT.APPLICATION_MODAL | SWT.CLOSE);
setBlockOnOpen(false);

Is there a way to make it close by clicking somewhere outside the dialog? Maybe something like listening for a click event on the whole screen and detecting if it's outside the dialog, and then closing.

Fugger answered 26/1, 2018 at 14:32 Comment(1)
if you have an underlying gui which will always be the same, you can add a listener to that oneIntrusive
P
4

You can attach an SWT.Deactivate listener to the underlying Shell of the dialog.

To attach the listener, you could override Window::configureShell like this:

@Override
protected void configureShell(Shell shell) {
  super.configureShell(shell);
  shell.addListener(SWT.Deactivate, event -> shell.close());
}

And here a standalone SWT example to illustrate the bare mechanism:

Display display = new Display();
Shell parentShell = new Shell(display);
parentShell.setSize(500, 500);
parentShell.open();
Shell shell = new Shell(parentShell);
shell.addListener(SWT.Deactivate, event -> shell.close());
shell.setSize(300, 300);
shell.setText("Closes on Deactivate");
shell.open();
while (!parentShell.isDisposed()) {
  if (!display.readAndDispatch())
    display.sleep();
}
display.dispose();
Paderna answered 26/1, 2018 at 17:46 Comment(3)
It seems that the Deactivate event is not triggered when clicking outside the dialogFugger
I can't reproduce what you describe. Where exactly do you click outside the dialog? I've added a plain SWT example. If you click on the parentShell the child shell will be closed and if you click on a shell of a different application, the shell will be closed as well.Damicke
On touch-screens the shell gets also deactivated when a child (enabled) composite gets clicked, so this method wouldn't really work. !Pinky
M
0

With the Dialog being modal I believe that causes some challenges using the Shell of the base application to listen for MouseEvents because the Dialog intercepts them.

If you're not opposed to using an additional library you could consider using JNativeHook to listen for global mouse click events. This would allow you to listen for a click anywhere on the computer and close the dialog if the click occurred outside the dialog bounds, if that's what you're looking for.

For example:

GlobalScreen.addNativeMouseListener(new NativeMouseInputAdapter() {
    public void nativeMouseClicked(final NativeMouseEvent nativeMouseEvent) {
        display.syncExec(new Runnable() {
            public void run() {
                if (dialog.getShell() == null || dialog.getShell().isDisposed()) {
                    return;
                }
                // Close the dialog if there is a mouse click outside the bounds of the dialog
                if (!dialog.getShell().getBounds().contains(awtToSwtPoint(nativeMouseEvent.getPoint()))) {
                    dialog.close();
                }
            }
        });
    }
});

Other than that, I'm not aware of a way to listen to mouse clicks that are outside of the base application / anywhere on the screen.


Full example:

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseInputAdapter;

public class DialogCloseTest {

    private final Display display;
    private final Shell shell;

    public DialogCloseTest() {
        display = new Display();
        shell = new Shell(display);
        shell.setSize(450, 450);

        final Dialog dialog = new MyDialog(shell);
        dialog.open();

        registerNativeHook();

        GlobalScreen.addNativeMouseListener(new NativeMouseInputAdapter() {
            public void nativeMouseClicked(final NativeMouseEvent nativeMouseEvent) {
                display.syncExec(new Runnable() {
                    public void run() {
                        if (dialog.getShell() == null || dialog.getShell().isDisposed()) {
                            return;
                        }
                        // Close the dialog if there is a mouse click outside the bounds of the dialog
                        if (!dialog.getShell().getBounds().contains(awtToSwtPoint(nativeMouseEvent.getPoint()))) {
                            dialog.close();
                        }
                    }
                });
            }
        });
    }

    private org.eclipse.swt.graphics.Point awtToSwtPoint(final java.awt.Point point) {
        return new org.eclipse.swt.graphics.Point(point.x, point.y);
    }

    private static void registerNativeHook() {
        try {
            GlobalScreen.registerNativeHook();
        } catch (NativeHookException ex) {
            System.err.println("There was a problem registering the native hook.");
            System.err.println(ex.getMessage());
            System.exit(1);
        }
    }

    private static void unregisterNativeHook() {
        try {
            GlobalScreen.unregisterNativeHook();
        } catch (NativeHookException e) {
            System.err.println("There was a problem unregistering the native hook.");
            System.err.println(e.getMessage());
        }
    }

    private static class MyDialog extends Dialog {

        MyDialog(final Shell parent) {
            super(parent);
        }

        @Override
        protected void configureShell(final Shell shell) {
            super.configureShell(shell);
            setShellStyle(SWT.APPLICATION_MODAL | SWT.CLOSE);
            setBlockOnOpen(false);
        }    

    }

    public void run() {
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
        unregisterNativeHook();
    }

    public static void main(String... args) {
        new DialogCloseTest().run();
    }

}

Note: This will close the Dialog even if it is not visible (eg. if you alt-tab away), so you could add some logic to check whether the dialog is visible as well, if you would like)

Marko answered 26/1, 2018 at 18:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.