diff --git a/gtk/gtkeventcontrollermotion.c b/gtk/gtkeventcontrollermotion.c index 5f40e94ade..377287742c 100644 --- a/gtk/gtkeventcontrollermotion.c +++ b/gtk/gtkeventcontrollermotion.c @@ -38,6 +38,9 @@ struct _GtkEventControllerMotion { GtkEventController parent_instance; + + guint is_pointer_focus : 1; + guint contains_pointer_focus : 1; }; struct _GtkEventControllerMotionClass @@ -52,14 +55,67 @@ enum { N_SIGNALS }; +enum { + PROP_IS_POINTER_FOCUS = 1, + PROP_CONTAINS_POINTER_FOCUS, + NUM_PROPERTIES +}; + +static GParamSpec *props[NUM_PROPERTIES] = { NULL, }; + static guint signals[N_SIGNALS] = { 0 }; G_DEFINE_TYPE (GtkEventControllerMotion, gtk_event_controller_motion, GTK_TYPE_EVENT_CONTROLLER) +static void +update_pointer_focus (GtkEventControllerMotion *motion, + gboolean enter, + GdkNotifyType detail) +{ + gboolean is_pointer; + gboolean contains_pointer; + + switch (detail) + { + case GDK_NOTIFY_VIRTUAL: + case GDK_NOTIFY_NONLINEAR_VIRTUAL: + is_pointer = FALSE; + contains_pointer = enter; + break; + case GDK_NOTIFY_ANCESTOR: + case GDK_NOTIFY_NONLINEAR: + is_pointer = enter; + contains_pointer = FALSE; + break; + case GDK_NOTIFY_INFERIOR: + is_pointer = enter; + contains_pointer = !enter; + break; + case GDK_NOTIFY_UNKNOWN: + default: + g_warning ("Unknown crossing detail"); + return; + } + + g_object_freeze_notify (G_OBJECT (motion)); + if (motion->is_pointer_focus != is_pointer) + { + motion->is_pointer_focus = is_pointer; + g_object_notify (G_OBJECT (motion), "is-pointer-focus"); + } + if (motion->contains_pointer_focus != contains_pointer) + { + motion->contains_pointer_focus = contains_pointer; + g_object_notify (G_OBJECT (motion), "contains-pointer-focus"); + } + g_object_thaw_notify (G_OBJECT (motion)); +} + static gboolean gtk_event_controller_motion_handle_event (GtkEventController *controller, const GdkEvent *event) { + GtkEventControllerMotion *motion = GTK_EVENT_CONTROLLER_MOTION (controller); GtkEventControllerClass *parent_class; GdkEventType type; @@ -73,6 +129,9 @@ gtk_event_controller_motion_handle_event (GtkEventController *controller, gdk_event_get_coords (event, &x, &y); gdk_event_get_crossing_mode (event, &mode); gdk_event_get_crossing_detail (event, &detail); + + update_pointer_focus (motion, TRUE, detail); + g_signal_emit (controller, signals[ENTER], 0, x, y, mode, detail); } else if (type == GDK_LEAVE_NOTIFY) @@ -82,6 +141,9 @@ gtk_event_controller_motion_handle_event (GtkEventController *controller, gdk_event_get_crossing_mode (event, &mode); gdk_event_get_crossing_detail (event, &detail); + + update_pointer_focus (motion, FALSE, detail); + g_signal_emit (controller, signals[LEAVE], 0, mode, detail); } else if (type == GDK_MOTION_NOTIFY) @@ -97,13 +159,74 @@ gtk_event_controller_motion_handle_event (GtkEventController *controller, return parent_class->handle_event (controller, event); } +static void +gtk_event_controller_motion_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkEventControllerMotion *controller = GTK_EVENT_CONTROLLER_MOTION (object); + + switch (prop_id) + { + case PROP_IS_POINTER_FOCUS: + g_value_set_boolean (value, controller->is_pointer_focus); + break; + + case PROP_CONTAINS_POINTER_FOCUS: + g_value_set_boolean (value, controller->contains_pointer_focus); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + static void gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); + object_class->get_property = gtk_event_controller_motion_get_property; + controller_class->handle_event = gtk_event_controller_motion_handle_event; + /** + * GtkEventControllerMotion:is-pointer-focus: + * + * Whether the pointer is in the controllers widget itself, + * as opposed to in a descendent widget. See + * #GtkEventControllerMotion:contains-pointer-focus. + * + * When handling crossing events, this property is updated + * before #GtkEventControllerMotion::enter or + * #GtkEventControllerMotion::leave are emitted. + */ + props[PROP_IS_POINTER_FOCUS] = + g_param_spec_boolean ("is-pointer-focus", + P_("Is Pointer Focus"), + P_("Whether the pointer is in the controllers widget"), + FALSE, + G_PARAM_READABLE); + + /** + * GtkEventControllerMotion:contains-pointer-focus: + * + * Whether the pointer is in a descendant of the controllers widget. + * See #GtkEventControllerMotion:is-pointer-focus. + * + * When handling crossing events, this property is updated + * before #GtkEventControllerMotion::enter or + * #GtkEventControllerMotion::leave are emitted. + */ + props[PROP_CONTAINS_POINTER_FOCUS] = + g_param_spec_boolean ("contains-pointer-focus", + P_("Contains Pointer Focus"), + P_("Whether the pointer is in a descendant of the controllers widget"), + FALSE, + G_PARAM_READABLE); + /** * GtkEventControllerMotion::enter: * @controller: The object that received the signal