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
} GtkInputHints;
typedef enum
{
GTK_PHASE_CAPTURE,
GTK_PHASE_BUBBLE
} GtkPropagationPhase;
typedef enum
{
GTK_EVENT_SEQUENCE_NONE,

View File

@ -396,6 +396,12 @@ typedef struct {
GDestroyNotify destroy_notify;
} GtkWidgetTemplate;
typedef struct {
GtkEventController *controller;
guint evmask_notify_id;
guint propagation_phase : 2;
} EventControllerData;
struct _GtkWidgetPrivate
{
/* 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 () */
guint verifying_invariants_count;
#endif /* G_ENABLE_DEBUG */
GList *event_controllers;
};
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);
}
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
_gtk_widget_captured_event (GtkWidget *widget,
GdkEvent *event)
@ -6951,13 +7001,15 @@ _gtk_widget_captured_event (GtkWidget *widget,
if (!event_window_is_still_viewable (event))
return TRUE;
return_val = _gtk_widget_run_controllers (widget, event, GTK_PHASE_CAPTURE);
handler = g_object_get_data (G_OBJECT (widget), "captured-event-handler");
if (!handler)
return FALSE;
return return_val;
g_object_ref (widget);
return_val = handler (widget, event);
return_val |= handler (widget, event);
return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
/* The widget that was originally to receive the event
@ -7155,6 +7207,7 @@ gtk_widget_event_internal (GtkWidget *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);
return_val |= !WIDGET_REALIZED_FOR_EVENT (widget, event);
if (!return_val)
@ -10701,8 +10754,11 @@ gtk_widget_add_events_internal_list (GtkWidget *widget,
gint events,
GList *window_list)
{
GdkEventMask controllers_mask;
GList *l;
controllers_mask = _gtk_widget_get_controllers_evmask (widget);
for (l = window_list; l != NULL; l = l->next)
{
GdkWindow *window = l->data;
@ -10714,9 +10770,16 @@ gtk_widget_add_events_internal_list (GtkWidget *widget,
GList *children;
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
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);
gtk_widget_add_events_internal_list (widget, device, events, children);
@ -11591,6 +11654,7 @@ gtk_widget_finalize (GObject *object)
GtkWidgetPrivate *priv = widget->priv;
GtkWidgetAuxInfo *aux_info;
GtkAccessible *accessible;
EventControllerData *data;
gtk_grab_remove (widget);
@ -11618,6 +11682,12 @@ gtk_widget_finalize (GObject *object)
_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))
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"
@ -16277,3 +16347,105 @@ _gtk_widget_get_action_group (GtkWidget *widget,
return gtk_action_muxer_lookup (widget->priv->muxer, prefix);
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/gtkborder.h>
#include <gtk/gtktypes.h>
#include <gtk/gtkeventcontroller.h>
#include <atk/atk.h>
G_BEGIN_DECLS
@ -1469,6 +1470,17 @@ void gtk_widget_class_bind_template_child_full (GtkWidgetClass *
gboolean internal_child,
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
#endif /* __GTK_WIDGET_H__ */