forked from AuroraMiddleware/gtk
scrolledwindow: Trigger builtin kinetic deceleration on libinput devices
The libinput driver will send a 0/0 scroll event on touchpads and other devices where it knows scrolling stopped for sure. Use these events to trigger kinetic scrolling from there. The mechanism is similar to GtkGestureSwipe, we keep a backlog of the latest dx/dy till a previous point in time, and calculate the final velocities from there, with the difference we're dealing with scroll units, and not pixel distances. https://bugzilla.gnome.org/show_bug.cgi?id=749770
This commit is contained in:
parent
fc28303948
commit
48bfabe59e
@ -149,6 +149,7 @@
|
|||||||
#define MAX_OVERSHOOT_DISTANCE 100
|
#define MAX_OVERSHOOT_DISTANCE 100
|
||||||
#define DECELERATION_FRICTION 4
|
#define DECELERATION_FRICTION 4
|
||||||
#define OVERSHOOT_FRICTION 20
|
#define OVERSHOOT_FRICTION 20
|
||||||
|
#define SCROLL_CAPTURE_THRESHOLD_MS 150
|
||||||
|
|
||||||
/* Animated scrolling */
|
/* Animated scrolling */
|
||||||
#define ANIMATION_DURATION 200
|
#define ANIMATION_DURATION 200
|
||||||
@ -180,6 +181,13 @@ typedef struct
|
|||||||
guint over_timeout_id;
|
guint over_timeout_id;
|
||||||
} Indicator;
|
} Indicator;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gdouble dx;
|
||||||
|
gdouble dy;
|
||||||
|
guint32 evtime;
|
||||||
|
} ScrollHistoryElem;
|
||||||
|
|
||||||
struct _GtkScrolledWindowPrivate
|
struct _GtkScrolledWindowPrivate
|
||||||
{
|
{
|
||||||
GtkWidget *hscrollbar;
|
GtkWidget *hscrollbar;
|
||||||
@ -208,6 +216,9 @@ struct _GtkScrolledWindowPrivate
|
|||||||
GtkGesture *long_press_gesture;
|
GtkGesture *long_press_gesture;
|
||||||
GtkGesture *swipe_gesture;
|
GtkGesture *swipe_gesture;
|
||||||
|
|
||||||
|
GArray *scroll_history;
|
||||||
|
GdkDevice *scroll_device;
|
||||||
|
|
||||||
/* These two gestures are mutually exclusive */
|
/* These two gestures are mutually exclusive */
|
||||||
GtkGesture *drag_gesture;
|
GtkGesture *drag_gesture;
|
||||||
GtkGesture *pan_gesture;
|
GtkGesture *pan_gesture;
|
||||||
@ -869,16 +880,16 @@ scrolled_window_drag_end_cb (GtkScrolledWindow *scrolled_window,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
scrolled_window_swipe_cb (GtkScrolledWindow *scrolled_window,
|
gtk_scrolled_window_decelerate (GtkScrolledWindow *scrolled_window,
|
||||||
gdouble x_velocity,
|
gdouble x_velocity,
|
||||||
gdouble y_velocity)
|
gdouble y_velocity)
|
||||||
{
|
{
|
||||||
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
|
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
|
||||||
gboolean overshoot;
|
gboolean overshoot;
|
||||||
|
|
||||||
overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
|
overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
|
||||||
priv->x_velocity = -x_velocity;
|
priv->x_velocity = x_velocity;
|
||||||
priv->y_velocity = -y_velocity;
|
priv->y_velocity = y_velocity;
|
||||||
|
|
||||||
/* Zero out vector components for which we don't scroll */
|
/* Zero out vector components for which we don't scroll */
|
||||||
if (!may_hscroll (scrolled_window))
|
if (!may_hscroll (scrolled_window))
|
||||||
@ -893,6 +904,14 @@ scrolled_window_swipe_cb (GtkScrolledWindow *scrolled_window,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
scrolled_window_swipe_cb (GtkScrolledWindow *scrolled_window,
|
||||||
|
gdouble x_velocity,
|
||||||
|
gdouble y_velocity)
|
||||||
|
{
|
||||||
|
gtk_scrolled_window_decelerate (scrolled_window, -x_velocity, -y_velocity);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
scrolled_window_long_press_cb (GtkScrolledWindow *scrolled_window,
|
scrolled_window_long_press_cb (GtkScrolledWindow *scrolled_window,
|
||||||
gdouble x,
|
gdouble x,
|
||||||
@ -1114,6 +1133,87 @@ get_scroll_unit (GtkScrolledWindow *sw,
|
|||||||
return scroll_unit;
|
return scroll_unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
scroll_history_push (GtkScrolledWindow *sw,
|
||||||
|
GdkEventScroll *event)
|
||||||
|
{
|
||||||
|
GtkScrolledWindowPrivate *priv = sw->priv;
|
||||||
|
ScrollHistoryElem new_item;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
if (event->direction != GDK_SCROLL_SMOOTH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < priv->scroll_history->len; i++)
|
||||||
|
{
|
||||||
|
ScrollHistoryElem *elem;
|
||||||
|
|
||||||
|
elem = &g_array_index (priv->scroll_history, ScrollHistoryElem, i);
|
||||||
|
|
||||||
|
if (elem->evtime >= event->time - SCROLL_CAPTURE_THRESHOLD_MS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 0)
|
||||||
|
g_array_remove_range (priv->scroll_history, 0, i);
|
||||||
|
|
||||||
|
new_item.dx = event->delta_x;
|
||||||
|
new_item.dy = event->delta_y;
|
||||||
|
new_item.evtime = event->time;
|
||||||
|
g_array_append_val (priv->scroll_history, new_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
scroll_history_reset (GtkScrolledWindow *sw)
|
||||||
|
{
|
||||||
|
GtkScrolledWindowPrivate *priv = sw->priv;
|
||||||
|
|
||||||
|
if (priv->scroll_history->len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_array_remove_range (priv->scroll_history, 0,
|
||||||
|
priv->scroll_history->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
scroll_history_finish (GtkScrolledWindow *sw,
|
||||||
|
gdouble *velocity_x,
|
||||||
|
gdouble *velocity_y)
|
||||||
|
{
|
||||||
|
GtkScrolledWindowPrivate *priv = sw->priv;
|
||||||
|
gdouble accum_dx = 0, accum_dy = 0;
|
||||||
|
guint32 first = 0, last = 0;
|
||||||
|
gdouble xunit, yunit;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
if (priv->scroll_history->len == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (i = 0; i < priv->scroll_history->len; i++)
|
||||||
|
{
|
||||||
|
ScrollHistoryElem *elem;
|
||||||
|
|
||||||
|
elem = &g_array_index (priv->scroll_history, ScrollHistoryElem, i);
|
||||||
|
accum_dx += elem->dx;
|
||||||
|
accum_dy += elem->dy;
|
||||||
|
last = elem->evtime;
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
first = elem->evtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last == first)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
xunit = get_scroll_unit (sw, GTK_ORIENTATION_HORIZONTAL);
|
||||||
|
yunit = get_scroll_unit (sw, GTK_ORIENTATION_VERTICAL);
|
||||||
|
*velocity_x = (accum_dx * 1000 * xunit) / (last - first);
|
||||||
|
*velocity_y = (accum_dy * 1000 * yunit) / (last - first);
|
||||||
|
scroll_history_reset (sw);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
captured_event_cb (GtkWidget *widget,
|
captured_event_cb (GtkWidget *widget,
|
||||||
GdkEvent *event)
|
GdkEvent *event)
|
||||||
@ -1127,20 +1227,31 @@ captured_event_cb (GtkWidget *widget,
|
|||||||
|
|
||||||
sw = GTK_SCROLLED_WINDOW (widget);
|
sw = GTK_SCROLLED_WINDOW (widget);
|
||||||
priv = sw->priv;
|
priv = sw->priv;
|
||||||
|
source_device = gdk_event_get_source_device (event);
|
||||||
|
|
||||||
if (event->type == GDK_SCROLL)
|
if (event->type == GDK_SCROLL)
|
||||||
{
|
{
|
||||||
gdouble dx, dy;
|
gdouble dx, dy, vel_x, vel_y;
|
||||||
|
|
||||||
/* The libinput driver may generate a final event with dx=dy=0
|
/* The libinput driver may generate a final event with dx=dy=0
|
||||||
* after scrolling finished, this is usually an indication that
|
* after scrolling finished, start kinetic scrolling when this
|
||||||
* the deceleration animation just started, so we definitely
|
* happens.
|
||||||
* shouldn't cancel it.
|
|
||||||
*/
|
*/
|
||||||
if (event->scroll.direction != GDK_SCROLL_SMOOTH ||
|
gtk_scrolled_window_cancel_deceleration (sw);
|
||||||
(gdk_event_get_scroll_deltas (event, &dx, &dy) &&
|
gdk_event_get_scroll_deltas (event, &dx, &dy);
|
||||||
(dx != 0 || dy != 0)))
|
|
||||||
gtk_scrolled_window_cancel_deceleration (sw);
|
if (priv->scroll_device != source_device)
|
||||||
|
{
|
||||||
|
priv->scroll_device = source_device;
|
||||||
|
scroll_history_reset (sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
scroll_history_push (sw, &event->scroll);
|
||||||
|
|
||||||
|
if (event->scroll.direction == GDK_SCROLL_SMOOTH &&
|
||||||
|
dx == 0 && dy == 0 &&
|
||||||
|
scroll_history_finish (sw, &vel_x, &vel_y))
|
||||||
|
gtk_scrolled_window_decelerate (sw, vel_x, vel_y);
|
||||||
|
|
||||||
return GDK_EVENT_PROPAGATE;
|
return GDK_EVENT_PROPAGATE;
|
||||||
}
|
}
|
||||||
@ -1152,7 +1263,6 @@ captured_event_cb (GtkWidget *widget,
|
|||||||
event->type != GDK_LEAVE_NOTIFY)
|
event->type != GDK_LEAVE_NOTIFY)
|
||||||
return GDK_EVENT_PROPAGATE;
|
return GDK_EVENT_PROPAGATE;
|
||||||
|
|
||||||
source_device = gdk_event_get_source_device (event);
|
|
||||||
input_source = gdk_device_get_source (source_device);
|
input_source = gdk_device_get_source (source_device);
|
||||||
|
|
||||||
if (input_source == GDK_SOURCE_KEYBOARD ||
|
if (input_source == GDK_SOURCE_KEYBOARD ||
|
||||||
@ -1265,6 +1375,8 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
|
|||||||
G_CALLBACK (scrolled_window_long_press_cancelled_cb),
|
G_CALLBACK (scrolled_window_long_press_cancelled_cb),
|
||||||
scrolled_window);
|
scrolled_window);
|
||||||
|
|
||||||
|
priv->scroll_history = g_array_new (FALSE, FALSE, sizeof (ScrollHistoryElem));
|
||||||
|
|
||||||
gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
|
gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
|
||||||
gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
|
gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
|
||||||
|
|
||||||
@ -1877,6 +1989,7 @@ gtk_scrolled_window_destroy (GtkWidget *widget)
|
|||||||
g_clear_object (&priv->drag_gesture);
|
g_clear_object (&priv->drag_gesture);
|
||||||
g_clear_object (&priv->swipe_gesture);
|
g_clear_object (&priv->swipe_gesture);
|
||||||
g_clear_object (&priv->long_press_gesture);
|
g_clear_object (&priv->long_press_gesture);
|
||||||
|
g_clear_pointer (&priv->scroll_history, (GDestroyNotify) g_array_unref);
|
||||||
|
|
||||||
GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->destroy (widget);
|
GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->destroy (widget);
|
||||||
}
|
}
|
||||||
@ -2966,7 +3079,10 @@ start_scroll_deceleration_cb (gpointer user_data)
|
|||||||
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
|
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
|
||||||
|
|
||||||
priv->scroll_events_overshoot_id = 0;
|
priv->scroll_events_overshoot_id = 0;
|
||||||
gtk_scrolled_window_start_deceleration (scrolled_window);
|
|
||||||
|
if (!priv->deceleration_id)
|
||||||
|
gtk_scrolled_window_start_deceleration (scrolled_window);
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user