Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Williams2018-02-28 16:10:48 -0500
committerEric Williams2018-03-01 10:05:58 -0500
commit272bf0d8ebce8f12e9bb0de2c389d3a18d319afb (patch)
tree80cd8d78c74581898c96273142bed3484ab0203a
parent64ef68738d8d1ef74937fc86faea774baa48287d (diff)
downloadeclipse.platform.swt-272bf0d8ebce8f12e9bb0de2c389d3a18d319afb.tar.gz
eclipse.platform.swt-272bf0d8ebce8f12e9bb0de2c389d3a18d319afb.tar.xz
eclipse.platform.swt-272bf0d8ebce8f12e9bb0de2c389d3a18d319afb.zip
Bug 431423: [GTK3] Context menu should appear by pointer when invoked
via mouse Some context menus are positioned incorrectly when the size (number of items) of the menu changes between clicks. The cause of this bug is due to the way SWT populates its menus: often it is done asynchronously outside of SWT based on an SWT.Show listener. The result is that the menu items are added/removed just before showing, and GTK sometimes doesn't have enough time to properly adjust the size of the menu internally. Specifically, the GdkWindow of the toplevel widget (GtkWindow) for the menu lags in height. Since GTK thinks the menu is taller/shorter than it is, the menu is positioned at the wrong y-coordinate. The fix is to check if the number of menu items has changed from one pop-up to the next. If so, calculate the preferred height of the menu and resize the GdkWindow of the menu's toplevel GtkWindow to reflect this change. This way the internal sizing calculations in GTK will be accurate and the menu will be positioned correctly. The bug is fixed on GTK3.22 with X11 only, as this uses the new GTK3.22 GtkMenu API. Older GTK3 versions use the outdated GtkMenu API and fixing these cases would not be worth the effort. Wayland is not affected by this bug. Reliable steps to reproduce the issue can be found here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=431423#c19 Tested JFace/SWT menu snippets, AllNonBrowser JUnit tests, and ControlExample on GTK3.22 with X11. Change-Id: I392983438cc32b9455c53cc464626294f3ec72f9 Signed-off-by: Eric Williams <ericwill@redhat.com>
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c25
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GTK.java12
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java46
5 files changed, 85 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c
index 428d448027..7cddab1bdb 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c
@@ -13328,6 +13328,31 @@ JNIEXPORT jintLong JNICALL GTK_NATIVE(_1gtk_1widget_1get_1parent_1window)
}
#endif
+#ifndef NO__1gtk_1widget_1get_1preferred_1height
+JNIEXPORT void JNICALL GTK_NATIVE(_1gtk_1widget_1get_1preferred_1height)
+ (JNIEnv *env, jclass that, jintLong arg0, jintArray arg1, jintArray arg2)
+{
+ jint *lparg1=NULL;
+ jint *lparg2=NULL;
+ GTK_NATIVE_ENTER(env, that, _1gtk_1widget_1get_1preferred_1height_FUNC);
+ if (arg1) if ((lparg1 = (*env)->GetIntArrayElements(env, arg1, NULL)) == NULL) goto fail;
+ if (arg2) if ((lparg2 = (*env)->GetIntArrayElements(env, arg2, NULL)) == NULL) goto fail;
+/*
+ gtk_widget_get_preferred_height(arg0, lparg1, lparg2);
+*/
+ {
+ GTK_LOAD_FUNCTION(fp, gtk_widget_get_preferred_height)
+ if (fp) {
+ ((void (CALLING_CONVENTION*)(jintLong, jint *, jint *))fp)(arg0, lparg1, lparg2);
+ }
+ }
+fail:
+ if (arg2 && lparg2) (*env)->ReleaseIntArrayElements(env, arg2, lparg2, 0);
+ if (arg1 && lparg1) (*env)->ReleaseIntArrayElements(env, arg1, lparg1, 0);
+ GTK_NATIVE_EXIT(env, that, _1gtk_1widget_1get_1preferred_1height_FUNC);
+}
+#endif
+
#ifndef NO__1gtk_1widget_1get_1preferred_1height_1for_1width
JNIEXPORT void JNICALL GTK_NATIVE(_1gtk_1widget_1get_1preferred_1height_1for_1width)
(JNIEnv *env, jclass that, jintLong arg0, jint arg1, jintArray arg2, jintArray arg3)
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c
index ddb326eacf..43acdb330e 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c
@@ -1068,6 +1068,7 @@ char * GTK_nativeFunctionNames[] = {
"_1gtk_1widget_1get_1pango_1context",
"_1gtk_1widget_1get_1parent",
"_1gtk_1widget_1get_1parent_1window",
+ "_1gtk_1widget_1get_1preferred_1height",
"_1gtk_1widget_1get_1preferred_1height_1for_1width",
"_1gtk_1widget_1get_1preferred_1size",
"_1gtk_1widget_1get_1preferred_1width_1for_1height",
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h
index f8fd88f489..2038eea393 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h
@@ -1066,6 +1066,7 @@ typedef enum {
_1gtk_1widget_1get_1pango_1context_FUNC,
_1gtk_1widget_1get_1parent_FUNC,
_1gtk_1widget_1get_1parent_1window_FUNC,
+ _1gtk_1widget_1get_1preferred_1height_FUNC,
_1gtk_1widget_1get_1preferred_1height_1for_1width_FUNC,
_1gtk_1widget_1get_1preferred_1size_FUNC,
_1gtk_1widget_1get_1preferred_1width_1for_1height_FUNC,
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GTK.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GTK.java
index a343cfef7f..a324248031 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GTK.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GTK.java
@@ -1143,6 +1143,18 @@ public class GTK extends OS {
/**
* @method flags=dynamic
*/
+ public static final native void _gtk_widget_get_preferred_height(long /*int*/ widget, int[] minimum_size, int[] natural_size);
+ public static final void gtk_widget_get_preferred_height(long /*int*/ widget, int[] minimum_size, int[] natural_size) {
+ lock.lock();
+ try {
+ _gtk_widget_get_preferred_height(widget, minimum_size, natural_size);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /**
+ * @method flags=dynamic
+ */
public static final native void _gtk_widget_get_preferred_width_for_height(long /*int*/ widget, int height, int[] minimum_size, int[] natural_size);
public static final void gtk_widget_get_preferred_width_for_height(long /*int*/ widget, int height, int[] minimum_size, int[] natural_size) {
lock.lock();
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java
index d6ddbbcd35..6620fbc796 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java
@@ -45,6 +45,7 @@ public class Menu extends Widget {
Decorations parent;
long /*int*/ imItem, imSeparator, imHandle;
ImageList imageList;
+ int poppedUpCount;
/**
* Constructs a new instance of this class given its parent,
@@ -278,9 +279,11 @@ void _setVisible (boolean visible) {
}
}
}
+ verifyMenuPosition(getItemCount());
GTK.gtk_menu_popup_at_pointer (handle, eventPtr);
GDK.gdk_event_free (eventPtr);
}
+ poppedUpCount = getItemCount();
} else {
sendEvent (SWT.Hide);
}
@@ -1125,6 +1128,49 @@ void setOrientation (boolean create) {
}
/**
+ * Feature in GTK3 on X11: context menus in SWT are populated
+ * dynamically, sometimes asynchronously outside of SWT
+ * (i.e. in Platform UI). This means that items are added and
+ * removed just before the menu is shown. This method of
+ * changing the menu content can sometimes cause sizing issues
+ * internally in GTK, specifically with the height of the
+ * toplevel GdkWindow. <p>
+ *
+ * The fix is to cache the number of items popped up previously,
+ * and if the number of items in the current menu (to be popped up)
+ * is different, then:<ul>
+ * <li>get the preferred height of the menu</li>
+ * <li>set the toplevel GdkWindow to that height</li></ul>
+ *
+ * @param itemCount the current number of items in the menu, just
+ * before it's about to be shown/popped-up
+ */
+void verifyMenuPosition (int itemCount) {
+ if (GTK.GTK3 && OS.isX11()) {
+ if (itemCount != poppedUpCount && poppedUpCount != 0) {
+ int [] naturalHeight = new int [1];
+ /*
+ * We need to "show" the menu before fetching the preferred height.
+ * Note, this does not actually pop-up the menu.
+ */
+ GTK.gtk_widget_show(handle);
+ /*
+ * Menus are height-for-width only: use gtk_widget_get_preferred_height()
+ * instead of gtk_widget_get_preferred_size().
+ */
+ GTK.gtk_widget_get_preferred_height(handle, null, naturalHeight);
+ if (naturalHeight[0] > 0) {
+ long /*int*/ topLevelWidget = GTK.gtk_widget_get_toplevel(handle);
+ long /*int*/ topLevelWindow = GTK.gtk_widget_get_window(topLevelWidget);
+ int width = GDK.gdk_window_get_width(topLevelWindow);
+ GDK.gdk_window_resize(topLevelWindow, width, naturalHeight[0]);
+ }
+ }
+ }
+ return;
+}
+
+/**
* Marks the receiver as visible if the argument is <code>true</code>,
* and marks it invisible otherwise.
* <p>

Back to the top