Commit f1bb0283 authored by Carlos Garnacho's avatar Carlos Garnacho

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.
parent d0da82fe
......@@ -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,
......
......@@ -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;
}
......@@ -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__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment