From 747be0f4999bc9e855cc1f6a4b98ccd371835d49 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 15 Sep 2017 13:22:57 +0200 Subject: [PATCH 01/14] gtk: Add GtkEventControllerScroll This is a GtkEventController implementation to handle mouse scrolling. It handles both smooth and discrete events and offers a way for callers to tell their preference too, so smooth events shall be accumulated and coalesced on request. On capable devices, it can also emit ::scroll-begin and ::scroll-end enclosing all ::scroll events for a scroll operation. It also has builtin kinetic scrolling capabilities, reporting the initial velocity for both axes after ::scroll-end if requested. --- gtk/Makefile.am | 2 + gtk/gtk.h | 1 + gtk/gtkeventcontrollerscroll.c | 401 +++++++++++++++++++++++++++++++++ gtk/gtkeventcontrollerscroll.h | 66 ++++++ 4 files changed, 470 insertions(+) create mode 100644 gtk/gtkeventcontrollerscroll.c create mode 100644 gtk/gtkeventcontrollerscroll.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 56b41a1c90..37336d540f 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -192,6 +192,7 @@ gtk_public_h_sources = \ gtkenums.h \ gtkeventbox.h \ gtkeventcontroller.h \ + gtkeventcontrollerscroll.h \ gtkexpander.h \ gtkfilechooser.h \ gtkfilechooserbutton.h \ @@ -757,6 +758,7 @@ gtk_base_c_sources = \ gtkentrycompletion.c \ gtkeventbox.c \ gtkeventcontroller.c \ + gtkeventcontrollerscroll.c \ gtkexpander.c \ gtkfilechooser.c \ gtkfilechooserbutton.c \ diff --git a/gtk/gtk.h b/gtk/gtk.h index 7b901e1ece..1ce5e5b37e 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -94,6 +94,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkeventcontrollerscroll.c b/gtk/gtkeventcontrollerscroll.c new file mode 100644 index 0000000000..61f916782c --- /dev/null +++ b/gtk/gtkeventcontrollerscroll.c @@ -0,0 +1,401 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ + +#include "config.h" + +#include "gtkintl.h" +#include "gtkwidget.h" +#include "gtkeventcontrollerprivate.h" +#include "gtkeventcontrollerscroll.h" +#include "gtktypebuiltins.h" +#include "gtkmarshalers.h" + +#define SCROLL_CAPTURE_THRESHOLD_MS 150 + +typedef struct +{ + gdouble dx; + gdouble dy; + guint32 evtime; +} ScrollHistoryElem; + +struct _GtkEventControllerScroll +{ + GtkEventController parent_instance; + GtkEventControllerScrollFlags flags; + GArray *scroll_history; + + /* For discrete event coalescing */ + gdouble cur_dx; + gdouble cur_dy; + + guint active : 1; +}; + +struct _GtkEventControllerScrollClass +{ + GtkEventControllerClass parent_class; +}; + +enum { + SCROLL_BEGIN, + SCROLL, + SCROLL_END, + DECELERATE, + N_SIGNALS +}; + +enum { + PROP_0, + PROP_FLAGS, + N_PROPS +}; + +static GParamSpec *pspecs[N_PROPS] = { NULL }; +static guint signals[N_SIGNALS] = { 0 }; + +G_DEFINE_TYPE (GtkEventControllerScroll, gtk_event_controller_scroll, + GTK_TYPE_EVENT_CONTROLLER) + +static void +scroll_history_push (GtkEventControllerScroll *scroll, + gdouble delta_x, + gdouble delta_y, + guint32 evtime) +{ + ScrollHistoryElem new_item; + guint i; + + for (i = 0; i < scroll->scroll_history->len; i++) + { + ScrollHistoryElem *elem; + + elem = &g_array_index (scroll->scroll_history, ScrollHistoryElem, i); + + if (elem->evtime >= evtime - SCROLL_CAPTURE_THRESHOLD_MS) + break; + } + + if (i > 0) + g_array_remove_range (scroll->scroll_history, 0, i); + + new_item.dx = delta_x; + new_item.dy = delta_y; + new_item.evtime = evtime; + g_array_append_val (scroll->scroll_history, new_item); +} + +static void +scroll_history_reset (GtkEventControllerScroll *scroll) +{ + if (scroll->scroll_history->len == 0) + return; + + g_array_remove_range (scroll->scroll_history, 0, + scroll->scroll_history->len); +} + +static void +scroll_history_finish (GtkEventControllerScroll *scroll, + gdouble *velocity_x, + gdouble *velocity_y) +{ + gdouble accum_dx = 0, accum_dy = 0; + guint32 first = 0, last = 0; + guint i; + + *velocity_x = 0; + *velocity_y = 0; + + if (scroll->scroll_history->len == 0) + return; + + for (i = 0; i < scroll->scroll_history->len; i++) + { + ScrollHistoryElem *elem; + + elem = &g_array_index (scroll->scroll_history, ScrollHistoryElem, i); + accum_dx += elem->dx; + accum_dy += elem->dy; + last = elem->evtime; + + if (i == 0) + first = elem->evtime; + } + + if (last != first) + { + *velocity_x = (accum_dx * 1000) / (last - first); + *velocity_y = (accum_dy * 1000) / (last - first); + } + + scroll_history_reset (scroll); +} + +static void +gtk_event_controller_scroll_finalize (GObject *object) +{ + GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object); + + g_array_unref (scroll->scroll_history); + + G_OBJECT_CLASS (gtk_event_controller_scroll_parent_class)->finalize (object); +} + +static void +gtk_event_controller_scroll_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object); + + switch (prop_id) + { + case PROP_FLAGS: + scroll->flags = g_value_get_flags (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_event_controller_scroll_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object); + + switch (prop_id) + { + case PROP_FLAGS: + g_value_set_flags (value, scroll->flags); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gtk_event_controller_scroll_handle_event (GtkEventController *controller, + const GdkEvent *event) +{ + GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (controller); + GdkScrollDirection direction = GDK_SCROLL_SMOOTH; + gdouble dx = 0, dy = 0; + + if (gdk_event_get_event_type (event) != GDK_SCROLL) + return FALSE; + if ((scroll->flags & (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | + GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL)) == 0) + return FALSE; + + /* FIXME: Handle device changes */ + + if (gdk_event_get_scroll_deltas (event, &dx, &dy)) + { + GdkDevice *device = gdk_event_get_source_device (event); + GdkInputSource input_source = gdk_device_get_source (device); + + if (!scroll->active && + (input_source == GDK_SOURCE_TRACKPOINT || + input_source == GDK_SOURCE_TOUCHPAD)) + { + g_signal_emit (controller, signals[SCROLL_BEGIN], 0); + scroll_history_reset (scroll); + scroll->active = TRUE; + } + + if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_VERTICAL) == 0) + dy = 0; + if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL) == 0) + dx = 0; + + if (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_DISCRETE) + { + gint steps; + + scroll->cur_dx += dx; + scroll->cur_dy += dy; + dx = dy = 0; + + if (ABS (scroll->cur_dx) > 1) + { + steps = trunc (scroll->cur_dx); + scroll->cur_dx -= steps; + dx = steps; + } + + if (ABS (scroll->cur_dy) > 1) + { + steps = trunc (scroll->cur_dy); + scroll->cur_dy -= steps; + dy = steps; + } + } + } + else if (gdk_event_get_scroll_direction (event, &direction)) + { + switch (direction) + { + case GDK_SCROLL_UP: + dy -= 1; + break; + case GDK_SCROLL_DOWN: + dy += 1; + break; + case GDK_SCROLL_LEFT: + dx -= 1; + break; + case GDK_SCROLL_RIGHT: + dx += 1; + break; + default: + g_assert_not_reached (); + break; + } + + if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_VERTICAL) == 0) + dy = 0; + if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL) == 0) + dx = 0; + } + + if (dx != 0 || dy != 0) + { + g_signal_emit (controller, signals[SCROLL], 0, dx, dy); + + if (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_KINETIC) + scroll_history_push (scroll, dx, dy, gdk_event_get_time (event)); + } + + if (scroll->active && gdk_event_is_scroll_stop_event (event)) + { + g_signal_emit (controller, signals[SCROLL_END], 0); + scroll->active = FALSE; + + if (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_KINETIC) + { + gdouble vel_x, vel_y; + + scroll_history_finish (scroll, &vel_x, &vel_y); + g_signal_emit (controller, signals[DECELERATE], 0, vel_x, vel_y); + } + } + + return TRUE; +} + +static void +gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) +{ + GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gtk_event_controller_scroll_finalize; + object_class->set_property = gtk_event_controller_scroll_set_property; + object_class->get_property = gtk_event_controller_scroll_get_property; + + controller_class->handle_event = gtk_event_controller_scroll_handle_event; + + pspecs[PROP_FLAGS] = + g_param_spec_flags ("flags", + P_("Flags"), + P_("Flags"), + GTK_TYPE_EVENT_CONTROLLER_SCROLL_FLAGS, + GTK_EVENT_CONTROLLER_SCROLL_NONE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + + signals[SCROLL_BEGIN] = + g_signal_new (I_("scroll-begin"), + GTK_TYPE_EVENT_CONTROLLER_SCROLL, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SCROLL] = + g_signal_new (I_("scroll"), + GTK_TYPE_EVENT_CONTROLLER_SCROLL, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + _gtk_marshal_VOID__DOUBLE_DOUBLE, + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + signals[SCROLL_END] = + g_signal_new (I_("scroll-end"), + GTK_TYPE_EVENT_CONTROLLER_SCROLL, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[DECELERATE] = + g_signal_new (I_("decelerate"), + GTK_TYPE_EVENT_CONTROLLER_SCROLL, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + _gtk_marshal_VOID__DOUBLE_DOUBLE, + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + + g_object_class_install_properties (object_class, N_PROPS, pspecs); +} + +static void +gtk_event_controller_scroll_init (GtkEventControllerScroll *scroll) +{ + scroll->scroll_history = g_array_new (FALSE, FALSE, + sizeof (ScrollHistoryElem)); +} + +GtkEventController * +gtk_event_controller_scroll_new (GtkWidget *widget, + GtkEventControllerScrollFlags flags) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + return g_object_new (GTK_TYPE_EVENT_CONTROLLER_SCROLL, + "widget", widget, + "flags", flags, + NULL); +} + +void +gtk_event_controller_scroll_set_flags (GtkEventControllerScroll *scroll, + GtkEventControllerScrollFlags flags) +{ + g_return_if_fail (GTK_IS_EVENT_CONTROLLER_SCROLL (scroll)); + + if (scroll->flags != flags) + { + scroll->flags = flags; + g_object_notify (G_OBJECT (scroll), "flags"); + } +} + +GtkEventControllerScrollFlags +gtk_event_controller_scroll_get_flags (GtkEventControllerScroll *scroll) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_SCROLL (scroll), + GTK_EVENT_CONTROLLER_SCROLL_NONE); + + return scroll->flags; +} diff --git a/gtk/gtkeventcontrollerscroll.h b/gtk/gtkeventcontrollerscroll.h new file mode 100644 index 0000000000..a7303f88d8 --- /dev/null +++ b/gtk/gtkeventcontrollerscroll.h @@ -0,0 +1,66 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ + +#ifndef __GTK_EVENT_CONTROLLER_SCROLL_H__ +#define __GTK_EVENT_CONTROLLER_SCROLL_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_EVENT_CONTROLLER_SCROLL (gtk_event_controller_scroll_get_type ()) +#define GTK_EVENT_CONTROLLER_SCROLL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_SCROLL, GtkEventControllerScroll)) +#define GTK_EVENT_CONTROLLER_SCROLL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_SCROLL, GtkEventControllerScrollClass)) +#define GTK_IS_EVENT_CONTROLLER_SCROLL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_SCROLL)) +#define GTK_IS_EVENT_CONTROLLER_SCROLL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_SCROLL)) +#define GTK_EVENT_CONTROLLER_SCROLL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_SCROLL, GtkEventControllerScrollClass)) + +typedef struct _GtkEventControllerScroll GtkEventControllerScroll; +typedef struct _GtkEventControllerScrollClass GtkEventControllerScrollClass; + +typedef enum { + GTK_EVENT_CONTROLLER_SCROLL_NONE = 0, + GTK_EVENT_CONTROLLER_SCROLL_VERTICAL = 1 << 0, + GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL = 1 << 1, + GTK_EVENT_CONTROLLER_SCROLL_DISCRETE = 1 << 2, + GTK_EVENT_CONTROLLER_SCROLL_KINETIC = 1 << 3, + GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES = (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL), +} GtkEventControllerScrollFlags; + +GDK_AVAILABLE_IN_3_24 +GType gtk_event_controller_scroll_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_3_24 +GtkEventController *gtk_event_controller_scroll_new (GtkWidget *widget, + GtkEventControllerScrollFlags flags); +GDK_AVAILABLE_IN_3_24 +void gtk_event_controller_scroll_set_flags (GtkEventControllerScroll *controller, + GtkEventControllerScrollFlags flags); +GDK_AVAILABLE_IN_3_24 +GtkEventControllerScrollFlags + gtk_event_controller_scroll_get_flags (GtkEventControllerScroll *controller); + +G_END_DECLS + +#endif /* __GTK_EVENT_CONTROLLER_SCROLL_H__ */ From 7b8c036f8fd1c351d6816250283d9651f625c4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Wed, 11 Oct 2017 08:06:33 +0200 Subject: [PATCH 02/14] eventcontrollerscroll: Emit ::scroll for -1/+1 discrete steps The > 1 meant it only emits the signal for -2/+2 steps. --- gtk/gtkeventcontrollerscroll.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtk/gtkeventcontrollerscroll.c b/gtk/gtkeventcontrollerscroll.c index 61f916782c..57991db1ae 100644 --- a/gtk/gtkeventcontrollerscroll.c +++ b/gtk/gtkeventcontrollerscroll.c @@ -239,14 +239,14 @@ gtk_event_controller_scroll_handle_event (GtkEventController *controller, scroll->cur_dy += dy; dx = dy = 0; - if (ABS (scroll->cur_dx) > 1) + if (ABS (scroll->cur_dx) >= 1) { steps = trunc (scroll->cur_dx); scroll->cur_dx -= steps; dx = steps; } - if (ABS (scroll->cur_dy) > 1) + if (ABS (scroll->cur_dy) >= 1) { steps = trunc (scroll->cur_dy); scroll->cur_dy -= steps; From 448551f31c0d7323b593d9ac7e293a1df8ba57aa Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sat, 2 Dec 2017 00:18:07 +0100 Subject: [PATCH 03/14] gtkeventcontrollerscroll: Add some docs --- docs/reference/gtk/gtk3.types.in | 1 + gtk/gtkeventcontrollerscroll.c | 114 +++++++++++++++++++++++++++++++ gtk/gtkeventcontrollerscroll.h | 14 ++++ 3 files changed, 129 insertions(+) diff --git a/docs/reference/gtk/gtk3.types.in b/docs/reference/gtk/gtk3.types.in index 9f7d51234e..45b28bc20a 100644 --- a/docs/reference/gtk/gtk3.types.in +++ b/docs/reference/gtk/gtk3.types.in @@ -65,6 +65,7 @@ gtk_entry_completion_get_type gtk_entry_get_type gtk_event_box_get_type gtk_event_controller_get_type +gtk_event_controller_scroll_get_type gtk_expander_get_type gtk_file_chooser_button_get_type gtk_file_chooser_dialog_get_type diff --git a/gtk/gtkeventcontrollerscroll.c b/gtk/gtkeventcontrollerscroll.c index 57991db1ae..f0816a5d65 100644 --- a/gtk/gtkeventcontrollerscroll.c +++ b/gtk/gtkeventcontrollerscroll.c @@ -17,6 +17,47 @@ * Author(s): Carlos Garnacho */ +/** + * SECTION:gtkeventcontrollerscroll + * @Short_description: Event controller for scroll events + * @Title: GtkEventControllerScroll + * @See_also: #GtkEventController + * + * #GtkEventControllerScroll is an event controller meant to handle + * scroll events from mice and touchpads. It is capable of handling + * both discrete and continuous scroll events, abstracting them both + * on the #GtkEventControllerScroll::scroll signal (deltas in the + * discrete case are multiples of 1). + * + * In the case of continuous scroll events, #GtkEventControllerScroll + * encloses all #GtkEventControllerScroll::scroll events between two + * #GtkEventControllerScroll::scroll-begin and #GtkEventControllerScroll::scroll-end + * signals. + * + * The behavior of the event controller can be modified by the + * flags given at creation time, or modified at a later point through + * gtk_event_controller_scroll_set_flags() (e.g. because the scrolling + * conditions of the widget changed). + * + * The controller can be set up to emit motion for either/both vertical + * and horizontal scroll events through #GTK_EVENT_CONTROLLER_SCROLL_VERTICAL, + * #GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL and #GTK_EVENT_CONTROLLER_SCROLL_BOTH. + * If any axis is disabled, the respective #GtkEventControllerScroll::scroll + * delta will be 0. Vertical scroll events will be translated to horizontal + * motion for the devices incapable of horizontal scrolling. + * + * The event controller can also be forced to emit discrete events on all devices + * through #GTK_EVENT_CONTROLLER_SCROLL_DISCRETE. This can be used to implement + * discrete actions triggered through scroll events (e.g. switching across + * combobox options). + * + * The #GTK_EVENT_CONTROLLER_SCROLL_KINETIC flag toggles the emission of the + * #GtkEventControllerScroll::decelerate signal, emitted at the end of scrolling + * with two X/Y velocity arguments that are consistent with the motion that + * was received. + * + * This object was added in 3.93. + **/ #include "config.h" #include "gtkintl.h" @@ -318,6 +359,13 @@ gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) controller_class->handle_event = gtk_event_controller_scroll_handle_event; + /** + * GtkEventControllerScroll:flags: + * + * The flags affecting event controller behavior + * + * Since: 3.93 + **/ pspecs[PROP_FLAGS] = g_param_spec_flags ("flags", P_("Flags"), @@ -327,6 +375,13 @@ gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + /** + * GtkEventControllerScroll::scroll-begin: + * @controller: The object that received the signal + * + * Signals that a new scrolling operation has begun. It will + * only be emitted on devices capable of it. + **/ signals[SCROLL_BEGIN] = g_signal_new (I_("scroll-begin"), GTK_TYPE_EVENT_CONTROLLER_SCROLL, @@ -334,6 +389,15 @@ gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + /** + * GtkEventControllerScroll::scroll: + * @controller: The object that received the signal + * @dx: X delta + * @dy: Y delta + * + * Signals that the widget should scroll by the + * amount specified by @dx and @dy. + **/ signals[SCROLL] = g_signal_new (I_("scroll"), GTK_TYPE_EVENT_CONTROLLER_SCROLL, @@ -341,6 +405,13 @@ gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) 0, NULL, NULL, _gtk_marshal_VOID__DOUBLE_DOUBLE, G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + /** + * GtkEventControllerScroll::scroll-end: + * @controller: The object that received the signal + * + * Signals that a new scrolling operation has finished. It will + * only be emitted on devices capable of it. + **/ signals[SCROLL_END] = g_signal_new (I_("scroll-end"), GTK_TYPE_EVENT_CONTROLLER_SCROLL, @@ -348,6 +419,18 @@ gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + /** + * GtkEventControllerScroll::decelerate: + * @controller: The object that received the signal + * @vel_x: X velocity + * @vel_y: Y velocity + * + * Emitted after scroll is finished if the #GTK_EVENT_CONTROLLER_SCROLL_KINETIC + * flag is set. @vel_x and @vel_y express the initial velocity that was + * imprinted by the scroll events. @vel_x and @vel_y are expressed in + * pixels/ms. + **/ signals[DECELERATE] = g_signal_new (I_("decelerate"), GTK_TYPE_EVENT_CONTROLLER_SCROLL, @@ -366,6 +449,18 @@ gtk_event_controller_scroll_init (GtkEventControllerScroll *scroll) sizeof (ScrollHistoryElem)); } +/** + * gtk_event_controller_scroll_new: + * @widget: a #GtkWidget + * @flags: behavior flags + * + * Creates a new event controller that will handle scroll events + * for the given @widget. + * + * Returns: a new #GtkEventControllerScroll + * + * Since: 3.93 + **/ GtkEventController * gtk_event_controller_scroll_new (GtkWidget *widget, GtkEventControllerScrollFlags flags) @@ -378,6 +473,15 @@ gtk_event_controller_scroll_new (GtkWidget *widget, NULL); } +/** + * gtk_event_controller_scroll_set_flags: + * @scroll: a #GtkEventControllerScroll + * @flags: behavior flags + * + * Sets the flags conditioning scroll controller behavior. + * + * Since: 3.93 + **/ void gtk_event_controller_scroll_set_flags (GtkEventControllerScroll *scroll, GtkEventControllerScrollFlags flags) @@ -391,6 +495,16 @@ gtk_event_controller_scroll_set_flags (GtkEventControllerScroll *scroll, } } +/** + * gtk_event_controller_scroll_get_flags: + * @scroll: a #GtkEventControllerScroll + * + * Gets the flags conditioning the scroll controller behavior. + * + * Returns: the controller flags. + * + * Since: 3.93 + **/ GtkEventControllerScrollFlags gtk_event_controller_scroll_get_flags (GtkEventControllerScroll *scroll) { diff --git a/gtk/gtkeventcontrollerscroll.h b/gtk/gtkeventcontrollerscroll.h index a7303f88d8..d1d33366c7 100644 --- a/gtk/gtkeventcontrollerscroll.h +++ b/gtk/gtkeventcontrollerscroll.h @@ -39,6 +39,20 @@ G_BEGIN_DECLS typedef struct _GtkEventControllerScroll GtkEventControllerScroll; typedef struct _GtkEventControllerScrollClass GtkEventControllerScrollClass; +/** + * GtkEventControllerScrollFlags: + * @GTK_EVENT_CONTROLLER_SCROLL_NONE: Don't emit scroll. + * @GTK_EVENT_CONTROLLER_SCROLL_VERTICAL: Emit scroll with vertical deltas. + * @GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL: Emit scroll with horizontal deltas. + * @GTK_EVENT_CONTROLLER_SCROLL_DISCRETE: Only emit deltas that are multiples of 1. + * @GTK_EVENT_CONTROLLER_SCROLL_KINETIC: Emit #GtkEventControllerScroll::decelerate + * after continuous scroll finishes. + * @GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES: Emit scroll on both axes. + * + * Describes the behavior of a #GtkEventControllerScroll. + * + * Since: 3.93 + **/ typedef enum { GTK_EVENT_CONTROLLER_SCROLL_NONE = 0, GTK_EVENT_CONTROLLER_SCROLL_VERTICAL = 1 << 0, From 84d3bfb6fd13959b44ee8d6947d91e03f90fc8e6 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 31 Oct 2017 12:45:37 +0100 Subject: [PATCH 04/14] gtk: Remove CONSTRUCT_ONLY flag from GtkEventControllerScroll::flags There is a gtk_event_controller_scroll_set_flags() call that's meant to be called after construction (eg. due to scrolledwindow relayouts hiding/showing scrollbars). The property shouldn't be construct-only for consistence. --- gtk/gtkeventcontrollerscroll.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gtk/gtkeventcontrollerscroll.c b/gtk/gtkeventcontrollerscroll.c index f0816a5d65..1edf6cc4ea 100644 --- a/gtk/gtkeventcontrollerscroll.c +++ b/gtk/gtkeventcontrollerscroll.c @@ -372,8 +372,7 @@ gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) P_("Flags"), GTK_TYPE_EVENT_CONTROLLER_SCROLL_FLAGS, GTK_EVENT_CONTROLLER_SCROLL_NONE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY); + G_PARAM_READWRITE); /** * GtkEventControllerScroll::scroll-begin: From e7af3410cfc80375e2aafdb26c6291779473323d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 26 Dec 2017 20:04:07 -0500 Subject: [PATCH 05/14] Avoid excess notification for GdkEventControllerScroll::flags We have a test that checks this, and it is the right thing to do. --- gtk/gtkeventcontrollerscroll.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/gtk/gtkeventcontrollerscroll.c b/gtk/gtkeventcontrollerscroll.c index 1edf6cc4ea..1738e0173f 100644 --- a/gtk/gtkeventcontrollerscroll.c +++ b/gtk/gtkeventcontrollerscroll.c @@ -66,6 +66,7 @@ #include "gtkeventcontrollerscroll.h" #include "gtktypebuiltins.h" #include "gtkmarshalers.h" +#include "gtkprivate.h" #define SCROLL_CAPTURE_THRESHOLD_MS 150 @@ -210,7 +211,7 @@ gtk_event_controller_scroll_set_property (GObject *object, switch (prop_id) { case PROP_FLAGS: - scroll->flags = g_value_get_flags (value); + gtk_event_controller_scroll_set_flags (scroll, g_value_get_flags (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -372,7 +373,7 @@ gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) P_("Flags"), GTK_TYPE_EVENT_CONTROLLER_SCROLL_FLAGS, GTK_EVENT_CONTROLLER_SCROLL_NONE, - G_PARAM_READWRITE); + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); /** * GtkEventControllerScroll::scroll-begin: @@ -487,11 +488,11 @@ gtk_event_controller_scroll_set_flags (GtkEventControllerScroll *scroll, { g_return_if_fail (GTK_IS_EVENT_CONTROLLER_SCROLL (scroll)); - if (scroll->flags != flags) - { - scroll->flags = flags; - g_object_notify (G_OBJECT (scroll), "flags"); - } + if (scroll->flags == flags) + return; + + scroll->flags = flags; + g_object_notify_by_pspec (G_OBJECT (scroll), pspecs[PROP_FLAGS]); } /** From 8b2c3a8c1a8330e64f5908557900fdd976162e5a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 11 Dec 2017 18:29:33 -0500 Subject: [PATCH 06/14] Add a simple motion eventcontroller This can serve as a replacement for the legacy event signals for enter/leave/motion notify. --- gtk/Makefile.am | 2 + gtk/gtk.h | 1 + gtk/gtkeventcontrollermotion.c | 160 +++++++++++++++++++++++++++++++++ gtk/gtkeventcontrollermotion.h | 50 +++++++++++ 4 files changed, 213 insertions(+) create mode 100644 gtk/gtkeventcontrollermotion.c create mode 100644 gtk/gtkeventcontrollermotion.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 37336d540f..ec81f395d7 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -192,6 +192,7 @@ gtk_public_h_sources = \ gtkenums.h \ gtkeventbox.h \ gtkeventcontroller.h \ + gtkeventcontrollermotion.h \ gtkeventcontrollerscroll.h \ gtkexpander.h \ gtkfilechooser.h \ @@ -758,6 +759,7 @@ gtk_base_c_sources = \ gtkentrycompletion.c \ gtkeventbox.c \ gtkeventcontroller.c \ + gtkeventcontrollermotion.c \ gtkeventcontrollerscroll.c \ gtkexpander.c \ gtkfilechooser.c \ diff --git a/gtk/gtk.h b/gtk/gtk.h index 1ce5e5b37e..46cfbfc063 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -94,6 +94,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkeventcontrollermotion.c b/gtk/gtkeventcontrollermotion.c new file mode 100644 index 0000000000..6fe5f23615 --- /dev/null +++ b/gtk/gtkeventcontrollermotion.c @@ -0,0 +1,160 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Matthias Clasen + */ + +/** + * SECTION:gtkeventcontrollermotion + * @Short_description: Event controller for motion events + * @Title: GtkEventControllerMotion + * @See_also: #GtkEventController + * + * #GtkEventControllerMotion is an event controller meant for situations + * where you need to track the position of the pointer. + * + * This object was added in 3.94. + **/ +#include "config.h" + +#include "gtkintl.h" +#include "gtkwidget.h" +#include "gtkeventcontrollerprivate.h" +#include "gtkeventcontrollermotion.h" +#include "gtktypebuiltins.h" +#include "gtkmarshalers.h" + +struct _GtkEventControllerMotion +{ + GtkEventController parent_instance; +}; + +struct _GtkEventControllerMotionClass +{ + GtkEventControllerClass parent_class; +}; + +enum { + ENTER, + LEAVE, + MOTION, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0 }; + +G_DEFINE_TYPE (GtkEventControllerMotion, gtk_event_controller_motion, GTK_TYPE_EVENT_CONTROLLER) + +static gboolean +gtk_event_controller_motion_handle_event (GtkEventController *controller, + const GdkEvent *event) +{ + GtkEventControllerClass *parent_class; + GdkEventType type; + + type = gdk_event_get_event_type (event); + if (type == GDK_ENTER_NOTIFY) + g_signal_emit (controller, signals[ENTER], 0); + else if (type == GDK_LEAVE_NOTIFY) + g_signal_emit (controller, signals[LEAVE], 0); + else if (type == GDK_MOTION_NOTIFY) + { + double x, y; + gdk_event_get_coords (event, &x, &y); + g_signal_emit (controller, signals[MOTION], 0, x, y); + } + + parent_class = GTK_EVENT_CONTROLLER_CLASS (gtk_event_controller_motion_parent_class); + + return parent_class->handle_event (controller, event); +} + +static void +gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass) +{ + GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); + + controller_class->handle_event = gtk_event_controller_motion_handle_event; + + /** + * GtkEventControllerMotion::enter: + * @controller: The object that received the signal + * + * Signals that the pointer has entered the widget. + */ + signals[ENTER] = + g_signal_new (I_("enter"), + GTK_TYPE_EVENT_CONTROLLER_MOTION, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + NULL, + G_TYPE_NONE, 0); + /** + * GtkEventControllerMotion::leave: + * @controller: The object that received the signal + * + * Signals that pointer has left the widget. + */ + signals[LEAVE] = + g_signal_new (I_("leave"), + GTK_TYPE_EVENT_CONTROLLER_MOTION, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkEventControllerMotion::motion: + * @controller: The object that received the signal + * @x: the x coordinate + * @y: the y coordinate + * + * Emitted when the pointer moves inside the widget. + */ + signals[MOTION] = + g_signal_new (I_("motion"), + GTK_TYPE_EVENT_CONTROLLER_MOTION, + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + NULL, + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); +} + +static void +gtk_event_controller_motion_init (GtkEventControllerMotion *motion) +{ +} + +/** + * gtk_event_controller_motion_new: + * @widget: a #GtkWidget + * + * Creates a new event controller that will handle motion events + * for the given @widget. + * + * Returns: a new #GtkEventControllerMotion + * + * Since: 3.94 + **/ +GtkEventController * +gtk_event_controller_motion_new (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + return g_object_new (GTK_TYPE_EVENT_CONTROLLER_MOTION, + "widget", widget, + NULL); +} diff --git a/gtk/gtkeventcontrollermotion.h b/gtk/gtkeventcontrollermotion.h new file mode 100644 index 0000000000..42b6aac508 --- /dev/null +++ b/gtk/gtkeventcontrollermotion.h @@ -0,0 +1,50 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Matthias Clasen + */ + +#ifndef __GTK_EVENT_CONTROLLER_MOTION_H__ +#define __GTK_EVENT_CONTROLLER_MOTION_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_EVENT_CONTROLLER_MOTION (gtk_event_controller_motion_get_type ()) +#define GTK_EVENT_CONTROLLER_MOTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_MOTION, GtkEventControllerMotion)) +#define GTK_EVENT_CONTROLLER_MOTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_MOTION, GtkEventControllerMotionClass)) +#define GTK_IS_EVENT_CONTROLLER_MOTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_MOTION)) +#define GTK_IS_EVENT_CONTROLLER_MOTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_MOTION)) +#define GTK_EVENT_CONTROLLER_MOTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_MOTION, GtkEventControllerMotionClass)) + +typedef struct _GtkEventControllerMotion GtkEventControllerMotion; +typedef struct _GtkEventControllerMotionClass GtkEventControllerMotionClass; + +GDK_AVAILABLE_IN_3_24 +GType gtk_event_controller_motion_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_3_24 +GtkEventController *gtk_event_controller_motion_new (GtkWidget *widget); + +G_END_DECLS + +#endif /* __GTK_EVENT_CONTROLLER_MOTION_H__ */ From 1f9de707f7e1bd6f7241000dbbad464530b23e81 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 1 Jan 2018 20:07:24 -0500 Subject: [PATCH 07/14] Add x/y to GtkEventControllerMotion::enter We have this information available in enter events, and having it in the controller signal as well makes porting easier. Update existing users. --- gtk/gtkeventcontrollermotion.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/gtk/gtkeventcontrollermotion.c b/gtk/gtkeventcontrollermotion.c index 6fe5f23615..75c293e205 100644 --- a/gtk/gtkeventcontrollermotion.c +++ b/gtk/gtkeventcontrollermotion.c @@ -67,9 +67,15 @@ gtk_event_controller_motion_handle_event (GtkEventController *controller, type = gdk_event_get_event_type (event); if (type == GDK_ENTER_NOTIFY) - g_signal_emit (controller, signals[ENTER], 0); + { + double x, y; + gdk_event_get_coords (event, &x, &y); + g_signal_emit (controller, signals[ENTER], 0, x, y); + } else if (type == GDK_LEAVE_NOTIFY) - g_signal_emit (controller, signals[LEAVE], 0); + { + g_signal_emit (controller, signals[LEAVE], 0); + } else if (type == GDK_MOTION_NOTIFY) { double x, y; @@ -92,6 +98,8 @@ gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass) /** * GtkEventControllerMotion::enter: * @controller: The object that received the signal + * @x: the x coordinate + * @y: the y coordinate * * Signals that the pointer has entered the widget. */ @@ -101,7 +109,8 @@ gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass) G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + /** * GtkEventControllerMotion::leave: * @controller: The object that received the signal @@ -113,7 +122,7 @@ gtk_event_controller_motion_class_init (GtkEventControllerMotionClass *klass) GTK_TYPE_EVENT_CONTROLLER_MOTION, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, - NULL, + NULL, G_TYPE_NONE, 0); /** From e0f3e8a7e5d898aba11cce1fcffe79fd142b12ae Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 11 Dec 2017 19:21:38 +0100 Subject: [PATCH 08/14] gtk: Add GtkEventControllerKey This event controller is meant to replace usage from key-press/release-event handlers all through. Optionally it can be set a GtkIMContext, so interaction is carried by the controller. --- gtk/Makefile.am | 2 + gtk/gtk.h | 1 + gtk/gtkeventcontrollerkey.c | 217 ++++++++++++++++++++++++++++++++++++ gtk/gtkeventcontrollerkey.h | 57 ++++++++++ po/POTFILES.in | 3 + 5 files changed, 280 insertions(+) create mode 100644 gtk/gtkeventcontrollerkey.c create mode 100644 gtk/gtkeventcontrollerkey.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index ec81f395d7..93f3d5c29c 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -192,6 +192,7 @@ gtk_public_h_sources = \ gtkenums.h \ gtkeventbox.h \ gtkeventcontroller.h \ + gtkeventcontrollerkey.h \ gtkeventcontrollermotion.h \ gtkeventcontrollerscroll.h \ gtkexpander.h \ @@ -759,6 +760,7 @@ gtk_base_c_sources = \ gtkentrycompletion.c \ gtkeventbox.c \ gtkeventcontroller.c \ + gtkeventcontrollerkey.c \ gtkeventcontrollermotion.c \ gtkeventcontrollerscroll.c \ gtkexpander.c \ diff --git a/gtk/gtk.h b/gtk/gtk.h index 46cfbfc063..8dee2a9efc 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -94,6 +94,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c new file mode 100644 index 0000000000..13015312c7 --- /dev/null +++ b/gtk/gtkeventcontrollerkey.c @@ -0,0 +1,217 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ + +#include "config.h" + +#include "gtkintl.h" +#include "gtkprivate.h" +#include "gtkwidget.h" +#include "gtkeventcontrollerprivate.h" +#include "gtkeventcontrollerkey.h" +#include "gtkbindings.h" + +#include + +struct _GtkEventControllerKey +{ + GtkEventController parent_instance; + GtkIMContext *im_context; + GHashTable *pressed_keys; + + const GdkEvent *current_event; +}; + +struct _GtkEventControllerKeyClass +{ + GtkEventControllerClass parent_class; +}; + +enum { + KEY_PRESSED, + KEY_RELEASED, + MODIFIERS, + IM_UPDATE, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0 }; + +G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key, + GTK_TYPE_EVENT_CONTROLLER) + +static void +gtk_event_controller_finalize (GObject *object) +{ + GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object); + + g_hash_table_destroy (key->pressed_keys); + g_clear_object (&key->im_context); + + G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object); +} + +static gboolean +gtk_event_controller_key_handle_event (GtkEventController *controller, + const GdkEvent *event) +{ + GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller); + GdkEventType event_type = gdk_event_get_event_type (event); + gboolean handled; + GdkModifierType state; + guint16 keycode; + guint keyval; + + if (event_type != GDK_KEY_PRESS && event_type != GDK_KEY_RELEASE) + return FALSE; + + if (key->im_context && + gtk_im_context_filter_keypress (key->im_context, (GdkEventKey *) event)) + { + g_signal_emit (controller, signals[IM_UPDATE], 0); + return TRUE; + } + + if (!gdk_event_get_state (event, &state) || !event->key.is_modifier) + return FALSE; + + key->current_event = event; + + if (event->key.is_modifier) + { + if (event_type == GDK_KEY_PRESS) + g_signal_emit (controller, signals[MODIFIERS], 0, state, &handled); + else + handled = TRUE; + + if (handled == TRUE) + { + key->current_event = NULL; + return TRUE; + } + } + + gdk_event_get_keycode (event, &keycode); + gdk_event_get_keyval (event, &keyval); + + if (event_type == GDK_KEY_PRESS) + { + g_signal_emit (controller, signals[KEY_PRESSED], 0, + keyval, keycode, state, &handled); + if (handled) + g_hash_table_add (key->pressed_keys, GUINT_TO_POINTER (keyval)); + } + else if (event_type == GDK_KEY_RELEASE) + { + g_signal_emit (controller, signals[KEY_RELEASED], 0, + keyval, keycode, state); + + handled = g_hash_table_lookup (key->pressed_keys, GUINT_TO_POINTER (keyval)) != NULL; + g_hash_table_remove (key->pressed_keys, GUINT_TO_POINTER (keyval)); + } + else + handled = FALSE; + + key->current_event = NULL; + + return handled; +} + +static void +gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) +{ + GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gtk_event_controller_finalize; + controller_class->handle_event = gtk_event_controller_key_handle_event; + + signals[KEY_PRESSED] = + g_signal_new (I_("key-pressed"), + GTK_TYPE_EVENT_CONTROLLER_KEY, + G_SIGNAL_RUN_LAST, + 0, _gtk_boolean_handled_accumulator, NULL, NULL, + G_TYPE_BOOLEAN, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE); + signals[KEY_RELEASED] = + g_signal_new (I_("key-released"), + GTK_TYPE_EVENT_CONTROLLER_KEY, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE); + signals[MODIFIERS] = + g_signal_new (I_("modifiers"), + GTK_TYPE_EVENT_CONTROLLER_KEY, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_BOOLEAN__FLAGS, + G_TYPE_BOOLEAN, 1, GDK_TYPE_MODIFIER_TYPE); + signals[IM_UPDATE] = + g_signal_new (I_("im-update"), + GTK_TYPE_EVENT_CONTROLLER_KEY, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +gtk_event_controller_key_init (GtkEventControllerKey *controller) +{ + controller->pressed_keys = g_hash_table_new (NULL, NULL); +} + +GtkEventController * +gtk_event_controller_key_new (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY, + "widget", widget, + NULL); +} + +void +gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller, + GtkIMContext *im_context) +{ + g_return_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller)); + g_return_if_fail (!im_context || GTK_IS_IM_CONTEXT (im_context)); + + if (controller->im_context) + gtk_im_context_reset (controller->im_context); + + g_set_object (&controller->im_context, im_context); +} + +/** + * gtk_event_controller_key_get_im_context: + * @controller: a #GtkEventControllerKey + * + * Gets the IM context of a key controller. + * + * Returns: (transfer none): the IM context + * + * Since: 3.94 + **/ +GtkIMContext * +gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); + + return controller->im_context; +} diff --git a/gtk/gtkeventcontrollerkey.h b/gtk/gtkeventcontrollerkey.h new file mode 100644 index 0000000000..025aa336ef --- /dev/null +++ b/gtk/gtkeventcontrollerkey.h @@ -0,0 +1,57 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ + +#ifndef __GTK_EVENT_CONTROLLER_KEY_H__ +#define __GTK_EVENT_CONTROLLER_KEY_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_EVENT_CONTROLLER_KEY (gtk_event_controller_key_get_type ()) +#define GTK_EVENT_CONTROLLER_KEY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKey)) +#define GTK_EVENT_CONTROLLER_KEY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKeyClass)) +#define GTK_IS_EVENT_CONTROLLER_KEY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_KEY)) +#define GTK_IS_EVENT_CONTROLLER_KEY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_KEY)) +#define GTK_EVENT_CONTROLLER_KEY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKeyClass)) + +typedef struct _GtkEventControllerKey GtkEventControllerKey; +typedef struct _GtkEventControllerKeyClass GtkEventControllerKeyClass; + +GDK_AVAILABLE_IN_3_24 +GType gtk_event_controller_key_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_3_24 +GtkEventController *gtk_event_controller_key_new (GtkWidget *widget); + +GDK_AVAILABLE_IN_3_24 +void gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller, + GtkIMContext *im_context); +GDK_AVAILABLE_IN_3_24 +GtkIMContext * gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller); + +G_END_DECLS + +#endif /* __GTK_EVENT_CONTROLLER_KEY_H__ */ diff --git a/po/POTFILES.in b/po/POTFILES.in index a2cd6f5c86..443d15a77d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -142,6 +142,9 @@ gtk/gtkentry.c gtk/gtkentrycompletion.c gtk/gtkeventbox.c gtk/gtkeventcontroller.c +gtk/gtkeventcontrollerkey.c +gtk/gtkeventcontrollermotion.c +gtk/gtkeventcontrollerscroll.c gtk/gtkexpander.c gtk/gtkfilechooserbutton.c gtk/gtkfilechooser.c From a32933b056a81e3b4325949450dd92e6b54c740d Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Sun, 11 Mar 2018 13:38:19 +0100 Subject: [PATCH 09/14] eventcontrollerkey: Add function to forward stuff elsewhere --- gtk/gtkeventcontrollerkey.c | 21 ++++++++++++++++++++- gtk/gtkeventcontrollerkey.h | 4 ++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 13015312c7..2469f7da3c 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -21,7 +21,7 @@ #include "gtkintl.h" #include "gtkprivate.h" -#include "gtkwidget.h" +#include "gtkwidgetprivate.h" #include "gtkeventcontrollerprivate.h" #include "gtkeventcontrollerkey.h" #include "gtkbindings.h" @@ -215,3 +215,22 @@ gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller) return controller->im_context; } + +gboolean +gtk_event_controller_key_forward (GtkEventControllerKey *controller, + GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE); + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + g_return_val_if_fail (controller->current_event != NULL, FALSE); + + if (!gtk_widget_get_realized (widget)) + gtk_widget_realize (widget); + + if (_gtk_widget_captured_event (widget, (GdkEvent *) controller->current_event)) + return TRUE; + if (gtk_widget_event (widget, (GdkEvent *) controller->current_event)) + return TRUE; + + return FALSE; +} diff --git a/gtk/gtkeventcontrollerkey.h b/gtk/gtkeventcontrollerkey.h index 025aa336ef..00dc2a5298 100644 --- a/gtk/gtkeventcontrollerkey.h +++ b/gtk/gtkeventcontrollerkey.h @@ -52,6 +52,10 @@ void gtk_event_controller_key_set_im_context (GtkEventControllerK GDK_AVAILABLE_IN_3_24 GtkIMContext * gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller); +GDK_AVAILABLE_IN_3_24 +gboolean gtk_event_controller_key_forward (GtkEventControllerKey *controller, + GtkWidget *widget); + G_END_DECLS #endif /* __GTK_EVENT_CONTROLLER_KEY_H__ */ From ecf9fa65b8d864f4c815e229783e8d5fa2fccb93 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Thu, 22 Mar 2018 17:54:52 +0100 Subject: [PATCH 10/14] gtkeventcontrollerkey: Add get_group() call Callers can use this function on a key-pressed/released signal to find out the key event group, useful in a few places. --- gtk/gtkeventcontrollerkey.c | 9 +++++++++ gtk/gtkeventcontrollerkey.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 2469f7da3c..2afaf7f0be 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -234,3 +234,12 @@ gtk_event_controller_key_forward (GtkEventControllerKey *controller, return FALSE; } + +guint +gtk_event_controller_key_get_group (GtkEventControllerKey *controller) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE); + g_return_val_if_fail (controller->current_event != NULL, FALSE); + + return controller->current_event->key.group; +} diff --git a/gtk/gtkeventcontrollerkey.h b/gtk/gtkeventcontrollerkey.h index 00dc2a5298..a3756c4ac0 100644 --- a/gtk/gtkeventcontrollerkey.h +++ b/gtk/gtkeventcontrollerkey.h @@ -55,6 +55,8 @@ GtkIMContext * gtk_event_controller_key_get_im_context (GtkEventControllerK GDK_AVAILABLE_IN_3_24 gboolean gtk_event_controller_key_forward (GtkEventControllerKey *controller, GtkWidget *widget); +GDK_AVAILABLE_IN_3_24 +guint gtk_event_controller_key_get_group (GtkEventControllerKey *controller); G_END_DECLS From 7793aab5f00ab14a5dbd4a0305d93c95ebee0a11 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Thu, 22 Mar 2018 18:32:19 +0100 Subject: [PATCH 11/14] gtkeventcontrollerkey: Add ::focus-in/out signals And handle GDK_FOCUS_CHANGE events in order to emit those. --- gtk/gtkeventcontrollerkey.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 2afaf7f0be..70c34bd4e6 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -47,6 +47,8 @@ enum { KEY_RELEASED, MODIFIERS, IM_UPDATE, + FOCUS_IN, + FOCUS_OUT, N_SIGNALS }; @@ -77,6 +79,16 @@ gtk_event_controller_key_handle_event (GtkEventController *controller, guint16 keycode; guint keyval; + if (event_type == GDK_FOCUS_CHANGE) + { + if (event->focus_change.in) + g_signal_emit (controller, signals[FOCUS_IN], 0); + else + g_signal_emit (controller, signals[FOCUS_OUT], 0); + + return FALSE; + } + if (event_type != GDK_KEY_PRESS && event_type != GDK_KEY_RELEASE) return FALSE; @@ -167,6 +179,20 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + signals[FOCUS_IN] = + g_signal_new (I_("focus-in"), + GTK_TYPE_EVENT_CONTROLLER_KEY, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[FOCUS_OUT] = + g_signal_new (I_("focus-out"), + GTK_TYPE_EVENT_CONTROLLER_KEY, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); } static void From a8463953a47da79818028d55458ebef726a51537 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Thu, 1 Feb 2018 17:52:40 +0100 Subject: [PATCH 12/14] gtk: Add GtkGestureStylus This is a GtkGesture done to deal with stylus events from drawing tablets. Those have a special number of characteristics that extend a regular pointer, so it makes sense to wrap that. --- gtk/Makefile.am | 3 + gtk/gtk.h | 1 + gtk/gtkgesturestylus.c | 269 ++++++++++++++++++++++++++++++++++ gtk/gtkgesturestylus.h | 59 ++++++++ gtk/gtkgesturestylusprivate.h | 51 +++++++ po/POTFILES.in | 1 + 6 files changed, 384 insertions(+) create mode 100644 gtk/gtkgesturestylus.c create mode 100644 gtk/gtkgesturestylus.h create mode 100644 gtk/gtkgesturestylusprivate.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 93f3d5c29c..1fdee6f720 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -216,6 +216,7 @@ gtk_public_h_sources = \ gtkgesturepan.h \ gtkgesturerotate.h \ gtkgesturesingle.h \ + gtkgesturestylus.h \ gtkgestureswipe.h \ gtkgesturezoom.h \ gtkglarea.h \ @@ -494,6 +495,7 @@ gtk_private_h_sources = \ gtkgesturepanprivate.h \ gtkgesturerotateprivate.h \ gtkgesturesingleprivate.h \ + gtkgesturestylusprivate.h \ gtkgestureswipeprivate.h \ gtkgesturezoomprivate.h \ gtkheaderbarprivate.h \ @@ -792,6 +794,7 @@ gtk_base_c_sources = \ gtkgesturepan.c \ gtkgesturerotate.c \ gtkgesturesingle.c \ + gtkgesturestylus.c \ gtkgestureswipe.c \ gtkgesturezoom.c \ gtkglarea.c \ diff --git a/gtk/gtk.h b/gtk/gtk.h index 8dee2a9efc..c08454ca1c 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -118,6 +118,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkgesturestylus.c b/gtk/gtkgesturestylus.c new file mode 100644 index 0000000000..cda366b92f --- /dev/null +++ b/gtk/gtkgesturestylus.c @@ -0,0 +1,269 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017-2018, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ + +/** + * SECTION:gtkgesturestylus + * @Short_description: Gesture for stylus input + * @Title: GtkGestureStylus + * @See_also: #GtkGesture, #GtkGestureSingle + * + * #GtkGestureStylus is a #GtkGesture implementation specific to stylus + * input. The provided signals just provide the basic information + */ + +#include "config.h" +#include "gtkgesturestylus.h" +#include "gtkgesturestylusprivate.h" +#include "gtkprivate.h" +#include "gtkintl.h" +#include "gtkmain.h" + +G_DEFINE_TYPE (GtkGestureStylus, gtk_gesture_stylus, GTK_TYPE_GESTURE_SINGLE) + +enum { + PROXIMITY, + DOWN, + MOTION, + UP, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0, }; + +static gboolean +gtk_gesture_stylus_handle_event (GtkEventController *controller, + const GdkEvent *event) +{ + GdkModifierType modifiers; + guint n_signal; + gdouble x, y; + + GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_stylus_parent_class)->handle_event (controller, event); + + if (!gdk_event_get_device_tool (event)) + return FALSE; + if (!gdk_event_get_coords (event, &x, &y)) + return FALSE; + + switch ((guint) gdk_event_get_event_type (event)) + { + case GDK_BUTTON_PRESS: + n_signal = DOWN; + break; + case GDK_BUTTON_RELEASE: + n_signal = UP; + break; + case GDK_MOTION_NOTIFY: + gdk_event_get_state (event, &modifiers); + + if (modifiers & GDK_BUTTON1_MASK) + n_signal = MOTION; + else + n_signal = PROXIMITY; + break; + default: + return FALSE; + } + + g_signal_emit (controller, signals[n_signal], 0, x, y); + + return TRUE; +} + +static void +gtk_gesture_stylus_class_init (GtkGestureStylusClass *klass) +{ + GtkEventControllerClass *event_controller_class; + + event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); + event_controller_class->handle_event = gtk_gesture_stylus_handle_event; + + signals[PROXIMITY] = + g_signal_new (I_("proximity"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureStylusClass, proximity), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + signals[DOWN] = + g_signal_new (I_("down"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureStylusClass, down), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + signals[MOTION] = + g_signal_new (I_("motion"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureStylusClass, motion), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + signals[UP] = + g_signal_new (I_("up"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkGestureStylusClass, up), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); +} + +static void +gtk_gesture_stylus_init (GtkGestureStylus *gesture) +{ +} + +/** + * gtk_gesture_stylus_new: + * @widget: a #GtkWidget + * + * Creates a new #GtkGestureStylus. + * + * Returns: a newly created stylus gesture + * + * Since: 3.94 + **/ +GtkGesture * +gtk_gesture_stylus_new (GtkWidget *widget) +{ + return g_object_new (GTK_TYPE_GESTURE_STYLUS, + "widget", widget, + NULL); +} + +static const GdkEvent * +gesture_get_current_event (GtkGestureStylus *gesture) +{ + GdkEventSequence *sequence; + + sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + + return gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); +} + +/** + * gtk_gesture_stylus_get_axis: + * @gesture: a #GtkGestureStylus + * @axis: requested device axis + * @value: (out): return location for the axis value + * + * Returns the current value for the requested @axis. This function + * must be called from either the #GtkGestureStylus:down, + * #GtkGestureStylus:motion, #GtkGestureStylus:up or #GtkGestureStylus:proximity + * signals. + * + * Returns: #TRUE if there is a current value for the axis + * + * Since: 3.94 + **/ +gboolean +gtk_gesture_stylus_get_axis (GtkGestureStylus *gesture, + GdkAxisUse axis, + gdouble *value) +{ + const GdkEvent *event; + + g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE); + g_return_val_if_fail (axis < GDK_AXIS_LAST, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + event = gesture_get_current_event (gesture); + if (!event) + return FALSE; + + return gdk_event_get_axis (event, axis, value); +} + +/** + * gtk_gesture_stylus_get_axes: + * @gesture: a GtkGestureStylus + * @axes: array of requested axes, terminated with #GDK_AXIS_IGNORE + * @values: (out): return location for the axis values + * + * Returns the current values for the requested @axes. This function + * must be called from either the #GtkGestureStylus:down, + * #GtkGestureStylus:motion, #GtkGestureStylus:up or #GtkGestureStylus:proximity + * signals. + * + * Returns: #TRUE if there is a current value for the axes + **/ +gboolean +gtk_gesture_stylus_get_axes (GtkGestureStylus *gesture, + GdkAxisUse axes[], + gdouble **values) +{ + const GdkEvent *event; + GArray *array; + gint i = 0; + + g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE); + g_return_val_if_fail (values != NULL, FALSE); + + event = gesture_get_current_event (gesture); + if (!event) + return FALSE; + + array = g_array_new (TRUE, FALSE, sizeof (gdouble)); + + while (axes[i] != GDK_AXIS_IGNORE) + { + gdouble value; + + if (axes[i] >= GDK_AXIS_LAST) + { + g_warning ("Requesting unknown axis %d, did you " + "forget to add a last GDK_AXIS_IGNORE axis?", + axes[i]); + g_array_free (array, TRUE); + return FALSE; + } + + gdk_event_get_axis (event, axes[i], &value); + g_array_append_val (array, value); + i++; + } + + *values = (gdouble *) g_array_free (array, FALSE); + return TRUE; +} + +/** + * gtk_gesture_stylus_get_device_tool: + * @gesture: a #GtkGestureStylus + * + * Returns the #GdkDeviceTool currently driving input through this gesture. + * This function must be called from either the #GtkGestureStylus:down, + * #GtkGestureStylus:motion, #GtkGestureStylus:up or #GtkGestureStylus:proximity + * signals. + * + * Returns: (nullable) (transfer none): The current stylus tool + **/ +GdkDeviceTool * +gtk_gesture_stylus_get_device_tool (GtkGestureStylus *gesture) +{ + const GdkEvent *event; + + g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE); + + event = gesture_get_current_event (gesture); + if (!event) + return NULL; + + return gdk_event_get_device_tool (event); +} diff --git a/gtk/gtkgesturestylus.h b/gtk/gtkgesturestylus.h new file mode 100644 index 0000000000..c45fd8a8b5 --- /dev/null +++ b/gtk/gtkgesturestylus.h @@ -0,0 +1,59 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017-2018, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ +#ifndef __GTK_GESTURE_STYLUS_H__ +#define __GTK_GESTURE_STYLUS_H__ + +#include + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +G_BEGIN_DECLS + +#define GTK_TYPE_GESTURE_STYLUS (gtk_gesture_stylus_get_type ()) +#define GTK_GESTURE_STYLUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GESTURE_STYLUS, GtkGestureStylus)) +#define GTK_GESTURE_STYLUS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GESTURE_STYLUS, GtkGestureStylusClass)) +#define GTK_IS_GESTURE_STYLUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GESTURE_STYLUS)) +#define GTK_IS_GESTURE_STYLUS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GESTURE_STYLUS)) +#define GTK_GESTURE_STYLUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GESTURE_STYLUS, GtkGestureStylusClass)) + +typedef struct _GtkGestureStylus GtkGestureStylus; +typedef struct _GtkGestureStylusClass GtkGestureStylusClass; + +GDK_AVAILABLE_IN_ALL +GType gtk_gesture_stylus_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_ALL +GtkGesture * gtk_gesture_stylus_new (GtkWidget *widget); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_gesture_stylus_get_axis (GtkGestureStylus *gesture, + GdkAxisUse axis, + gdouble *value); +GDK_AVAILABLE_IN_ALL +gboolean gtk_gesture_stylus_get_axes (GtkGestureStylus *gesture, + GdkAxisUse axes[], + gdouble **values); +GDK_AVAILABLE_IN_ALL +GdkDeviceTool * gtk_gesture_stylus_get_device_tool (GtkGestureStylus *gesture); + +G_END_DECLS + +#endif /* __GTK_GESTURE_STYLUS_H__ */ diff --git a/gtk/gtkgesturestylusprivate.h b/gtk/gtkgesturestylusprivate.h new file mode 100644 index 0000000000..9869b528a5 --- /dev/null +++ b/gtk/gtkgesturestylusprivate.h @@ -0,0 +1,51 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017-2018, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Carlos Garnacho + */ +#ifndef __GTK_GESTURE_STYLUS_PRIVATE_H__ +#define __GTK_GESTURE_STYLUS_PRIVATE_H__ + +#include "gtkgesturesingleprivate.h" +#include "gtkgesturestylus.h" + +struct _GtkGestureStylus +{ + GtkGestureSingle parent_instance; +}; + +struct _GtkGestureStylusClass +{ + GtkGestureSingleClass parent_class; + + void (*proximity) (GtkGestureStylus *gesture, + gdouble x, + gdouble y); + void (*down) (GtkGestureStylus *gesture, + gdouble x, + gdouble y); + void (*motion) (GtkGestureStylus *gesture, + gdouble x, + gdouble y); + void (*up) (GtkGestureStylus *gesture, + gdouble x, + gdouble y); + + /*< private >*/ + gpointer padding[10]; +}; + +#endif /* __GTK_GESTURE_STYLUS_PRIVATE_H__ */ diff --git a/po/POTFILES.in b/po/POTFILES.in index 443d15a77d..f3bfe4d7f8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -173,6 +173,7 @@ gtk/gtkgesturemultipress.c gtk/gtkgesturepan.c gtk/gtkgesturerotate.c gtk/gtkgesturesingle.c +gtk/gtkgesturestylus.c gtk/gtkgestureswipe.c gtk/gtkgesturezoom.c gtk/gtkglarea.c From effdf3af74a15e6f49e8b00279f384529b42cf14 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 2 Feb 2018 16:04:17 +0100 Subject: [PATCH 13/14] demos: Add "Paint" demo --- demos/gtk-demo/Makefile.am | 1 + demos/gtk-demo/demo.gresource.xml | 1 + demos/gtk-demo/paint.c | 255 ++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 demos/gtk-demo/paint.c diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 261d555ae5..64245b4b6e 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -48,6 +48,7 @@ demos_base = \ offscreen_window2.c \ overlay.c \ overlay2.c \ + paint.c \ panes.c \ pickers.c \ pixbufs.c \ diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index a02c4843f9..3fac7342eb 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -177,6 +177,7 @@ offscreen_window2.c overlay.c overlay2.c + paint.c pagesetup.c panes.c pickers.c diff --git a/demos/gtk-demo/paint.c b/demos/gtk-demo/paint.c new file mode 100644 index 0000000000..55232b0565 --- /dev/null +++ b/demos/gtk-demo/paint.c @@ -0,0 +1,255 @@ +/* Paint + * + * Demonstrates practical handling of drawing tablets in a real world + * usecase. + */ +#include + +typedef struct +{ + GtkEventBox parent_instance; + cairo_surface_t *surface; + cairo_t *cr; + GdkRGBA draw_color; + + GtkGesture *stylus_gesture; +} DrawingArea; + +typedef struct +{ + GtkEventBoxClass parent_class; +} DrawingAreaClass; + +G_DEFINE_TYPE (DrawingArea, drawing_area, GTK_TYPE_EVENT_BOX) + +static void +drawing_area_ensure_surface (DrawingArea *area, + gint width, + gint height) +{ + if (!area->surface || + cairo_image_surface_get_width (area->surface) != width || + cairo_image_surface_get_height (area->surface) != height) + { + cairo_surface_t *surface; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + if (area->surface) + { + cairo_t *cr; + + cr = cairo_create (surface); + cairo_set_source_surface (cr, area->surface, 0, 0); + cairo_paint (cr); + + cairo_surface_destroy (area->surface); + cairo_destroy (area->cr); + cairo_destroy (cr); + } + + area->surface = surface; + area->cr = cairo_create (surface); + } +} + +static void +drawing_area_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + DrawingArea *area = (DrawingArea *) widget; + + drawing_area_ensure_surface (area, allocation->width, allocation->height); + + GTK_WIDGET_CLASS (drawing_area_parent_class)->size_allocate (widget, allocation); +} + +static void +drawing_area_map (GtkWidget *widget) +{ + GtkAllocation allocation; + + GTK_WIDGET_CLASS (drawing_area_parent_class)->map (widget); + + gdk_window_set_event_compression (gtk_widget_get_window (widget), TRUE); + + gtk_widget_get_allocation (widget, &allocation); + drawing_area_ensure_surface ((DrawingArea *) widget, + allocation.width, allocation.height); +} + +static void +drawing_area_unmap (GtkWidget *widget) +{ + DrawingArea *area = (DrawingArea *) widget; + + g_clear_pointer (&area->cr, cairo_destroy); + g_clear_pointer (&area->surface, cairo_surface_destroy); + + GTK_WIDGET_CLASS (drawing_area_parent_class)->unmap (widget); +} + +static gboolean +drawing_area_draw (GtkWidget *widget, + cairo_t *cr) +{ + DrawingArea *area = (DrawingArea *) widget; + GtkAllocation allocation; + + gtk_widget_get_allocation (widget, &allocation); + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + cairo_set_source_surface (cr, area->surface, 0, 0); + cairo_paint (cr); + + cairo_set_source_rgb (cr, 0.6, 0.6, 0.6); + cairo_rectangle (cr, 0, 0, allocation.width, allocation.height); + cairo_stroke (cr); + + return TRUE; +} + +static void +drawing_area_class_init (DrawingAreaClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + widget_class->size_allocate = drawing_area_size_allocate; + widget_class->draw = drawing_area_draw; + widget_class->map = drawing_area_map; + widget_class->unmap = drawing_area_unmap; +} + +static void +drawing_area_apply_stroke (DrawingArea *area, + GdkDeviceTool *tool, + gdouble x, + gdouble y, + gdouble pressure) +{ + if (gdk_device_tool_get_tool_type (tool) == GDK_DEVICE_TOOL_TYPE_ERASER) + { + cairo_set_line_width (area->cr, 10 * pressure); + cairo_set_operator (area->cr, CAIRO_OPERATOR_DEST_OUT); + } + else + { + cairo_set_line_width (area->cr, 4 * pressure); + cairo_set_operator (area->cr, CAIRO_OPERATOR_SATURATE); + } + + cairo_set_source_rgba (area->cr, area->draw_color.red, + area->draw_color.green, area->draw_color.blue, + area->draw_color.alpha * pressure); + + //cairo_set_source_rgba (area->cr, 0, 0, 0, pressure); + + cairo_line_to (area->cr, x, y); + cairo_stroke (area->cr); + cairo_move_to (area->cr, x, y); +} + +static void +stylus_gesture_down (GtkGestureStylus *gesture, + gdouble x, + gdouble y, + DrawingArea *area) +{ + cairo_new_path (area->cr); +} + +static void +stylus_gesture_motion (GtkGestureStylus *gesture, + gdouble x, + gdouble y, + DrawingArea *area) +{ + GdkDeviceTool *tool; + gdouble pressure; + + tool = gtk_gesture_stylus_get_device_tool (gesture); + + if (!gtk_gesture_stylus_get_axis (gesture, GDK_AXIS_PRESSURE, &pressure)) + pressure = 1; + + drawing_area_apply_stroke (area, tool, x, y, pressure); + gtk_widget_queue_draw (GTK_WIDGET (area)); +} + +static void +drawing_area_init (DrawingArea *area) +{ + gtk_event_box_set_visible_window (GTK_EVENT_BOX (area), TRUE); + + area->stylus_gesture = gtk_gesture_stylus_new (GTK_WIDGET (area)); + g_signal_connect (area->stylus_gesture, "down", + G_CALLBACK (stylus_gesture_down), area); + g_signal_connect (area->stylus_gesture, "motion", + G_CALLBACK (stylus_gesture_motion), area); + + area->draw_color = (GdkRGBA) { 0, 0, 0, 1 }; +} + +GtkWidget * +drawing_area_new (void) +{ + return g_object_new (drawing_area_get_type (), NULL); +} + +void +drawing_area_set_color (DrawingArea *area, + GdkRGBA *color) +{ + area->draw_color = *color; +} + +static void +color_button_color_set (GtkColorButton *button, + DrawingArea *draw_area) +{ + GdkRGBA color; + + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (button), &color); + drawing_area_set_color (draw_area, &color); +} + +GtkWidget * +do_paint (GtkWidget *toplevel) +{ + static GtkWidget *window = NULL; + + if (!window) + { + GtkWidget *draw_area, *headerbar, *colorbutton; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + draw_area = drawing_area_new (); + gtk_container_add (GTK_CONTAINER (window), draw_area); + + headerbar = gtk_header_bar_new (); + gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), "Paint"); + gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE); + + colorbutton = gtk_color_button_new (); + g_signal_connect (colorbutton, "color-set", + G_CALLBACK (color_button_color_set), draw_area); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (colorbutton), + &(GdkRGBA) { 0, 0, 0, 1 }); + + gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), colorbutton); + gtk_window_set_titlebar (GTK_WINDOW (window), headerbar); + + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + } + + if (!gtk_widget_get_visible (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); + + return window; +} From 9ee60777e7e1e0e16641746f741dc68d0412e6b1 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 16 Jul 2018 15:44:35 +0200 Subject: [PATCH 14/14] sm scroll --- gtk/gtkeventcontrollerscroll.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gtk/gtkeventcontrollerscroll.c b/gtk/gtkeventcontrollerscroll.c index 1738e0173f..db98d7c51a 100644 --- a/gtk/gtkeventcontrollerscroll.c +++ b/gtk/gtkeventcontrollerscroll.c @@ -60,6 +60,8 @@ **/ #include "config.h" +#include "math.h" + #include "gtkintl.h" #include "gtkwidget.h" #include "gtkeventcontrollerprivate.h"