mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2025-01-04 01:31:13 +00:00
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:
parent
d0da82fed6
commit
f1bb0283dd
@ -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,
|
||||
|
180
gtk/gtkwidget.c
180
gtk/gtkwidget.c
@ -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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user