diff options
-rw-r--r-- | demos/gtk-demo/Makefile.am | 1 | ||||
-rw-r--r-- | demos/gtk-demo/demo.gresource.xml | 1 | ||||
-rw-r--r-- | demos/gtk-demo/paint.c | 255 | ||||
-rw-r--r-- | docs/reference/gtk/gtk3.types.in | 1 | ||||
-rw-r--r-- | gtk/Makefile.am | 9 | ||||
-rw-r--r-- | gtk/gtk.h | 4 | ||||
-rw-r--r-- | gtk/gtkeventcontrollerkey.c | 271 | ||||
-rw-r--r-- | gtk/gtkeventcontrollerkey.h | 63 | ||||
-rw-r--r-- | gtk/gtkeventcontrollermotion.c | 169 | ||||
-rw-r--r-- | gtk/gtkeventcontrollermotion.h | 50 | ||||
-rw-r--r-- | gtk/gtkeventcontrollerscroll.c | 517 | ||||
-rw-r--r-- | gtk/gtkeventcontrollerscroll.h | 80 | ||||
-rw-r--r-- | gtk/gtkgesturestylus.c | 269 | ||||
-rw-r--r-- | gtk/gtkgesturestylus.h | 59 | ||||
-rw-r--r-- | gtk/gtkgesturestylusprivate.h | 51 | ||||
-rw-r--r-- | po/POTFILES.in | 4 |
16 files changed, 1804 insertions, 0 deletions
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 @@ <file>offscreen_window2.c</file> <file>overlay.c</file> <file>overlay2.c</file> + <file>paint.c</file> <file>pagesetup.c</file> <file>panes.c</file> <file>pickers.c</file> 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 <gtk/gtk.h> + +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; +} 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/Makefile.am b/gtk/Makefile.am index 56b41a1c90..1fdee6f720 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -192,6 +192,9 @@ gtk_public_h_sources = \ gtkenums.h \ gtkeventbox.h \ gtkeventcontroller.h \ + gtkeventcontrollerkey.h \ + gtkeventcontrollermotion.h \ + gtkeventcontrollerscroll.h \ gtkexpander.h \ gtkfilechooser.h \ gtkfilechooserbutton.h \ @@ -213,6 +216,7 @@ gtk_public_h_sources = \ gtkgesturepan.h \ gtkgesturerotate.h \ gtkgesturesingle.h \ + gtkgesturestylus.h \ gtkgestureswipe.h \ gtkgesturezoom.h \ gtkglarea.h \ @@ -491,6 +495,7 @@ gtk_private_h_sources = \ gtkgesturepanprivate.h \ gtkgesturerotateprivate.h \ gtkgesturesingleprivate.h \ + gtkgesturestylusprivate.h \ gtkgestureswipeprivate.h \ gtkgesturezoomprivate.h \ gtkheaderbarprivate.h \ @@ -757,6 +762,9 @@ gtk_base_c_sources = \ gtkentrycompletion.c \ gtkeventbox.c \ gtkeventcontroller.c \ + gtkeventcontrollerkey.c \ + gtkeventcontrollermotion.c \ + gtkeventcontrollerscroll.c \ gtkexpander.c \ gtkfilechooser.c \ gtkfilechooserbutton.c \ @@ -786,6 +794,7 @@ gtk_base_c_sources = \ gtkgesturepan.c \ gtkgesturerotate.c \ gtkgesturesingle.c \ + gtkgesturestylus.c \ gtkgestureswipe.c \ gtkgesturezoom.c \ gtkglarea.c \ @@ -94,6 +94,9 @@ #include <gtk/gtkenums.h> #include <gtk/gtkeventbox.h> #include <gtk/gtkeventcontroller.h> +#include <gtk/gtkeventcontrollerkey.h> +#include <gtk/gtkeventcontrollermotion.h> +#include <gtk/gtkeventcontrollerscroll.h> #include <gtk/gtkexpander.h> #include <gtk/gtkfixed.h> #include <gtk/gtkfilechooser.h> @@ -115,6 +118,7 @@ #include <gtk/gtkgesturepan.h> #include <gtk/gtkgesturerotate.h> #include <gtk/gtkgesturesingle.h> +#include <gtk/gtkgesturestylus.h> #include <gtk/gtkgestureswipe.h> #include <gtk/gtkgesturezoom.h> #include <gtk/gtkglarea.h> diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c new file mode 100644 index 0000000000..70c34bd4e6 --- /dev/null +++ b/gtk/gtkeventcontrollerkey.c @@ -0,0 +1,271 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Carlos Garnacho <carlosg@gnome.org> + */ + +#include "config.h" + +#include "gtkintl.h" +#include "gtkprivate.h" +#include "gtkwidgetprivate.h" +#include "gtkeventcontrollerprivate.h" +#include "gtkeventcontrollerkey.h" +#include "gtkbindings.h" + +#include <gdk/gdk.h> + +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, + FOCUS_IN, + FOCUS_OUT, + 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_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; + + 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); + 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 +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; +} + +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; +} + +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 new file mode 100644 index 0000000000..a3756c4ac0 --- /dev/null +++ b/gtk/gtkeventcontrollerkey.h @@ -0,0 +1,63 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Carlos Garnacho <carlosg@gnome.org> + */ + +#ifndef __GTK_EVENT_CONTROLLER_KEY_H__ +#define __GTK_EVENT_CONTROLLER_KEY_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gdk/gdk.h> +#include <gtk/gtkeventcontroller.h> +#include <gtk/gtkimcontext.h> + +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); + +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 + +#endif /* __GTK_EVENT_CONTROLLER_KEY_H__ */ diff --git a/gtk/gtkeventcontrollermotion.c b/gtk/gtkeventcontrollermotion.c new file mode 100644 index 0000000000..75c293e205 --- /dev/null +++ b/gtk/gtkeventcontrollermotion.c @@ -0,0 +1,169 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Matthias Clasen <mclasen@redhat.com> + */ + +/** + * 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) + { + 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); + } + 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 + * @x: the x coordinate + * @y: the y coordinate + * + * 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, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + + /** + * 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Matthias Clasen <mclasen@redhat.com> + */ + +#ifndef __GTK_EVENT_CONTROLLER_MOTION_H__ +#define __GTK_EVENT_CONTROLLER_MOTION_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gdk/gdk.h> +#include <gtk/gtkeventcontroller.h> + +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__ */ diff --git a/gtk/gtkeventcontrollerscroll.c b/gtk/gtkeventcontrollerscroll.c new file mode 100644 index 0000000000..db98d7c51a --- /dev/null +++ b/gtk/gtkeventcontrollerscroll.c @@ -0,0 +1,517 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Carlos Garnacho <carlosg@gnome.org> + */ + +/** + * 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 "math.h" + +#include "gtkintl.h" +#include "gtkwidget.h" +#include "gtkeventcontrollerprivate.h" +#include "gtkeventcontrollerscroll.h" +#include "gtktypebuiltins.h" +#include "gtkmarshalers.h" +#include "gtkprivate.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: + gtk_event_controller_scroll_set_flags (scroll, 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; + + /** + * GtkEventControllerScroll:flags: + * + * The flags affecting event controller behavior + * + * Since: 3.93 + **/ + pspecs[PROP_FLAGS] = + g_param_spec_flags ("flags", + P_("Flags"), + P_("Flags"), + GTK_TYPE_EVENT_CONTROLLER_SCROLL_FLAGS, + GTK_EVENT_CONTROLLER_SCROLL_NONE, + GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + /** + * 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, + G_SIGNAL_RUN_FIRST, + 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, + G_SIGNAL_RUN_FIRST, + 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, + G_SIGNAL_RUN_FIRST, + 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, + 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)); +} + +/** + * 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) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + return g_object_new (GTK_TYPE_EVENT_CONTROLLER_SCROLL, + "widget", widget, + "flags", flags, + 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) +{ + g_return_if_fail (GTK_IS_EVENT_CONTROLLER_SCROLL (scroll)); + + if (scroll->flags == flags) + return; + + scroll->flags = flags; + g_object_notify_by_pspec (G_OBJECT (scroll), pspecs[PROP_FLAGS]); +} + +/** + * 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) +{ + 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..d1d33366c7 --- /dev/null +++ b/gtk/gtkeventcontrollerscroll.h @@ -0,0 +1,80 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Carlos Garnacho <carlosg@gnome.org> + */ + +#ifndef __GTK_EVENT_CONTROLLER_SCROLL_H__ +#define __GTK_EVENT_CONTROLLER_SCROLL_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gdk/gdk.h> +#include <gtk/gtkeventcontroller.h> + +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; + +/** + * 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, + 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__ */ 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Carlos Garnacho <carlosg@gnome.org> + */ + +/** + * 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Carlos Garnacho <carlosg@gnome.org> + */ +#ifndef __GTK_GESTURE_STYLUS_H__ +#define __GTK_GESTURE_STYLUS_H__ + +#include <gtk/gtkgesture.h> + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Carlos Garnacho <carlosg@gnome.org> + */ +#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 a2cd6f5c86..f3bfe4d7f8 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 @@ -170,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 |