[gtk+/touch-for-3.4: 3/65] Add GtkWidget::captured-event signal
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/touch-for-3.4: 3/65] Add GtkWidget::captured-event signal
- Date: Fri, 24 Feb 2012 15:25:05 +0000 (UTC)
commit 31cad1bb81ac1e131f572d562bc1932a05e13742
Author: Carlos Garcia Campos <cgarcia igalia com>
Date: Tue Feb 8 14:49:31 2011 +0100
Add GtkWidget::captured-event signal
This patch adds a capture phase to GTK+'s event propagation
model. Events are first propagated from the toplevel (or the
grab widget, if a grab is in place) down to the target widget
using the new ::captured-event signal, and then back up, using
the existing ::event signal.
This mechanism can be used in many places where we currently
have to prevent child widgets from getting events by putting
an input-only window over them. It will also be used to implement
kinetic scrolling in subsequent patches.
http://bugzilla.gnome.org/show_bug.cgi?id=641836
gtk/gtkmain.c | 224 ++++++++++++++++++++++++++++++++---------------
gtk/gtkprivate.h | 4 +
gtk/gtkwidget.c | 63 ++++++++++++++
gtk/gtkwidget.h | 3 +-
gtk/gtkwidgetprivate.h | 3 +
5 files changed, 224 insertions(+), 73 deletions(-)
---
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 9626569..5d71794 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -1473,6 +1473,7 @@ gtk_main_do_event (GdkEvent *event)
{
GtkWidget *event_widget;
GtkWidget *grab_widget = NULL;
+ GtkWidget *topmost_widget = NULL;
GtkWindowGroup *window_group;
GdkEvent *rewritten_event = NULL;
GdkDevice *device;
@@ -1532,6 +1533,14 @@ gtk_main_do_event (GdkEvent *event)
if (!grab_widget)
grab_widget = gtk_window_group_get_current_grab (window_group);
+ /* Find out the topmost widget where captured event propagation
+ * should start, which is the widget holding the GTK+ grab
+ * if any, otherwise it's left NULL and events are emitted
+ * from the toplevel (or topmost parentless parent).
+ */
+ if (grab_widget)
+ topmost_widget = grab_widget;
+
/* If the grab widget is an ancestor of the event widget
* then we send the event to the original event widget.
* This is the key to implementing modality.
@@ -1628,14 +1637,16 @@ gtk_main_do_event (GdkEvent *event)
case GDK_WINDOW_STATE:
case GDK_GRAB_BROKEN:
case GDK_DAMAGE:
- gtk_widget_event (event_widget, event);
+ if (!_gtk_widget_captured_event (event_widget, event))
+ gtk_widget_event (event_widget, event);
break;
case GDK_SCROLL:
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
- gtk_propagate_event (grab_widget, event);
+ if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
+ gtk_propagate_event (grab_widget, event);
break;
case GDK_KEY_PRESS:
@@ -1684,7 +1695,8 @@ gtk_main_do_event (GdkEvent *event)
case GDK_BUTTON_RELEASE:
case GDK_PROXIMITY_IN:
case GDK_PROXIMITY_OUT:
- gtk_propagate_event (grab_widget, event);
+ if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
+ gtk_propagate_event (grab_widget, event);
break;
case GDK_ENTER_NOTIFY:
@@ -1693,7 +1705,8 @@ gtk_main_do_event (GdkEvent *event)
_gtk_widget_set_device_window (event_widget,
gdk_event_get_device (event),
event->any.window);
- if (gtk_widget_is_sensitive (grab_widget))
+ if (gtk_widget_is_sensitive (grab_widget) &&
+ !_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_widget_event (grab_widget, event);
break;
@@ -1703,7 +1716,8 @@ gtk_main_do_event (GdkEvent *event)
_gtk_widget_set_device_window (event_widget,
gdk_event_get_device (event),
NULL);
- if (gtk_widget_is_sensitive (grab_widget))
+ if (gtk_widget_is_sensitive (grab_widget) &&
+ !_gtk_propagate_captured_event (grab_widget, event, topmost_widget))
gtk_widget_event (grab_widget, event);
break;
@@ -2333,45 +2347,96 @@ gtk_get_event_widget (GdkEvent *event)
return widget;
}
-/**
- * gtk_propagate_event:
- * @widget: a #GtkWidget
- * @event: an event
- *
- * Sends an event to a widget, propagating the event to parent widgets
- * if the event remains unhandled.
- *
- * Events received by GTK+ from GDK normally begin in gtk_main_do_event().
- * Depending on the type of event, existence of modal dialogs, grabs, etc.,
- * the event may be propagated; if so, this function is used.
- *
- * gtk_propagate_event() calls gtk_widget_event() on each widget it
- * decides to send the event to. So gtk_widget_event() is the lowest-level
- * function; it simply emits the #GtkWidget::event and possibly an
- * event-specific signal on a widget. gtk_propagate_event() is a bit
- * higher-level, and gtk_main_do_event() is the highest level.
- *
- * All that said, you most likely don't want to use any of these
- * functions; synthesizing events is rarely needed. There are almost
- * certainly better ways to achieve your goals. For example, use
- * gdk_window_invalidate_rect() or gtk_widget_queue_draw() instead
- * of making up expose events.
- */
-void
-gtk_propagate_event (GtkWidget *widget,
- GdkEvent *event)
+static gboolean
+propagate_event_up (GtkWidget *widget,
+ GdkEvent *event,
+ GtkWidget *topmost)
{
- gint handled_event;
+ gboolean handled_event = FALSE;
- g_return_if_fail (GTK_IS_WIDGET (widget));
- g_return_if_fail (event != NULL);
+ /* Propagate event up the widget tree so that
+ * parents can see the button and motion
+ * events of the children.
+ */
+ while (TRUE)
+ {
+ GtkWidget *tmp;
+
+ g_object_ref (widget);
+
+ /* Scroll events are special cased here because it
+ * feels wrong when scrolling a GtkViewport, say,
+ * to have children of the viewport eat the scroll
+ * event
+ */
+ if (!gtk_widget_is_sensitive (widget))
+ handled_event = event->type != GDK_SCROLL;
+ else
+ handled_event = gtk_widget_event (widget, event);
+
+ tmp = gtk_widget_get_parent (widget);
+ g_object_unref (widget);
+
+ if (widget == topmost)
+ break;
+
+ widget = tmp;
+
+ if (handled_event || !widget)
+ break;
+ }
+
+ return handled_event;
+}
+
+static gboolean
+propagate_event_down (GtkWidget *widget,
+ GdkEvent *event,
+ GtkWidget *topmost)
+{
+ gint handled_event = FALSE;
+ GList *widgets = NULL;
+ GList *l;
+
+ widgets = g_list_prepend (widgets, g_object_ref (widget));
+ while (widget && widget != topmost)
+ {
+ widget = gtk_widget_get_parent (widget);
+ if (!widget)
+ break;
+
+ widgets = g_list_prepend (widgets, g_object_ref (widget));
+
+ if (widget == topmost)
+ break;
+ }
+
+ for (l = widgets; l && !handled_event; l = g_list_next (l))
+ {
+ widget = (GtkWidget *)l->data;
- handled_event = FALSE;
+ if (!gtk_widget_is_sensitive (widget))
+ handled_event = TRUE;
+ else
+ handled_event = _gtk_widget_captured_event (widget, event);
+ }
+ g_list_free_full (widgets, (GDestroyNotify)g_object_unref);
- g_object_ref (widget);
+ return handled_event;
+}
- if ((event->type == GDK_KEY_PRESS) ||
- (event->type == GDK_KEY_RELEASE))
+static gboolean
+propagate_event (GtkWidget *widget,
+ GdkEvent *event,
+ gboolean captured,
+ GtkWidget *topmost)
+{
+ gboolean handled_event = FALSE;
+ gboolean (* propagate_func) (GtkWidget *widget, GdkEvent *event);
+
+ propagate_func = captured ? _gtk_widget_captured_event : gtk_widget_event;
+
+ if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)
{
/* Only send key events within Window widgets to the Window
* The Window widget will in turn pass the
@@ -2383,11 +2448,12 @@ gtk_propagate_event (GtkWidget *widget,
window = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (window))
{
+ g_object_ref (widget);
/* If there is a grab within the window, give the grab widget
* a first crack at the key event
*/
if (widget != window && gtk_widget_has_grab (widget))
- handled_event = gtk_widget_event (widget, event);
+ handled_event = propagate_func (widget, event);
if (!handled_event)
{
@@ -2395,45 +2461,59 @@ gtk_propagate_event (GtkWidget *widget,
if (GTK_IS_WINDOW (window))
{
if (gtk_widget_is_sensitive (window))
- gtk_widget_event (window, event);
+ handled_event = propagate_func (window, event);
}
}
- handled_event = TRUE; /* don't send to widget */
+ g_object_unref (widget);
+ return handled_event;
}
}
- /* Other events get propagated up the widget tree
- * so that parents can see the button and motion
- * events of the children.
- */
- if (!handled_event)
- {
- while (TRUE)
- {
- GtkWidget *tmp;
-
- /* Scroll events are special cased here because it
- * feels wrong when scrolling a GtkViewport, say,
- * to have children of the viewport eat the scroll
- * event
- */
- if (!gtk_widget_is_sensitive (widget))
- handled_event = event->type != GDK_SCROLL;
- else
- handled_event = gtk_widget_event (widget, event);
+ /* Other events get propagated up/down the widget tree */
+ return captured ?
+ propagate_event_down (widget, event, topmost) :
+ propagate_event_up (widget, event, topmost);
+}
- tmp = gtk_widget_get_parent (widget);
- g_object_unref (widget);
+/**
+ * gtk_propagate_event:
+ * @widget: a #GtkWidget
+ * @event: an event
+ *
+ * Sends an event to a widget, propagating the event to parent widgets
+ * if the event remains unhandled.
+ *
+ * Events received by GTK+ from GDK normally begin in gtk_main_do_event().
+ * Depending on the type of event, existence of modal dialogs, grabs, etc.,
+ * the event may be propagated; if so, this function is used.
+ *
+ * gtk_propagate_event() calls gtk_widget_event() on each widget it
+ * decides to send the event to. So gtk_widget_event() is the lowest-level
+ * function; it simply emits the #GtkWidget::event and possibly an
+ * event-specific signal on a widget. gtk_propagate_event() is a bit
+ * higher-level, and gtk_main_do_event() is the highest level.
+ *
+ * All that said, you most likely don't want to use any of these
+ * functions; synthesizing events is rarely needed. There are almost
+ * certainly better ways to achieve your goals. For example, use
+ * gdk_window_invalidate_rect() or gtk_widget_queue_draw() instead
+ * of making up expose events.
+ */
+void
+gtk_propagate_event (GtkWidget *widget,
+ GdkEvent *event)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (event != NULL);
- widget = tmp;
+ propagate_event (widget, event, FALSE, NULL);
+}
- if (!handled_event && widget)
- g_object_ref (widget);
- else
- break;
- }
- }
- else
- g_object_unref (widget);
+gboolean
+_gtk_propagate_captured_event (GtkWidget *widget,
+ GdkEvent *event,
+ GtkWidget *topmost)
+{
+ return propagate_event (widget, event, TRUE, topmost);
}
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index 1b4d0ae..121b169 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -74,6 +74,10 @@ gboolean _gtk_translate_keyboard_accel_state (GdkKeymap *keymap,
gint *level,
GdkModifierType *consumed_modifiers);
+gboolean _gtk_propagate_captured_event (GtkWidget *widget,
+ GdkEvent *event,
+ GtkWidget *topmost);
+
G_END_DECLS
#endif /* __GTK_PRIVATE_H__ */
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 0e4d198..280060e 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -484,6 +484,7 @@ enum {
QUERY_TOOLTIP,
DRAG_FAILED,
STYLE_UPDATED,
+ CAPTURED_EVENT,
LAST_SIGNAL
};
@@ -705,6 +706,7 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget,
GdkDevice *device,
gboolean recurse,
gboolean enabled);
+static gboolean event_window_is_still_viewable (GdkEvent *event);
/* --- variables --- */
static gpointer gtk_widget_parent_class = NULL;
@@ -1798,6 +1800,9 @@ gtk_widget_class_init (GtkWidgetClass *klass)
* #GtkWidget::key-press-event) and finally a generic
* #GtkWidget::event-after signal.
*
+ * An event can be captured before ::event signal is emitted by connecting to
+ * ::captured-event event signal.
+ *
* Returns: %TRUE to stop other handlers from being invoked for the event
* and to cancel the emission of the second specific ::event signal.
* %FALSE to propagate the event further and to allow the emission of
@@ -1835,6 +1840,34 @@ gtk_widget_class_init (GtkWidgetClass *klass)
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
+ * GtkWidget::captured-event:
+ * @widget: the object which received the signal.
+ * @event: the #GdkEvent which triggered this signal
+ *
+ * The #GtkWidget::captured-event signal is emitted before the
+ * #GtkWidget::event signal to allow capturing an event before the
+ * specialized events are emitted. 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.
+ *
+ * Since: 3.2
+ */
+ 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,
+ GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ /**
* GtkWidget::button-press-event:
* @widget: the object which received the signal.
* @event: (type Gdk.EventButton): the #GdkEventButton which triggered
@@ -5870,6 +5903,36 @@ gtk_widget_event (GtkWidget *widget,
return gtk_widget_event_internal (widget, event);
}
+gboolean
+_gtk_widget_captured_event (GtkWidget *widget,
+ GdkEvent *event)
+{
+ gboolean return_val = FALSE;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), TRUE);
+ g_return_val_if_fail (WIDGET_REALIZED_FOR_EVENT (widget, event), TRUE);
+
+ if (event->type == GDK_EXPOSE)
+ {
+ g_warning ("Events of type GDK_EXPOSE cannot be synthesized. To get "
+ "the same effect, call gdk_window_invalidate_rect/region(), "
+ "followed by gdk_window_process_updates().");
+ return TRUE;
+ }
+
+ if (!event_window_is_still_viewable (event))
+ return TRUE;
+
+ 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_object_unref (widget);
+
+ return return_val;
+}
+
/* Returns TRUE if a translation should be done */
gboolean
_gtk_widget_get_translation_to_window (GtkWidget *widget,
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 76d192c..14a1a84 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -425,6 +425,8 @@ struct _GtkWidgetClass
void (* style_updated) (GtkWidget *widget);
+ gboolean (* captured_event) (GtkWidget *widget,
+ GdkEvent *event);
/*< private >*/
GtkWidgetClassPrivate *priv;
@@ -436,7 +438,6 @@ struct _GtkWidgetClass
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
void (*_gtk_reserved7) (void);
- void (*_gtk_reserved8) (void);
};
struct _GtkWidgetAuxInfo
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 241a570..cbe659e 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -168,6 +168,9 @@ void _gtk_widget_set_style (GtkWidget *widget,
GtkStyle *style);
+gboolean _gtk_widget_captured_event (GtkWidget *widget,
+ GdkEvent *event);
+
G_END_DECLS
#endif /* __GTK_WIDGET_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]