[gtk+/wip/carlosg/event-delivery: 28/105] gtk: Handle events with coordinates in toplevel-relative ones
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/carlosg/event-delivery: 28/105] gtk: Handle events with coordinates in toplevel-relative ones
- Date: Sun, 14 May 2017 23:39:48 +0000 (UTC)
commit c43ce71c21da075b53f1c00bd286e2ccfd1312aa
Author: Carlos Garnacho <carlosg gnome org>
Date: Fri Mar 31 17:38:38 2017 +0200
gtk: Handle events with coordinates in toplevel-relative ones
Implement target finding per-pointer/touchpoint through GtkPointerFocus and
_gtk_toplevel_pick(). Focus changes are handled through the emission of
crossing events between the old target and the new one.
gtk/gtkmain.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 207 insertions(+), 0 deletions(-)
---
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 70b1439..e062110 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -1301,6 +1301,207 @@ check_event_in_child_popover (GtkWidget *event_widget,
return (popover_parent == grab_widget || gtk_widget_is_ancestor (popover_parent, grab_widget));
}
+static GdkNotifyType
+get_virtual_notify_type (GdkNotifyType notify_type)
+{
+ switch (notify_type)
+ {
+ case GDK_NOTIFY_ANCESTOR:
+ case GDK_NOTIFY_INFERIOR:
+ return GDK_NOTIFY_VIRTUAL;
+ case GDK_NOTIFY_NONLINEAR:
+ return GDK_NOTIFY_NONLINEAR_VIRTUAL;
+ default:
+ g_assert_not_reached ();
+ return GDK_NOTIFY_UNKNOWN;
+ }
+}
+
+static void
+synth_crossing_for_motion (GtkWidget *widget,
+ GtkWidget *toplevel,
+ gboolean enter,
+ GtkWidget *other_widget,
+ GdkEvent *source,
+ GdkNotifyType notify_type)
+{
+ GdkEvent *event;
+ gdouble x, y;
+
+ event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY);
+ gdk_event_set_device (event, gdk_event_get_device (source));
+ gdk_event_set_source_device (event, gdk_event_get_source_device (source));
+
+ event->any.window = g_object_ref (gtk_widget_get_window (toplevel));
+ if (other_widget)
+ event->crossing.subwindow = g_object_ref (gtk_widget_get_window (other_widget));
+
+ gdk_event_get_coords (source, &x, &y);
+ event->crossing.x = x;
+ event->crossing.y = y;
+ event->crossing.mode = GDK_CROSSING_NORMAL;
+ event->crossing.detail = notify_type;
+
+ gtk_widget_event (widget, event);
+ gdk_event_free (event);
+}
+
+static GtkWidget *
+update_pointer_focus_state (GtkWindow *toplevel,
+ GdkEvent *event,
+ GtkWidget *new_target)
+{
+ GtkWidget *old_target = NULL, *ancestor = NULL, *widget;
+ GdkNotifyType notify_type;
+ GdkEventSequence *sequence;
+ GdkDevice *device;
+ gdouble x, y;
+
+ device = gdk_event_get_device (event);
+ sequence = gdk_event_get_event_sequence (event);
+ old_target = gtk_window_lookup_pointer_focus_widget (toplevel, device, sequence);
+ if (old_target == new_target)
+ return old_target;
+
+ gdk_event_get_coords (event, &x, &y);
+ gtk_window_update_pointer_focus (toplevel, device, sequence,
+ new_target, x, y);
+
+ if (sequence != NULL)
+ return old_target;
+
+ if (old_target && new_target)
+ ancestor = gtk_widget_common_ancestor (old_target, new_target);
+
+ if (ancestor == old_target)
+ notify_type = GDK_NOTIFY_ANCESTOR;
+ else if (ancestor == new_target)
+ notify_type = GDK_NOTIFY_INFERIOR;
+ else
+ notify_type = GDK_NOTIFY_NONLINEAR;
+
+ if (old_target)
+ {
+ widget = old_target;
+
+ while (widget != ancestor)
+ {
+ synth_crossing_for_motion (widget, GTK_WIDGET (toplevel), FALSE,
+ new_target, event,
+ widget == old_target ?
+ notify_type :
+ get_virtual_notify_type (notify_type));
+ widget = gtk_widget_get_parent (widget);
+ }
+ }
+
+ if (new_target)
+ {
+ GSList *widgets = NULL;
+
+ widget = new_target;
+
+ while (widget)
+ {
+ widgets = g_slist_prepend (widgets, widget);
+ widget = gtk_widget_get_parent (widget);
+ }
+
+ while (widgets)
+ {
+ widget = widgets->data;
+ widgets = g_slist_delete_link (widgets, widgets);
+ synth_crossing_for_motion (widget, GTK_WIDGET (toplevel), TRUE,
+ old_target, event,
+ widget == new_target ?
+ notify_type :
+ get_virtual_notify_type (notify_type));
+ }
+ }
+
+ return old_target;
+}
+
+static gboolean
+is_pointing_event (GdkEvent *event)
+{
+ switch (event->type)
+ {
+ case GDK_MOTION_NOTIFY:
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ case GDK_BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ case GDK_SCROLL:
+ case GDK_TOUCH_BEGIN:
+ case GDK_TOUCH_UPDATE:
+ case GDK_TOUCH_END:
+ case GDK_TOUCH_CANCEL:
+ case GDK_TOUCHPAD_PINCH:
+ case GDK_TOUCHPAD_SWIPE:
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ }
+}
+
+static GtkWidget *
+handle_pointing_event (GdkEvent *event)
+{
+ GtkWidget *target = NULL, *old_target = NULL, *widget;
+ GtkWindow *toplevel;
+ GdkEventSequence *sequence;
+ GdkDevice *device;
+ gdouble x, y;
+
+ widget = gtk_get_event_widget (event);
+ device = gdk_event_get_device (event);
+ if (!device || !gdk_event_get_coords (event, &x, &y))
+ return widget;
+
+ toplevel = GTK_WINDOW (gtk_widget_get_toplevel (widget));
+ if (!GTK_IS_WINDOW (toplevel))
+ return widget;
+
+ sequence = gdk_event_get_event_sequence (event);
+
+ switch (event->type)
+ {
+ case GDK_LEAVE_NOTIFY:
+ case GDK_TOUCH_END:
+ case GDK_TOUCH_CANCEL:
+ old_target = update_pointer_focus_state (toplevel, event, NULL);
+ break;
+ case GDK_TOUCH_BEGIN:
+ case GDK_TOUCH_UPDATE:
+ case GDK_MOTION_NOTIFY:
+ case GDK_ENTER_NOTIFY:
+ target = _gtk_toplevel_pick (toplevel, x, y, NULL, NULL);
+ old_target = update_pointer_focus_state (toplevel, event, target);
+
+ /* Let it take the effective pointer focus anyway, as it may change due
+ * to implicit grabs.
+ */
+ target = NULL;
+ break;
+ case GDK_BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ case GDK_SCROLL:
+ case GDK_TOUCHPAD_PINCH:
+ case GDK_TOUCHPAD_SWIPE:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (!target)
+ target = gtk_window_lookup_effective_pointer_focus_widget (toplevel,
+ device,
+ sequence);
+ return target ? target : old_target;
+}
+
/**
* gtk_main_do_event:
* @event: An event to process (normally passed by GDK)
@@ -1394,6 +1595,12 @@ gtk_main_do_event (GdkEvent *event)
event_widget = gtk_get_event_widget (event);
}
+ if (is_pointing_event (event))
+ event_widget = handle_pointing_event (event);
+
+ if (!event_widget)
+ return;
+
/* Push the event onto a stack of current events for
* gtk_current_event_get().
*/
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]