widget: Hook GtkEventController to widget event processing.

A controller can be optionally hooked on the capture or the bubble
phase, so the controller will automatically receive and handle events
as they arrive without further interaction.
This commit is contained in:
Carlos Garnacho 2014-02-26 12:50:31 +01:00
parent d0da82fed6
commit f1bb0283dd
3 changed files with 194 additions and 4 deletions

View File

@ -1049,6 +1049,12 @@ typedef enum
GTK_INPUT_HINT_INHIBIT_OSK = 1 << 7 GTK_INPUT_HINT_INHIBIT_OSK = 1 << 7
} GtkInputHints; } GtkInputHints;
typedef enum
{
GTK_PHASE_CAPTURE,
GTK_PHASE_BUBBLE
} GtkPropagationPhase;
typedef enum typedef enum
{ {
GTK_EVENT_SEQUENCE_NONE, GTK_EVENT_SEQUENCE_NONE,

View File

@ -396,6 +396,12 @@ typedef struct {
GDestroyNotify destroy_notify; GDestroyNotify destroy_notify;
} GtkWidgetTemplate; } GtkWidgetTemplate;
typedef struct {
GtkEventController *controller;
guint evmask_notify_id;
guint propagation_phase : 2;
} EventControllerData;
struct _GtkWidgetPrivate struct _GtkWidgetPrivate
{ {
/* The state of the widget. Needs to be able to hold all GtkStateFlags bits /* The state of the widget. Needs to be able to hold all GtkStateFlags bits
@ -505,6 +511,8 @@ struct _GtkWidgetPrivate
/* Number of gtk_widget_push_verify_invariants () */ /* Number of gtk_widget_push_verify_invariants () */
guint verifying_invariants_count; guint verifying_invariants_count;
#endif /* G_ENABLE_DEBUG */ #endif /* G_ENABLE_DEBUG */
GList *event_controllers;
}; };
struct _GtkWidgetClassPrivate struct _GtkWidgetClassPrivate
@ -6930,6 +6938,48 @@ _gtk_widget_set_captured_event_handler (GtkWidget *widget,
g_object_set_data (G_OBJECT (widget), "captured-event-handler", callback); g_object_set_data (G_OBJECT (widget), "captured-event-handler", callback);
} }
static GdkEventMask
_gtk_widget_get_controllers_evmask (GtkWidget *widget)
{
EventControllerData *data;
GdkEventMask evmask = 0;
GtkWidgetPrivate *priv;
GList *l;
priv = widget->priv;
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
evmask |= gtk_event_controller_get_event_mask (data->controller);
}
return evmask;
}
static gboolean
_gtk_widget_run_controllers (GtkWidget *widget,
const GdkEvent *event,
GtkPropagationPhase phase)
{
EventControllerData *data;
gboolean handled = FALSE;
GtkWidgetPrivate *priv;
GList *l;
priv = widget->priv;
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
if (data->propagation_phase == phase)
handled |= gtk_event_controller_handle_event (data->controller, event);
}
return handled;
}
gboolean gboolean
_gtk_widget_captured_event (GtkWidget *widget, _gtk_widget_captured_event (GtkWidget *widget,
GdkEvent *event) GdkEvent *event)
@ -6951,13 +7001,15 @@ _gtk_widget_captured_event (GtkWidget *widget,
if (!event_window_is_still_viewable (event)) if (!event_window_is_still_viewable (event))
return TRUE; return TRUE;
return_val = _gtk_widget_run_controllers (widget, event, GTK_PHASE_CAPTURE);
handler = g_object_get_data (G_OBJECT (widget), "captured-event-handler"); handler = g_object_get_data (G_OBJECT (widget), "captured-event-handler");
if (!handler) if (!handler)
return FALSE; return return_val;
g_object_ref (widget); g_object_ref (widget);
return_val = handler (widget, event); return_val |= handler (widget, event);
return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event); return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
/* The widget that was originally to receive the event /* The widget that was originally to receive the event
@ -7155,6 +7207,7 @@ gtk_widget_event_internal (GtkWidget *widget,
g_object_ref (widget); g_object_ref (widget);
return_val |= _gtk_widget_run_controllers (widget, event, GTK_PHASE_BUBBLE);
g_signal_emit (widget, widget_signals[EVENT], 0, event, &return_val); g_signal_emit (widget, widget_signals[EVENT], 0, event, &return_val);
return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event); return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
if (!return_val) if (!return_val)
@ -10701,8 +10754,11 @@ gtk_widget_add_events_internal_list (GtkWidget *widget,
gint events, gint events,
GList *window_list) GList *window_list)
{ {
GdkEventMask controllers_mask;
GList *l; GList *l;
controllers_mask = _gtk_widget_get_controllers_evmask (widget);
for (l = window_list; l != NULL; l = l->next) for (l = window_list; l != NULL; l = l->next)
{ {
GdkWindow *window = l->data; GdkWindow *window = l->data;
@ -10714,9 +10770,16 @@ gtk_widget_add_events_internal_list (GtkWidget *widget,
GList *children; GList *children;
if (device) if (device)
gdk_window_set_device_events (window, device, gdk_window_get_events (window) | events); {
gdk_window_set_device_events (window, device,
gdk_window_get_events (window) |
events | controllers_mask);
}
else else
gdk_window_set_events (window, gdk_window_get_events (window) | events); {
gdk_window_set_events (window, gdk_window_get_events (window) |
events | controllers_mask);
}
children = gdk_window_get_children (window); children = gdk_window_get_children (window);
gtk_widget_add_events_internal_list (widget, device, events, children); gtk_widget_add_events_internal_list (widget, device, events, children);
@ -11591,6 +11654,7 @@ gtk_widget_finalize (GObject *object)
GtkWidgetPrivate *priv = widget->priv; GtkWidgetPrivate *priv = widget->priv;
GtkWidgetAuxInfo *aux_info; GtkWidgetAuxInfo *aux_info;
GtkAccessible *accessible; GtkAccessible *accessible;
EventControllerData *data;
gtk_grab_remove (widget); gtk_grab_remove (widget);
@ -11618,6 +11682,12 @@ gtk_widget_finalize (GObject *object)
_gtk_size_request_cache_free (&priv->requests); _gtk_size_request_cache_free (&priv->requests);
while (priv->event_controllers)
{
data = priv->event_controllers->data;
gtk_widget_remove_controller (widget, data->controller);
}
if (g_object_is_floating (object)) if (g_object_is_floating (object))
g_warning ("A floating object was finalized. This means that someone\n" 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" "called g_object_unref() on an object that had only a floating\n"
@ -16277,3 +16347,105 @@ _gtk_widget_get_action_group (GtkWidget *widget,
return gtk_action_muxer_lookup (widget->priv->muxer, prefix); return gtk_action_muxer_lookup (widget->priv->muxer, prefix);
return NULL; return NULL;
} }
static void
event_controller_notify_event_mask (GtkEventController *controller,
GParamSpec *pspec,
GtkWidget *widget)
{
GdkEventMask evmask;
evmask = gtk_event_controller_get_event_mask (controller);
gtk_widget_add_events_internal (widget, NULL, evmask);
}
void
gtk_widget_add_controller (GtkWidget *widget,
GtkEventController *controller,
GtkPropagationPhase phase)
{
EventControllerData *data;
GtkWidgetPrivate *priv;
GList *l;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (GTK_IS_EVENT_CONTROLLER (controller));
g_return_if_fail (phase == GTK_PHASE_CAPTURE ||
phase == GTK_PHASE_BUBBLE);
priv = widget->priv;
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
if (data->controller != controller)
continue;
data->propagation_phase = phase;
return;
}
data = g_new0 (EventControllerData, 1);
data->controller = g_object_ref (controller);
data->propagation_phase = phase;
data->evmask_notify_id =
g_signal_connect (controller, "notify::event-mask",
G_CALLBACK (event_controller_notify_event_mask), widget);
priv->event_controllers = g_list_prepend (priv->event_controllers, data);
}
void
gtk_widget_remove_controller (GtkWidget *widget,
GtkEventController *controller)
{
EventControllerData *data;
GtkWidgetPrivate *priv;
GList *l;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (GTK_IS_EVENT_CONTROLLER (controller));
priv = widget->priv;
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
if (data->controller != controller)
continue;
gtk_event_controller_reset (data->controller);
g_signal_handler_disconnect (data->controller, data->evmask_notify_id);
g_object_unref (data->controller);
g_free (data);
priv->event_controllers = g_list_delete_link (priv->event_controllers, l);
return;
}
}
GList *
gtk_widget_list_controllers (GtkWidget *widget,
GtkPropagationPhase phase)
{
EventControllerData *data;
GtkWidgetPrivate *priv;
GList *l, *retval = NULL;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
g_return_val_if_fail (phase == GTK_PHASE_CAPTURE ||
phase == GTK_PHASE_BUBBLE, NULL);
priv = widget->priv;
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
if (data->propagation_phase == phase)
retval = g_list_prepend (retval, data->controller);
}
return retval;
}

View File

@ -33,6 +33,7 @@
#include <gtk/gtkaccelgroup.h> #include <gtk/gtkaccelgroup.h>
#include <gtk/gtkborder.h> #include <gtk/gtkborder.h>
#include <gtk/gtktypes.h> #include <gtk/gtktypes.h>
#include <gtk/gtkeventcontroller.h>
#include <atk/atk.h> #include <atk/atk.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -1469,6 +1470,17 @@ void gtk_widget_class_bind_template_child_full (GtkWidgetClass *
gboolean internal_child, gboolean internal_child,
gssize struct_offset); gssize struct_offset);
GDK_AVAILABLE_IN_3_14
void gtk_widget_add_controller (GtkWidget *widget,
GtkEventController *controller,
GtkPropagationPhase phase);
GDK_AVAILABLE_IN_3_14
void gtk_widget_remove_controller (GtkWidget *widget,
GtkEventController *controller);
GDK_AVAILABLE_IN_3_14
GList * gtk_widget_list_controllers (GtkWidget *widget,
GtkPropagationPhase phase);
G_END_DECLS G_END_DECLS
#endif /* __GTK_WIDGET_H__ */ #endif /* __GTK_WIDGET_H__ */