forked from AuroraMiddleware/gtk
43c212ac28
This patch makes that work using 1 of 2 options: 1. Add all missing enums to the switch statement or 2. Cast the switch argument to a uint to avoid having to do that (mostly for GdkEventType). I even found a bug while doing that: clearing a GtkImage with a surface did not notify thae surface property. The reason for enabling this flag even though it is tedious at times is that it is very useful when adding values to an enum, because it makes GTK immediately warn about all the switch statements where this enum is relevant. And I expect changes to enums to be frequent during the GTK4 development cycle.
403 lines
11 KiB
C
403 lines
11 KiB
C
/* 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 "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;
|
|
case GDK_SCROLL_SMOOTH:
|
|
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;
|
|
}
|