diff options
-rw-r--r-- | bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java | 2 | ||||
-rw-r--r-- | bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java | 128 |
2 files changed, 57 insertions, 73 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java index a71af47b72..f58399f729 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java @@ -89,7 +89,7 @@ public class Composite extends Scrollable { static final String NO_INPUT_METHOD = "org.eclipse.swt.internal.gtk.noInputMethod"; //$NON-NLS-1$ - boolean hasPopupChild; // Wayland only, see Shell#bringToTop() + Shell popupChild; Composite () { /* Do nothing */ diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java index 4de6cc3480..beddf30376 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java @@ -129,14 +129,12 @@ public class Shell extends Decorations { Control lastActive; ToolTip [] toolTips; boolean ignoreFocusOut, ignoreFocusIn; - boolean ignoreFocusOutAfterGrab; + boolean ignoreFocusOutAfterGrab, grabbedFocus; Region originalRegion; static final int MAXIMUM_TRIM = 128; static final int BORDER = 3; - static Callback gdkSeatGrabCallback, parentDestroyedCallback; // Wayland only. - /** * Constructs a new instance of this class. This is equivalent * to calling <code>Shell((Display) null)</code>. @@ -596,28 +594,15 @@ void bringToTop (boolean force) { if (GTK.GTK_VERSION >= OS.VERSION(3, 20, 0)) { GTK.gtk_grab_add(shellHandle); long /*int*/ seat = GDK.gdk_display_get_default_seat(GDK.gdk_window_get_display(window)); + GDK.gdk_window_show(window); + GDK.gdk_seat_grab(seat, window, GDK.GDK_SEAT_CAPABILITY_ALL, true, 0, 0, 0, 0); /* - * NOTE: Using gdk_seat_grab to get the keyboard focus needs to be handled differently - * for cases with a single popup, and for cases with multiple popup - * 1) For normal popups without a child popup (i.e. F2 Javadoc), showing GdkWindow and - * grabbing focus does the job - * 2) For popups with a child popup attached to it (i.e. auto-completion), using (1) - * makes pressing Tab close the popup instead of changing focus. - * Current best workaround is to hide the immediate popup mapped to a top level window - * before grabbing, and show it using gdkSeatGrabPrepareFunc callback. + * Bug 541185: Hover over to open Javadoc popup will make the popup + * close instead of gaining focus due to an extra focus out signal sent + * after grabbing focus. This triggers SWT.Deactivate handler which closes the shell. + * Workaround is to ignore this focus out. */ - if (!hasPopupChild) { - GDK.gdk_window_show(window); - GDK.gdk_seat_grab(seat, window, GDK.GDK_SEAT_CAPABILITY_KEYBOARD, true, 0, 0, 0, 0); - } else { - if (gdkSeatGrabCallback == null) { - gdkSeatGrabCallback = new Callback(Shell.class, "GdkSeatGrabPrepareFunc", 3); //$NON-NLS-1$ - } - long /*int*/ gdkSeatGrabPrepareFunc = gdkSeatGrabCallback.getAddress(); - if (gdkSeatGrabPrepareFunc == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); - if (GTK.gtk_widget_get_visible(shellHandle) && !isMappedToPopup()) GTK.gtk_widget_hide(shellHandle); - GDK.gdk_seat_grab(seat, window, GDK.GDK_SEAT_CAPABILITY_KEYBOARD, true, 0, 0, gdkSeatGrabPrepareFunc, shellHandle); - } + grabbedFocus = true; ignoreFocusOutAfterGrab = true; } } @@ -747,12 +732,7 @@ void createHandle (int index) { GTK.gtk_window_set_attached_to (shellHandle, parent.topHandle()); // implements the gtk_window_set_destroy_with_parent for the *logical* parent if (parent != topLevelParent && isMappedToPopup()) { - if (parentDestroyedCallback == null) { - parentDestroyedCallback = new Callback(Shell.class, "ParentDestroyedCallbackFunc", 2); - } - long /*int*/ parentDestroyedFunc = parentDestroyedCallback.getAddress(); - OS.g_signal_connect(parent.topHandle(), OS.destroy, parentDestroyedFunc, shellHandle); - parent.hasPopupChild = true; + parent.popupChild = this; } } else { GTK.gtk_window_set_transient_for (shellHandle, parent.topHandle ()); @@ -1379,13 +1359,10 @@ long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event) { * Feature in GTK: This handles ungrabbing the keyboard focus from a SWT.ON_TOP window * if it has editable fields and is running Wayland. Refer to bug 515773. */ - if (!OS.isX11() && GTK.GTK_VERSION >= OS.VERSION(3, 20, 0)) { - if ((style & SWT.ON_TOP) != 0 && (style & SWT.NO_FOCUS) == 0) { - long /*int*/ seat = GDK.gdk_event_get_seat(event); - GDK.gdk_seat_ungrab(seat); - GTK.gtk_grab_remove(shellHandle); - GTK.gtk_widget_hide(shellHandle); - } + if (requiresUngrab()) { + long /*int*/ seat = GDK.gdk_event_get_seat(event); + GDK.gdk_seat_ungrab(seat); + GTK.gtk_grab_remove(shellHandle); } return 0; } @@ -2620,22 +2597,7 @@ public void setVisible (boolean visible) { } } else { fixActiveShell (); - // Feature in Wayland: If the shell item is ON_TOP, remove its grab before hiding it, otherwise focus is locked to - // the hidden widget and can never be returned. - if (!OS.isX11() && GTK.GTK_VERSION >= OS.VERSION(3, 20, 0)) { - if ((style & SWT.ON_TOP) != 0 && (style & SWT.NO_FOCUS) == 0) { - long /*int*/ seat; - if (GTK.GTK4) { - long /*int*/ surface = gtk_widget_get_surface (shellHandle); - seat = GDK.gdk_display_get_default_seat(GDK.gdk_surface_get_display(surface)); - } else { - long /*int*/ window = gtk_widget_get_window (shellHandle); - seat = GDK.gdk_display_get_default_seat(GDK.gdk_window_get_display(window)); - } - GDK.gdk_seat_ungrab(seat); - GTK.gtk_grab_remove(shellHandle); - } - } + checkAndUnrabFocus(); GTK.gtk_widget_hide (shellHandle); sendEvent (SWT.Hide); } @@ -2877,6 +2839,41 @@ void deregister () { } } +boolean requiresUngrab () { + return !OS.isX11() && GTK.GTK_VERSION >= OS.VERSION(3, 20, 0) && (style & SWT.ON_TOP) != 0 && (style & SWT.NO_FOCUS) == 0; +} + +/** + * SWT.ON_TOP shells on Wayland requires gdk_seat_grab to grab keyboard/input focus, + * the grabbed focus need to be removed when Shell is disposed/hidden. + */ +void checkAndUnrabFocus () { + /* + * Bug 515773, 542104: Wayland POPUP window limitations + * In bringToTop(), we grabbed keyboard/pointer focus to popup shell, which needs to + * be ungrabbed when the Shell is disposed. There are two cases here: + * + * 1) If the popup shell is a regular popup window attached to a toplevel window, + * like Javadoc popup, we can just ungrab the window. + * + * 2) If the popup shell is attached to another popup, like auto-completion details, + * ungrabbing the focus will also remove the grab for its parent. (To see this, + * focus on the completion details page using Tab and hit Esc makes the parent + * completion list disposed as well.) To deal with this, we should not ungrab focus + * for child popup shells so that focus is restored to the parent popup when the child + * is disposed. + * **NOTE**: Not ungrabbing focus for child popup restores focus to parent popup + * assumes that GdkSeat are the same for parent and child, which seems to be the case. + */ + if (requiresUngrab() && !isMappedToPopup() && grabbedFocus) { + long /*int*/ window = gtk_widget_get_window (shellHandle); + long /*int*/ seat = GDK.gdk_display_get_default_seat(GDK.gdk_window_get_display(window)); + GDK.gdk_seat_ungrab(seat); + GTK.gtk_grab_remove(shellHandle); + grabbedFocus = false; + } +} + @Override public void dispose () { /* @@ -2885,13 +2882,13 @@ public void dispose () { */ if (isDisposed()) return; fixActiveShell (); - if (!OS.isX11() && GTK.GTK_VERSION >= OS.VERSION(3, 20, 0)) { - if ((style & SWT.ON_TOP) != 0 && (style & SWT.NO_FOCUS) == 0) { - long /*int*/ window = gtk_widget_get_window (shellHandle); - long /*int*/ seat = GDK.gdk_display_get_default_seat(GDK.gdk_window_get_display(window)); - GDK.gdk_seat_ungrab(seat); - GTK.gtk_grab_remove(shellHandle); - } + checkAndUnrabFocus(); + /* + * Bug 540166: Dispose the popup child if any when the parent is disposed so that + * it does not remain open forever. + */ + if (popupChild != null && popupChild.shellHandle != 0 && !popupChild.isDisposed()) { + popupChild.dispose(); } GTK.gtk_widget_hide (shellHandle); super.dispose (); @@ -3034,17 +3031,4 @@ Point getSurfaceOrigin () { return super.getSurfaceOrigin( ); } -static long /*int*/ ParentDestroyedCallbackFunc (long /*int*/ parent, long /*int*/ child) { - if (child != 0 && GTK.GTK_IS_WINDOW(child)) { - GTK.gtk_widget_destroy(child); - } - return 0; -} - -static long /*int*/ GdkSeatGrabPrepareFunc (long /*int*/ gdkSeat, long /*int*/ gdkWindow, long /*int*/ userData_shellHandle) { - if (userData_shellHandle != 0) { - GTK.gtk_widget_show(userData_shellHandle); - } - return 0; -} } |