Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXi Yan2018-12-03 18:12:24 +0000
committerXi Yan2018-12-06 16:07:08 +0000
commit3615a6bf1b910747186515b2526504f91dbe4dae (patch)
treed8718902ab8f24991af400b51f10385bcc5abc52
parenteae67768d5e042f65ed4a0815555badcc1de4e7e (diff)
downloadeclipse.platform.swt-3615a6bf1b910747186515b2526504f91dbe4dae.tar.gz
eclipse.platform.swt-3615a6bf1b910747186515b2526504f91dbe4dae.tar.xz
eclipse.platform.swt-3615a6bf1b910747186515b2526504f91dbe4dae.zip
Bug 541854 - [Wayland] Ctrl+1 popup issues
Attempting to address all popup related issue (see bug 542104). 1) Patch to bug 515773 ungrabbed focus when shell is hidden in setVisible(false) and used showing/hiding hack to avoid being stuck with no focus, which introduced some of the related issues. Rewrote logic to ungrab focus after shell is disposed. - Removed showing/hiding hack that uses callback to show shell in bringToTop(). - Addressed different cases for single popup and popups attached to a parent popup. - Added additional guard to ungrab the grabbed focus.. 2) Patch to bug 540166 uses callback connected to gtk destroy signal which causes crash in Flatpak due to invalid child handle passed to the callback. Workaround is to move the logic to SWT level by keeping track of the child popup shell of a parent popup shell, and dispose the child popup whenever the parent popup shell is disposed. Known limitation: - For the auto-completion, if you switch focus to the details page using Tab, clicking on an item on the list page will close the auto-completion popup instead of highlighting the list item. This is because we ungrab the focus whenever user clicks outside the focused popup window (in this case its the details page) to avoid being stuck without focus. This means we can only switch between list item using the mouse when the details page is not in focus (i.e. hit Esc first to bring focus back to the list item before clicking list item). - However, we should not fix this with the show/hide hack because with the show/hide hack, hitting Esc does not work when focus is switched to the details page using Tab. See bug 542104 comment #2 for test cases. Change-Id: I4ba90afdf9664e6858aa3c28cf51825f85171a76 Signed-off-by: Xi Yan <xixiyan@redhat.com>
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java2
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java128
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;
-}
}

Back to the top