[gtk+/touchscreens: 33/69] gtk: Add event storing/replaying to GtkWidget::captured-event
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/touchscreens: 33/69] gtk: Add event storing/replaying to GtkWidget::captured-event
- Date: Thu, 5 Jan 2012 11:42:36 +0000 (UTC)
commit ce879ae739802114d5862ac5ae590c8ad2e2f7b3
Author: Carlos Garnacho <carlosg gnome org>
Date: Sun Nov 13 17:44:30 2011 +0100
gtk: Add event storing/replaying to GtkWidget::captured-event
It now returns a GtkCapturedEventFlags which tells whether the
widget captured the event, and additionally whether the event
should be stored for later replay.
gtk_widget_release_captured_events() has been added too so
all stored events are released onto the widget that was initially
to receive the events.
gtk/gtkenums.h | 6 ++
gtk/gtkmarshalers.list | 1 +
gtk/gtkprivate.c | 17 ++++++
gtk/gtkprivate.h | 4 ++
gtk/gtkscrolledwindow.c | 120 ++++++++++++++---------------------------
gtk/gtkwidget.c | 136 +++++++++++++++++++++++++++++++++++++++++++----
gtk/gtkwidget.h | 7 ++-
7 files changed, 200 insertions(+), 91 deletions(-)
---
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 7045f92..9188208 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -920,6 +920,12 @@ typedef enum {
GTK_BORDER_STYLE_OUTSET
} GtkBorderStyle;
+typedef enum {
+ GTK_CAPTURED_EVENT_NONE = 0,
+ GTK_CAPTURED_EVENT_HANDLED = 1 << 0,
+ GTK_CAPTURED_EVENT_STORE = 1 << 1
+} GtkCapturedEventFlags;
+
G_END_DECLS
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index a21caff..b75651f 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -48,6 +48,7 @@ BOOLEAN:BOOLEAN,BOOLEAN,BOOLEAN
BOOLEAN:STRING
ENUM:ENUM
ENUM:VOID
+FLAGS:BOXED
INT:POINTER
OBJECT:VOID
STRING:DOUBLE
diff --git a/gtk/gtkprivate.c b/gtk/gtkprivate.c
index df45790..6f57cb0 100644
--- a/gtk/gtkprivate.c
+++ b/gtk/gtkprivate.c
@@ -32,6 +32,7 @@
#include "gdk/gdk.h"
#include "gtkprivate.h"
+#include "gtkenums.h"
#if !defined G_OS_WIN32 && !(defined GDK_WINDOWING_QUARTZ && defined QUARTZ_RELOCATION)
@@ -159,6 +160,22 @@ _gtk_single_string_accumulator (GSignalInvocationHint *ihint,
return continue_emission;
}
+gboolean
+_gtk_captured_enum_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer dummy)
+{
+ gboolean continue_emission;
+ GtkCapturedEventFlags flags;
+
+ flags = g_value_get_flags (handler_return);
+ g_value_set_flags (return_accu, flags);
+ continue_emission = (flags & GTK_CAPTURED_EVENT_HANDLED) == 0;
+
+ return continue_emission;
+}
+
GdkModifierType
_gtk_replace_virtual_modifiers (GdkKeymap *keymap,
GdkModifierType modifiers)
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index 1b4d0ae..957176f 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -59,6 +59,10 @@ gboolean _gtk_single_string_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy);
+gboolean _gtk_captured_enum_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer dummy);
GdkModifierType _gtk_replace_virtual_modifiers (GdkKeymap *keymap,
GdkModifierType modifiers);
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index b932e03..2f10709 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -168,7 +168,7 @@ struct _GtkScrolledWindowPrivate
guint hide_scrollbars_id;
/* Kinetic scrolling */
- GdkEvent *button_press_event;
+ GdkDevice *drag_device;
guint kinetic_scrolling_enabled : 1;
guint in_drag : 1;
guint hmoving : 1;
@@ -1252,12 +1252,6 @@ gtk_scrolled_window_destroy (GtkWidget *widget)
priv->release_timeout_id = 0;
}
- if (priv->button_press_event)
- {
- gdk_event_free (priv->button_press_event);
- priv->button_press_event = NULL;
- }
-
motion_event_list_clear (&priv->motion_events);
gtk_scrolled_window_auto_hide_scrollbars_stop (scrolled_window);
@@ -2717,17 +2711,13 @@ static gboolean
gtk_scrolled_window_release_captured_events (GtkScrolledWindow *scrolled_window)
{
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
- GdkDevice *device;
/* Cancel the scrolling and send the button press
* event to the child widget
*/
- if (!priv->button_press_event)
- return FALSE;
-
- device = gdk_event_get_device (priv->button_press_event);
- gdk_device_ungrab (device, GDK_CURRENT_TIME);
- gtk_device_grab_remove (GTK_WIDGET (scrolled_window), device);
+ gdk_device_ungrab (priv->drag_device, GDK_CURRENT_TIME);
+ gtk_device_grab_remove (GTK_WIDGET (scrolled_window), priv->drag_device);
+ priv->drag_device = NULL;
if (priv->motion_notify_id > 0)
{
@@ -2740,18 +2730,7 @@ gtk_scrolled_window_release_captured_events (GtkScrolledWindow *scrolled_window)
priv->button_release_id = 0;
}
- /* We are going to synthesize the button press event so that
- * it can be handled by child widget, but we don't want to
- * handle it, so block both button-press and and press-and-hold
- * during this button press
- */
- g_signal_handler_block (scrolled_window, priv->button_press_id);
-
- gtk_main_do_event (priv->button_press_event);
-
- g_signal_handler_unblock (scrolled_window, priv->button_press_id);
- gdk_event_free (priv->button_press_event);
- priv->button_press_event = NULL;
+ gtk_widget_release_captured_events (GTK_WIDGET (scrolled_window), TRUE);
return FALSE;
}
@@ -2765,23 +2744,25 @@ gtk_scrolled_window_button_release_event (GtkWidget *widget,
GtkWidget *child;
gdouble distance;
GdkEventButton *event;
- GdkDevice *device;
if (_event->type != GDK_BUTTON_RELEASE)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
event = (GdkEventButton *)_event;
if (event->button != 1)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
child = gtk_bin_get_child (GTK_BIN (widget));
if (!child)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
+
+ if (priv->drag_device != gdk_event_get_device (_event))
+ return GTK_CAPTURED_EVENT_NONE;
- device = gdk_event_get_device (_event);
- gdk_device_ungrab (device, event->time);
- gtk_device_grab_remove (widget, device);
+ gdk_device_ungrab (priv->drag_device, event->time);
+ gtk_device_grab_remove (widget, priv->drag_device);
+ priv->drag_device = NULL;
if (priv->motion_notify_id > 0)
{
@@ -2806,28 +2787,12 @@ gtk_scrolled_window_button_release_event (GtkWidget *widget,
/* There hasn't been scrolling at all, so just let the
* child widget handle the events normally
*/
- if (priv->button_press_event)
- {
- g_signal_handler_block (widget, priv->button_press_id);
- gtk_main_do_event (priv->button_press_event);
- g_signal_handler_unblock (widget, priv->button_press_id);
- gdk_event_free (priv->button_press_event);
- priv->button_press_event = NULL;
- gtk_main_do_event (_event);
-
- return TRUE;
- }
+ gtk_widget_release_captured_events (widget, TRUE);
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
}
priv->in_drag = FALSE;
- if (priv->button_press_event)
- {
- gdk_event_free (priv->button_press_event);
- priv->button_press_event = NULL;
- }
-
distance =
gtk_scrolled_window_get_deceleration_distance (scrolled_window,
event->x_root, event->y_root,
@@ -2843,7 +2808,7 @@ gtk_scrolled_window_button_release_event (GtkWidget *widget,
/* Reset motion event buffer */
motion_event_list_reset (&priv->motion_events);
- return TRUE;
+ return GTK_CAPTURED_EVENT_HANDLED;
}
static gboolean
@@ -2860,16 +2825,19 @@ gtk_scrolled_window_motion_notify_event (GtkWidget *widget,
GdkEventMotion *event;
if (_event->type != GDK_MOTION_NOTIFY)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
event = (GdkEventMotion *)_event;
if (!(event->state & GDK_BUTTON1_MASK))
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
child = gtk_bin_get_child (GTK_BIN (widget));
if (!child)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
+
+ if (priv->drag_device != gdk_event_get_device (_event))
+ return GTK_CAPTURED_EVENT_NONE;
/* Check if we've passed the drag threshold */
if (!priv->in_drag)
@@ -2885,18 +2853,13 @@ gtk_scrolled_window_motion_notify_event (GtkWidget *widget,
priv->in_drag = TRUE;
}
else
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
}
priv->last_button_event_x_root = -TOUCH_BYPASS_CAPTURED_THRESHOLD;
priv->last_button_event_y_root = -TOUCH_BYPASS_CAPTURED_THRESHOLD;
- if (priv->button_press_event)
- {
- gdk_event_free (priv->button_press_event);
- priv->button_press_event = NULL;
- }
-
+ gtk_widget_release_captured_events (widget, FALSE);
motion = motion_event_list_last (&priv->motion_events);
if (motion)
@@ -2921,7 +2884,7 @@ gtk_scrolled_window_motion_notify_event (GtkWidget *widget,
motion->y = event->y_root;
motion->time = event->time;
- return FALSE;
+ return GTK_CAPTURED_EVENT_HANDLED;
}
static gboolean
@@ -2935,24 +2898,24 @@ gtk_scrolled_window_button_press_event (GtkWidget *widget,
gint threshold;
GtkWidget *event_widget;
GdkEventButton *event;
- GdkDevice *device, *source_device;
+ GdkDevice *source_device;
GdkInputSource source;
guint timeout;
if (_event->type != GDK_BUTTON_PRESS)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
source_device = gdk_event_get_source_device (_event);
source = gdk_device_get_source (source_device);
if (source != GDK_SOURCE_TOUCH)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
event = (GdkEventButton *)_event;
if (!priv->vscrollbar_visible &&
!priv->hscrollbar_visible)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
/* Check whether the button press is close to the previous one,
* take that as a shortcut to get the child widget handle events
@@ -2962,32 +2925,32 @@ gtk_scrolled_window_button_press_event (GtkWidget *widget,
{
priv->last_button_event_x_root = -TOUCH_BYPASS_CAPTURED_THRESHOLD;
priv->last_button_event_y_root = -TOUCH_BYPASS_CAPTURED_THRESHOLD;
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
}
priv->last_button_event_x_root = event->x_root;
priv->last_button_event_y_root = event->y_root;
if (event->button != 1)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
child = gtk_bin_get_child (GTK_BIN (widget));
if (!child)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
event_widget = gtk_get_event_widget (_event);
if (priv->hscrollbar == event_widget || priv->vscrollbar == event_widget)
- return FALSE;
+ return GTK_CAPTURED_EVENT_NONE;
- device = gdk_event_get_device (_event);
- gdk_device_grab (device,
+ priv->drag_device = gdk_event_get_device (_event);
+ gdk_device_grab (priv->drag_device,
gtk_widget_get_window (widget),
GDK_OWNERSHIP_WINDOW,
TRUE,
GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK,
NULL,
event->time);
- gtk_device_grab_add (widget, device, TRUE);
+ gtk_device_grab_add (widget, priv->drag_device, TRUE);
/* Reset motion buffer */
motion_event_list_reset (&priv->motion_events);
@@ -3034,14 +2997,15 @@ gtk_scrolled_window_button_press_event (GtkWidget *widget,
priv->in_drag = TRUE;
/* Swallow the press event */
- return TRUE;
+ return GTK_CAPTURED_EVENT_HANDLED;
}
else
priv->in_drag = FALSE;
- priv->button_press_event = gdk_event_copy (_event);
-
- return TRUE;
+ /* Store the button press event in
+ * case we need to propagate it later
+ */
+ return GTK_CAPTURED_EVENT_HANDLED | GTK_CAPTURED_EVENT_STORE;
}
static gboolean
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 21ae6ba..d3089af 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -398,6 +398,8 @@ struct _GtkWidgetPrivate
/* The widget's parent */
GtkWidget *parent;
+ GSList *captured_events;
+
#ifdef G_ENABLE_DEBUG
/* Number of gtk_widget_push_verify_invariants () */
guint verifying_invariants_count;
@@ -1868,21 +1870,41 @@ gtk_widget_class_init (GtkWidgetClass *klass)
* The event is propagated starting from the top-level container to
* the widget that received the event going down the hierarchy.
*
- * Returns: %TRUE to stop other handlers from being invoked for the event
- * and to cancel the emission of the ::event signal.
- * %FALSE to propagate the event further and to allow the emission of
- * the ::event signal.
+ * This signal returns a #GtkCapturedEventFlags with the handling
+ * status of the event, if %GTK_CAPTURED_EVENT_HANDLED is enabled,
+ * the event will be considered to be handled, and thus not propagated
+ * further.
*
- * Since: 3.2
+ * If, additionally, %GTK_CAPTURED_EVENT_STORE is enabled, the event will
+ * be stored so it can possibly be re-sent at a later stage. See
+ * gtk_widget_release_captured_events().
+ *
+ * If no flags are enabled, the captured event will be propagated even
+ * further, eventually triggering the emission of the #GtkWidget::event
+ * signal on the widget that received the event if no parent widgets
+ * captured it.
+ *
+ * <note><para>Enabling %GTK_CAPTURED_EVENT_STORE without
+ * %GTK_CAPTURED_EVENT_HANDLED is not allowed to avoid doubly
+ * event emission.</para></note>
+ *
+ * <warning><para>%GTK_CAPTURED_EVENT_STORE will not keep any track of
+ * event parity (eg. ensuring that button/key presses and releases
+ * are paired, or focus/crossing events) nor consistency, so use with
+ * discretion.</para></warning>
+ *
+ * Returns: a #GtkCapturedEventFlags specifying what to do with the event.
+ *
+ * Since: 3.4
*/
widget_signals[CAPTURED_EVENT] =
g_signal_new (I_("captured-event"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, captured_event),
- _gtk_boolean_handled_accumulator, NULL,
- _gtk_marshal_BOOLEAN__BOXED,
- G_TYPE_BOOLEAN, 1,
+ _gtk_captured_enum_accumulator, NULL,
+ _gtk_marshal_FLAGS__BOXED,
+ GTK_TYPE_CAPTURED_EVENT_FLAGS, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
@@ -5989,11 +6011,15 @@ gboolean
_gtk_widget_captured_event (GtkWidget *widget,
GdkEvent *event)
{
- gboolean return_val = FALSE;
+ GtkCapturedEventFlags flags;
+ GtkWidgetPrivate *priv;
+ gboolean return_val;
g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE);
+ priv = widget->priv;
+
if (event->type == GDK_EXPOSE)
{
g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get "
@@ -6007,8 +6033,25 @@ _gtk_widget_captured_event (GtkWidget *widget,
g_object_ref (widget);
- g_signal_emit (widget, widget_signals[CAPTURED_EVENT], 0, event, &return_val);
- return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
+ g_signal_emit (widget, widget_signals[CAPTURED_EVENT], 0, event, &flags);
+
+ /* Only store events that have been handled, so we don't end up
+ * sending twice the same event.
+ */
+ if (flags & GTK_CAPTURED_EVENT_STORE)
+ {
+ if ((flags & GTK_CAPTURED_EVENT_HANDLED) != 0)
+ priv->captured_events = g_slist_prepend (priv->captured_events,
+ gdk_event_copy (event));
+ else
+ g_warning ("Captured events can only be stored if they are claimed "
+ "to be handled by the capturing widget. The use of "
+ "GTK_CAPTURED_EVENT_HANDLED is mandatory if using "
+ "GTK_CAPTURED_EVENT_STORE.");
+ }
+
+ return_val = (flags & GTK_CAPTURED_EVENT_HANDLED) != 0 ||
+ !WIDGET_REALIZED_FOR_EVENT (widget, event);
g_object_unref (widget);
@@ -10659,6 +10702,12 @@ gtk_widget_finalize (GObject *object)
_gtk_widget_free_cached_sizes (widget);
+ if (priv->captured_events)
+ {
+ g_slist_foreach (priv->captured_events, (GFunc) gdk_event_free, NULL);
+ g_slist_free (priv->captured_events);
+ }
+
if (g_object_is_floating (object))
g_warning ("A floating object was finalized. This means that someone\n"
"called g_object_unref() on an object that had only a floating\n"
@@ -14406,3 +14455,68 @@ _gtk_widget_set_style (GtkWidget *widget,
{
widget->priv->style = style;
}
+
+/**
+ * gtk_widget_release_captured_events:
+ * @widget: the #GtkWidget holding the events
+ * @emit: #TRUE if the events must be emitted on the targed widget
+ *
+ * Releases the events that a widget has captured and stored
+ * in the #GtkWidget::captured-event signal. if @emit is #TRUE,
+ * the events will be emitted on the target widget (the widget
+ * that would receive the event if no signal capturing happened)
+ *
+ * Since: 3.4
+ **/
+void
+gtk_widget_release_captured_events (GtkWidget *widget,
+ gboolean emit)
+{
+ GtkWidgetPrivate *priv;
+ GSList *l;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ priv = widget->priv;
+
+ if (emit)
+ {
+ priv->captured_events = g_slist_reverse (priv->captured_events);
+
+ for (l = priv->captured_events; l; l = l->next)
+ {
+ GtkWidget *event_widget;
+ GdkEvent *event = l->data;
+
+ event_widget = gtk_get_event_widget (event);
+
+ switch (event->type)
+ {
+ case GDK_PROPERTY_NOTIFY:
+ case GDK_FOCUS_CHANGE:
+ case GDK_CONFIGURE:
+ case GDK_MAP:
+ case GDK_UNMAP:
+ case GDK_SELECTION_CLEAR:
+ case GDK_SELECTION_REQUEST:
+ case GDK_SELECTION_NOTIFY:
+ case GDK_CLIENT_EVENT:
+ case GDK_VISIBILITY_NOTIFY:
+ case GDK_WINDOW_STATE:
+ case GDK_GRAB_BROKEN:
+ case GDK_DAMAGE:
+ /* These events are capturable, but don't bubble up */
+ gtk_widget_event (event_widget, event);
+ break;
+ default:
+ /* All other capturable events do bubble up */
+ gtk_propagate_event (event_widget, event);
+ break;
+ }
+ }
+ }
+
+ g_slist_foreach (priv->captured_events, (GFunc) gdk_event_free, NULL);
+ g_slist_free (priv->captured_events);
+ priv->captured_events = NULL;
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 32fc5bb..653c188 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -432,8 +432,9 @@ struct _GtkWidgetClass
void (* style_updated) (GtkWidget *widget);
- void (* captured_event) (GtkWidget *widget,
- GdkEvent *event);
+ GtkCapturedEventFlags (* captured_event) (GtkWidget *widget,
+ GdkEvent *event);
+
gboolean (* press_and_hold) (GtkWidget *widget,
GdkDevice *device,
GtkPressAndHoldAction action,
@@ -904,6 +905,8 @@ GtkWidgetPath * gtk_widget_get_path (GtkWidget *widget);
GdkModifierType gtk_widget_get_modifier_mask (GtkWidget *widget,
GdkModifierIntent intent);
+void gtk_widget_release_captured_events (GtkWidget *widget,
+ gboolean emit);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]