diff options
author | Eric Williams | 2018-03-28 19:55:38 +0000 |
---|---|---|
committer | Eric Williams | 2018-04-16 19:27:06 +0000 |
commit | 7ac153d75bbe583d855662ea077c9ab158c18169 (patch) | |
tree | d5e38fb2fcb5f64690151bcf1dd70f85a5c39b4d | |
parent | 802688f6202feab16b46f81f6e6b893c37341a30 (diff) | |
download | eclipse.platform.swt-7ac153d75bbe583d855662ea077c9ab158c18169.tar.gz eclipse.platform.swt-7ac153d75bbe583d855662ea077c9ab158c18169.tar.xz eclipse.platform.swt-7ac153d75bbe583d855662ea077c9ab158c18169.zip |
Bug 529431: [GTK3.10+] Snippet294 fails to draw Region
Fix input handling for GTK3.10+ setRegion behaviour. This does a manual
check to see if the incoming event coordinates are within the set
region: if they are, ignore them. This prevents events like
SWT.Selection, SWT.Mouse[Up/Down], etc. from triggering when they are
spawned from within the set region.
There are two main limitations though:
1) we cannot stop native GTK events as we are doing the region drawing
on the SWT level. This means things like mouse enter/leave, button
pre-light, etc. will still be visible. We block the selection/click
events at the SWT level, but unfortunately there is no reliable way to
block these on the GTK level.
2) we also can't block all selection events, as some widgets are
selectable using the keyboard (which has no coordinates). This means we
could potentially block out selection events that *should* be sent.
Tested on GTK3.22, no AllNonBrowser JUnit test failures.
Change-Id: Ifd8377feb7d71a366edcdf6083d2bc94ab779886
Signed-off-by: Eric Williams <ericwill@redhat.com>
10 files changed, 97 insertions, 4 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c index f79d487dba..e8d3ce9784 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c @@ -855,6 +855,26 @@ JNIEXPORT jintLong JNICALL Cairo_NATIVE(_1cairo_1reference) } #endif +#ifndef NO__1cairo_1region_1contains_1point +JNIEXPORT jboolean JNICALL Cairo_NATIVE(_1cairo_1region_1contains_1point) + (JNIEnv *env, jclass that, jintLong arg0, jint arg1, jint arg2) +{ + jboolean rc = 0; + Cairo_NATIVE_ENTER(env, that, _1cairo_1region_1contains_1point_FUNC); +/* + rc = (jboolean)cairo_region_contains_point(arg0, arg1, arg2); +*/ + { + Cairo_LOAD_FUNCTION(fp, cairo_region_contains_point) + if (fp) { + rc = (jboolean)((jboolean (CALLING_CONVENTION*)(jintLong, jint, jint))fp)(arg0, arg1, arg2); + } + } + Cairo_NATIVE_EXIT(env, that, _1cairo_1region_1contains_1point_FUNC); + return rc; +} +#endif + #ifndef NO__1cairo_1region_1get_1rectangle JNIEXPORT void JNICALL Cairo_NATIVE(_1cairo_1region_1get_1rectangle) (JNIEnv *env, jclass that, jintLong arg0, jint arg1, jintLong arg2) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h index a164b42b53..43082af563 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h @@ -31,6 +31,7 @@ #define cairo_pdf_surface_set_size_LIB LIB_CAIRO #define cairo_ps_surface_set_size_LIB LIB_CAIRO #define cairo_region_num_rectangles_LIB LIB_CAIRO +#define cairo_region_contains_point_LIB LIB_CAIRO #define cairo_region_get_rectangle_LIB LIB_CAIRO #ifdef CAIRO_HAS_XLIB_SURFACE diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c index efd463895f..0511e14bde 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c @@ -96,6 +96,7 @@ char * Cairo_nativeFunctionNames[] = { "_1cairo_1push_1group", "_1cairo_1rectangle", "_1cairo_1reference", + "_1cairo_1region_1contains_1point", "_1cairo_1region_1get_1rectangle", "_1cairo_1region_1num_1rectangles", "_1cairo_1reset_1clip", diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h index 8bdc8adad5..2a055a030a 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h @@ -106,6 +106,7 @@ typedef enum { _1cairo_1push_1group_FUNC, _1cairo_1rectangle_FUNC, _1cairo_1reference_FUNC, + _1cairo_1region_1contains_1point_FUNC, _1cairo_1region_1get_1rectangle_FUNC, _1cairo_1region_1num_1rectangles_FUNC, _1cairo_1reset_1clip_FUNC, diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java index 63df89f412..1194701344 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java @@ -1248,6 +1248,16 @@ public static final int cairo_region_num_rectangles(long /*int*/ region) { } } /** @method flags=dynamic */ +public static final native boolean _cairo_region_contains_point(long /*int*/ region, int x, int y); +public static final boolean cairo_region_contains_point(long /*int*/ region, int x, int y) { + lock.lock(); + try { + return _cairo_region_contains_point(region, x, y); + } finally { + lock.unlock(); + } +} +/** @method flags=dynamic */ public static final native void _cairo_region_get_rectangle(long /*int*/ region, int nth, long /*int*/ rectangle); public static final void cairo_region_get_rectangle(long /*int*/ region, int nth, long /*int*/ rectangle) { lock.lock(); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java index cddd2817ef..ca8aa54ebb 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java @@ -504,6 +504,7 @@ long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event) { @Override long /*int*/ gtk_clicked (long /*int*/ widget) { + if (containedInRegion(lastInput.x, lastInput.y)) return 0; if ((style & SWT.RADIO) != 0) { if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) { setSelection (!selected); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java index 2b42fefd28..239c99b77f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java @@ -59,12 +59,19 @@ public abstract class Control extends Widget implements Drawable { Image backgroundImage; Font font; Region region; + long /*int*/ eventRegion; String toolTipText; Object layoutData; Accessible accessible; Control labelRelation; String cssBackground, cssForeground = " "; boolean drawRegion; + /** + * Point for storing the (x, y) coordinate of the last input (click/scroll/etc.). + * This is useful for checking input event coordinates in methods that act on input, + * but do not receive coordinates (like gtk_clicked, for example). See bug 529431. + */ + Point lastInput = new Point(0, 0); LinkedList <Event> dragDetectionQueue; @@ -429,6 +436,7 @@ boolean hooksPaint () { long /*int*/ hoverProc (long /*int*/ widget) { int [] x = new int [1], y = new int [1], mask = new int [1]; gdk_window_get_device_position (0, x, y, mask); + if (containedInRegion(x[0], y[0])) return 0; sendMouseEvent (SWT.MouseHover, 0, /*time*/0, x [0], y [0], false, mask [0]); /* Always return zero in order to cancel the hover timer */ return 0; @@ -713,6 +721,21 @@ void checkMirrored () { if ((style & SWT.RIGHT_TO_LEFT) != 0) style |= SWT.MIRRORED; } +/** + * Convenience method for checking whether an (x, y) coordinate is in the set + * region. Only relevant for GTK3.10+. + * + * @param x an x coordinate + * @param y a y coordinate + * @return true if the coordinate (x, y) is in the region, false otherwise + */ +boolean containedInRegion (int x, int y) { + if (drawRegion && eventRegion != 0) { + return Cairo.cairo_region_contains_point(eventRegion, x, y); + } + return false; +} + long /*int*/ childStyle () { return parent.childStyle (); } @@ -3335,6 +3358,9 @@ long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event) { long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event, boolean sendMouseDown) { GdkEventButton gdkEvent = new GdkEventButton (); OS.memmove (gdkEvent, event, GdkEventButton.sizeof); + lastInput.x = (int) gdkEvent.x; + lastInput.y = (int) gdkEvent.y; + if (containedInRegion(lastInput.x, lastInput.y)) return 0; if (gdkEvent.type == GDK.GDK_3BUTTON_PRESS) return 0; /* @@ -3414,6 +3440,9 @@ long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event, bo long /*int*/ gtk_button_release_event (long /*int*/ widget, long /*int*/ event) { GdkEventButton gdkEvent = new GdkEventButton (); OS.memmove (gdkEvent, event, GdkEventButton.sizeof); + lastInput.x = (int) gdkEvent.x; + lastInput.y = (int) gdkEvent.y; + if (containedInRegion(lastInput.x, lastInput.y)) return 0; return sendMouseEvent (SWT.MouseUp, gdkEvent.button, display.clickCount, 0, false, gdkEvent.time, gdkEvent.x_root, gdkEvent.y_root, false, gdkEvent.state) ? 0 : 1; } @@ -3448,6 +3477,10 @@ long /*int*/ gtk_enter_notify_event (long /*int*/ widget, long /*int*/ event) { if (display.currentControl == this) return 0; GdkEventCrossing gdkEvent = new GdkEventCrossing (); OS.memmove (gdkEvent, event, GdkEventCrossing.sizeof); + lastInput.x = (int) gdkEvent.x; + lastInput.y = (int) gdkEvent.y; + if (containedInRegion(lastInput.x, lastInput.y)) return 0; + /* * It is possible to send out too many enter/exit events if entering a * control through a subwindow. The fix is to return without sending any @@ -3560,6 +3593,7 @@ void cairoClipRegion (long /*int*/ cairo) { GDK.gdk_cairo_region(cairo, actualRegion); Cairo.cairo_clip(cairo); Cairo.cairo_paint(cairo); + eventRegion = actualRegion; } @Override @@ -3702,11 +3736,14 @@ long /*int*/ gtk_key_release_event (long /*int*/ widget, long /*int*/ event) { @Override long /*int*/ gtk_leave_notify_event (long /*int*/ widget, long /*int*/ event) { if (display.currentControl != this) return 0; + GdkEventCrossing gdkEvent = new GdkEventCrossing (); + OS.memmove (gdkEvent, event, GdkEventCrossing.sizeof); + lastInput.x = (int) gdkEvent.x; + lastInput.y = (int) gdkEvent.y; + if (containedInRegion(lastInput.x, lastInput.y)) return 0; display.removeMouseHoverTimeout (handle); int result = 0; if (sendLeaveNotify () || display.getCursorControl () == null) { - GdkEventCrossing gdkEvent = new GdkEventCrossing (); - OS.memmove (gdkEvent, event, GdkEventCrossing.sizeof); if (gdkEvent.mode != GDK.GDK_CROSSING_NORMAL && gdkEvent.mode != GDK.GDK_CROSSING_UNGRAB) return 0; if ((gdkEvent.state & (GDK.GDK_BUTTON1_MASK | GDK.GDK_BUTTON2_MASK | GDK.GDK_BUTTON3_MASK)) != 0) return 0; result = sendMouseEvent (SWT.MouseExit, 0, gdkEvent.time, gdkEvent.x_root, gdkEvent.y_root, false, gdkEvent.state) ? 0 : 1; @@ -3742,6 +3779,9 @@ long /*int*/ gtk_motion_notify_event (long /*int*/ widget, long /*int*/ event) { int result; GdkEventMotion gdkEvent = new GdkEventMotion (); OS.memmove (gdkEvent, event, GdkEventMotion.sizeof); + lastInput.x = (int) gdkEvent.x; + lastInput.y = (int) gdkEvent.y; + if (containedInRegion(lastInput.x, lastInput.y)) return 0; /* * Feature in GTK: DND detection for X.11 & Wayland support is done through motion notify event * instead of mouse click event. See Bug 503431. @@ -3827,6 +3867,9 @@ long /*int*/ gtk_realize (long /*int*/ widget) { long /*int*/ gtk_scroll_event (long /*int*/ widget, long /*int*/ eventPtr) { GdkEventScroll gdkEvent = new GdkEventScroll (); OS.memmove (gdkEvent, eventPtr, GdkEventScroll.sizeof); + lastInput.x = (int) gdkEvent.x; + lastInput.y = (int) gdkEvent.y; + if (containedInRegion(lastInput.x, lastInput.y)) return 0; switch (gdkEvent.direction) { case GDK.GDK_SCROLL_UP: return sendMouseEvent (SWT.MouseWheel, 0, 3, SWT.SCROLL_LINE, true, gdkEvent.time, gdkEvent.x_root, gdkEvent.y_root, false, gdkEvent.state) ? 0 : 1; @@ -4340,6 +4383,7 @@ void sendFocusEvent (int type) { } boolean sendGestureEvent (int stateMask, int detail, int x, int y, double delta) { + if (containedInRegion(x, y)) return false; switch (detail) { case SWT.GESTURE_ROTATE: { return sendGestureEvent(stateMask, detail, x, y, delta, 0, 0, 0); @@ -4360,12 +4404,14 @@ void sendFocusEvent (int type) { } boolean sendGestureEvent (int stateMask, int detail, int x, int y, double xDirection, double yDirection) { + if (containedInRegion(x, y)) return false; if (detail == SWT.GESTURE_SWIPE) { return sendGestureEvent(stateMask, detail, x, y, 0, (int)xDirection, (int)yDirection, 0); } else return false; } boolean sendGestureEvent (int stateMask, int detail, int x, int y, double rotation, int xDirection, int yDirection, double magnification) { + if (containedInRegion(x, y)) return false; Event event = new Event (); event.stateMask = stateMask; event.detail = detail; @@ -4412,6 +4458,7 @@ boolean sendLeaveNotify() { } boolean sendMouseEvent (int type, int button, int time, double x, double y, boolean is_hint, int state) { + if (containedInRegion((int) x, (int) y)) return true; return sendMouseEvent (type, button, 0, 0, false, time, x, y, is_hint, state); } @@ -4421,6 +4468,7 @@ boolean sendMouseEvent (int type, int button, int time, double x, double y, bool * false - event sending canceled by user. */ boolean sendMouseEvent (int type, int button, int count, int detail, boolean send, int time, double x, double y, boolean is_hint, int state) { + if (containedInRegion((int) x, (int) y)) return true; if (!hooks (type) && !filters (type)) { /* * On Wayland, MouseDown events are cached for DnD purposes, but diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java index 9e218f4b92..7aabcef410 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java @@ -2184,6 +2184,10 @@ void sendTreeDefaultSelection() { long /*int*/ gtk_button_release_event (long /*int*/ widget, long /*int*/ event) { GdkEventButton gdkEvent = new GdkEventButton (); OS.memmove (gdkEvent, event, GdkEventButton.sizeof); + // Check region since super.gtk_button_release_event() isn't called + lastInput.x = (int) gdkEvent.x; + lastInput.y = (int) gdkEvent.y; + if (containedInRegion(lastInput.x, lastInput.y)) return 0; if (gdkEvent.window != GTK.gtk_tree_view_get_bin_window (handle)) return 0; /* * Feature in GTK. In multi-select tree view there is a problem with using DnD operations while also selecting multiple items. diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java index b26870ed8a..98be900516 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java @@ -2205,6 +2205,10 @@ void sendTreeDefaultSelection() { long /*int*/ gtk_button_release_event (long /*int*/ widget, long /*int*/ event) { GdkEventButton gdkEvent = new GdkEventButton (); OS.memmove (gdkEvent, event, GdkEventButton.sizeof); + // Check region since super.gtk_button_release_event() isn't called + lastInput.x = (int) gdkEvent.x; + lastInput.y = (int) gdkEvent.y; + if (containedInRegion(lastInput.x, lastInput.y)) return 0; if (gdkEvent.window != GTK.gtk_tree_view_get_bin_window (handle)) return 0; /* * Feature in GTK. In multi-select tree view there is a problem with using DnD operations while also selecting multiple items. diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java index 135afdc265..fab5f9bdf8 100644 --- a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java +++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java @@ -86,8 +86,11 @@ public class Bug529431_SetRegionTesting { // define the shape of the button using setRegion widget.setRegion(region); - // Uncomment to test input handling -// b2.addListener(SWT.Selection, e -> shell.close()); + // test input handling + widget.addListener(SWT.Selection, e -> System.out.println("SWT.Selection sent")); + widget.addListener(SWT.MouseDown, e -> System.out.println("SWT.MouseDown sent")); + widget.addListener(SWT.MouseExit, e -> System.out.println("SWT.MouseExit sent")); + widget.addListener(SWT.MouseEnter, e -> System.out.println("SWT.MouseEnter sent")); shell.setSize(200,200); shell.open(); |