1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2017, Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author(s): Matthias Clasen <mclasen@redhat.com>
18 */
19
20/**
21 * GtkEventControllerMotion:
22 *
23 * `GtkEventControllerMotion` is an event controller tracking the pointer
24 * position.
25 *
26 * The event controller offers [signal@Gtk.EventControllerMotion::enter]
27 * and [signal@Gtk.EventControllerMotion::leave] signals, as well as
28 * [property@Gtk.EventControllerMotion:is-pointer] and
29 * [property@Gtk.EventControllerMotion:contains-pointer] properties
30 * which are updated to reflect changes in the pointer position as it
31 * moves over the widget.
32 */
33#include "config.h"
34
35#include "gtkintl.h"
36#include "gtkprivate.h"
37#include "gtkwidgetprivate.h"
38#include "gtkmarshalers.h"
39#include "gtkeventcontrollerprivate.h"
40#include "gtkeventcontrollermotion.h"
41#include "gtktypebuiltins.h"
42#include "gtkmarshalers.h"
43
44struct _GtkEventControllerMotion
45{
46 GtkEventController parent_instance;
47
48 guint is_pointer : 1;
49 guint contains_pointer : 1;
50};
51
52struct _GtkEventControllerMotionClass
53{
54 GtkEventControllerClass parent_class;
55};
56
57enum {
58 ENTER,
59 LEAVE,
60 MOTION,
61 N_SIGNALS
62};
63
64enum {
65 PROP_IS_POINTER = 1,
66 PROP_CONTAINS_POINTER,
67 NUM_PROPERTIES
68};
69
70static GParamSpec *props[NUM_PROPERTIES] = { NULL, };
71
72static guint signals[N_SIGNALS] = { 0 };
73
74G_DEFINE_TYPE (GtkEventControllerMotion, gtk_event_controller_motion, GTK_TYPE_EVENT_CONTROLLER)
75
76static gboolean
77gtk_event_controller_motion_handle_event (GtkEventController *controller,
78 GdkEvent *event,
79 double x,
80 double y)
81{
82 GtkEventControllerClass *parent_class;
83 GdkEventType type;
84
85 type = gdk_event_get_event_type (event);
86 if (type == GDK_MOTION_NOTIFY)
87 g_signal_emit (instance: controller, signal_id: signals[MOTION], detail: 0, x, y);
88
89 parent_class = GTK_EVENT_CONTROLLER_CLASS (gtk_event_controller_motion_parent_class);
90
91 return parent_class->handle_event (controller, event, x, y);
92}
93
94static void
95update_pointer_focus (GtkEventController *controller,
96 const GtkCrossingData *crossing,
97 double x,
98 double y)
99{
100 GtkEventControllerMotion *motion = GTK_EVENT_CONTROLLER_MOTION (controller);
101 GtkWidget *widget = gtk_event_controller_get_widget (controller);
102 gboolean is_pointer = FALSE;
103 gboolean contains_pointer = FALSE;
104 gboolean enter = FALSE;
105 gboolean leave = FALSE;
106
107 if (crossing->direction == GTK_CROSSING_IN)
108 {
109 if (crossing->new_descendent != NULL)
110 {
111 contains_pointer = TRUE;
112 }
113 if (crossing->new_target == widget)
114 {
115 contains_pointer = TRUE;
116 is_pointer = TRUE;
117 }
118 }
119 else
120 {
121 if (crossing->new_descendent != NULL ||
122 crossing->new_target == widget)
123 contains_pointer = TRUE;
124 is_pointer = FALSE;
125 }
126
127 if (motion->contains_pointer != contains_pointer)
128 {
129 enter = contains_pointer;
130 leave = !contains_pointer;
131 }
132
133 if (leave)
134 g_signal_emit (instance: controller, signal_id: signals[LEAVE], detail: 0);
135
136 g_object_freeze_notify (G_OBJECT (motion));
137 if (motion->is_pointer != is_pointer)
138 {
139 motion->is_pointer = is_pointer;
140 g_object_notify_by_pspec (G_OBJECT (motion), pspec: props[PROP_IS_POINTER]);
141 }
142 if (motion->contains_pointer != contains_pointer)
143 {
144 motion->contains_pointer = contains_pointer;
145 g_object_notify_by_pspec (G_OBJECT (motion), pspec: props[PROP_CONTAINS_POINTER]);
146 }
147 g_object_thaw_notify (G_OBJECT (motion));
148
149 if (enter)
150 g_signal_emit (instance: controller, signal_id: signals[ENTER], detail: 0, x, y);
151}
152
153static void
154gtk_event_controller_motion_handle_crossing (GtkEventController *controller,
155 const GtkCrossingData *crossing,
156 double x,
157 double y)
158{
159 if (crossing->type == GTK_CROSSING_POINTER)
160 update_pointer_focus (controller, crossing, x, y);
161}
162
163static void
164gtk_event_controller_motion_get_property (GObject *object,
165 guint prop_id,
166 GValue *value,
167 GParamSpec *pspec)
168{
169 GtkEventControllerMotion *controller = GTK_EVENT_CONTROLLER_MOTION (object);
170
171 switch (prop_id)
172 {
173 case PROP_IS_POINTER:
174 g_value_set_boolean (value, v_boolean: controller->is_pointer);
175 break;
176
177 case PROP_CONTAINS_POINTER:
178 g_value_set_boolean (value, v_boolean: controller->contains_pointer);
179 break;
180
181 default:
182 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183 }
184}
185
186static void
187gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass)
188{
189 GObjectClass *object_class = G_OBJECT_CLASS (klass);
190 GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
191
192 object_class->get_property = gtk_event_controller_motion_get_property;
193
194 controller_class->handle_event = gtk_event_controller_motion_handle_event;
195 controller_class->handle_crossing = gtk_event_controller_motion_handle_crossing;
196
197 /**
198 * GtkEventControllerMotion:is-pointer: (attributes org.gtk.Property.get=gtk_event_controller_motion_is_pointer)
199 *
200 * Whether the pointer is in the controllers widget itself,
201 * as opposed to in a descendent widget.
202 *
203 * See also [property@Gtk.EventControllerMotion:contains-pointer].
204 *
205 * When handling crossing events, this property is updated
206 * before [signal@Gtk.EventControllerMotion::enter], but after
207 * [signal@Gtk.EventControllerMotion::leave] is emitted.
208 */
209 props[PROP_IS_POINTER] =
210 g_param_spec_boolean (name: "is-pointer",
211 P_("Is Pointer"),
212 P_("Whether the pointer is in the controllers widget"),
213 FALSE,
214 flags: G_PARAM_READABLE);
215
216 /**
217 * GtkEventControllerMotion:contains-pointer: (attributes org.gtk.Property.get=gtk_event_controller_motion_contains_pointer)
218 *
219 * Whether the pointer is in the controllers widget or a descendant.
220 *
221 * See also [property@Gtk.EventControllerMotion:is-pointer].
222 *
223 * When handling crossing events, this property is updated
224 * before [signal@Gtk.EventControllerMotion::enter], but after
225 * [signal@Gtk.EventControllerMotion::leave] is emitted.
226 */
227 props[PROP_CONTAINS_POINTER] =
228 g_param_spec_boolean (name: "contains-pointer",
229 P_("Contains Pointer"),
230 P_("Whether the pointer is in the controllers widget or a descendant"),
231 FALSE,
232 flags: G_PARAM_READABLE);
233
234 g_object_class_install_properties (oclass: object_class, n_pspecs: NUM_PROPERTIES, pspecs: props);
235
236 /**
237 * GtkEventControllerMotion::enter:
238 * @controller: the object which received the signal
239 * @x: coordinates of pointer location
240 * @y: coordinates of pointer location
241 *
242 * Signals that the pointer has entered the widget.
243 */
244 signals[ENTER] =
245 g_signal_new (I_("enter"),
246 GTK_TYPE_EVENT_CONTROLLER_MOTION,
247 signal_flags: G_SIGNAL_RUN_LAST,
248 class_offset: 0, NULL, NULL,
249 NULL,
250 G_TYPE_NONE, n_params: 2,
251 G_TYPE_DOUBLE,
252 G_TYPE_DOUBLE);
253
254 /**
255 * GtkEventControllerMotion::leave:
256 * @controller: the object which received the signal
257 *
258 * Signals that the pointer has left the widget.
259 */
260 signals[LEAVE] =
261 g_signal_new (I_("leave"),
262 GTK_TYPE_EVENT_CONTROLLER_MOTION,
263 signal_flags: G_SIGNAL_RUN_LAST,
264 class_offset: 0, NULL, NULL,
265 NULL,
266 G_TYPE_NONE, n_params: 0);
267
268 /**
269 * GtkEventControllerMotion::motion:
270 * @controller: The object that received the signal
271 * @x: the x coordinate
272 * @y: the y coordinate
273 *
274 * Emitted when the pointer moves inside the widget.
275 */
276 signals[MOTION] =
277 g_signal_new (I_("motion"),
278 GTK_TYPE_EVENT_CONTROLLER_MOTION,
279 signal_flags: G_SIGNAL_RUN_FIRST,
280 class_offset: 0, NULL, NULL,
281 c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE,
282 G_TYPE_NONE, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
283 g_signal_set_va_marshaller (signal_id: signals[MOTION],
284 G_TYPE_FROM_CLASS (klass),
285 va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLEv);
286}
287
288static void
289gtk_event_controller_motion_init (GtkEventControllerMotion *motion)
290{
291}
292
293/**
294 * gtk_event_controller_motion_new:
295 *
296 * Creates a new event controller that will handle motion events.
297 *
298 * Returns: a new `GtkEventControllerMotion`
299 **/
300GtkEventController *
301gtk_event_controller_motion_new (void)
302{
303 return g_object_new (GTK_TYPE_EVENT_CONTROLLER_MOTION,
304 NULL);
305}
306
307/**
308 * gtk_event_controller_motion_contains_pointer: (attributes org.gtk.Method.get_property=contains-pointer)
309 * @self: a `GtkEventControllerMotion`
310 *
311 * Returns if a pointer is within @self or one of its children.
312 *
313 * Returns: %TRUE if a pointer is within @self or one of its children
314 */
315gboolean
316gtk_event_controller_motion_contains_pointer (GtkEventControllerMotion *self)
317{
318 g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_MOTION (self), FALSE);
319
320 return self->contains_pointer;
321}
322
323/**
324 * gtk_event_controller_motion_is_pointer: (attributes org.gtk.Method.get_property=is-pointer)
325 * @self: a `GtkEventControllerMotion`
326 *
327 * Returns if a pointer is within @self, but not one of its children.
328 *
329 * Returns: %TRUE if a pointer is within @self but not one of its children
330 */
331gboolean
332gtk_event_controller_motion_is_pointer (GtkEventControllerMotion *self)
333{
334 g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_MOTION (self), FALSE);
335
336 return self->is_pointer;
337}
338

source code of gtk/gtk/gtkeventcontrollermotion.c