From ab426801023babba7513d9e27a4dbdd44bdbc318 Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Tue, 8 May 2012 14:11:50 +0200 Subject: [PATCH 08/68] Smooth scrolling --- gdk/gdkevents.c | 32 +++++++++++++++++ gdk/gdkevents.h | 6 ++++ gdk/gdkwindow.c | 3 ++ gdk/quartz/gdkevents-quartz.c | 80 +++++++++++++++++++++++++++++++---------- gtk/gtkrange.c | 51 +++++++++++++++++++------- gtk/gtkrange.h | 4 +-- gtk/gtkscrolledwindow.c | 74 ++++++++++++++++++++++++++++++-------- 7 files changed, 203 insertions(+), 47 deletions(-) diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 53833a0..0f8bba2 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -392,6 +392,8 @@ gdk_event_new (GdkEventType type) new_event->scroll.y = 0.; new_event->scroll.x_root = 0.; new_event->scroll.y_root = 0.; + new_event->scroll.delta_x = 0.; + new_event->scroll.delta_y = 0.; break; case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: @@ -845,6 +847,36 @@ gdk_event_get_root_coords (const GdkEvent *event, return fetched; } +gboolean +gdk_event_get_scroll_deltas (const GdkEvent *event, + gdouble *delta_x, + gdouble *delta_y) +{ + gboolean fetched = TRUE; + gdouble dx = 0.0; + gdouble dy = 0.0; + + switch (event->type) + { + case GDK_SCROLL: + fetched = event->scroll.has_deltas; + dx = event->scroll.delta_x; + dy = event->scroll.delta_y; + break; + default: + fetched = FALSE; + break; + } + + if (delta_x) + *delta_x = dx; + + if (delta_y) + *delta_y = dy; + + return fetched; +} + /** * gdk_event_get_axis: * @event: a #GdkEvent diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h index 0602bd0..f6b4e04 100644 --- a/gdk/gdkevents.h +++ b/gdk/gdkevents.h @@ -337,6 +337,9 @@ struct _GdkEventScroll GdkScrollDirection direction; GdkDevice *device; gdouble x_root, y_root; + gboolean has_deltas; + gdouble delta_x; + gdouble delta_y; }; struct _GdkEventKey @@ -537,6 +540,9 @@ gboolean gdk_event_get_coords (const GdkEvent *event, gboolean gdk_event_get_root_coords (const GdkEvent *event, gdouble *x_root, gdouble *y_root); +gboolean gdk_event_get_scroll_deltas (const GdkEvent *event, + gdouble *delta_x, + gdouble *delta_y); gboolean gdk_event_get_axis (const GdkEvent *event, GdkAxisUse axis_use, gdouble *value); diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index f5f0339..d48751e 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -10800,6 +10800,9 @@ proxy_button_event (GdkEvent *source_event, event->scroll.y_root = source_event->scroll.y_root; event->scroll.state = state; event->scroll.device = source_event->scroll.device; + event->scroll.has_deltas = source_event->scroll.has_deltas; + event->scroll.delta_x = source_event->scroll.delta_x; + event->scroll.delta_y = source_event->scroll.delta_y; return TRUE; default: diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c index c99a2c9..e7d97dc 100644 --- a/gdk/quartz/gdkevents-quartz.c +++ b/gdk/quartz/gdkevents-quartz.c @@ -57,6 +57,13 @@ static GdkWindow *find_toplevel_under_pointer (GdkDisplay *display, gint *x, gint *y); +/* Protocol to build cleanly for OSX < 10.7 */ +@protocol PreciseDeltas +- (BOOL) hasPreciseScrollingDeltas; +- (CGFloat) scrollingDeltaX; +- (CGFloat) scrollingDeltaY; +@end + NSEvent * gdk_quartz_event_get_nsevent (GdkEvent *event) @@ -980,6 +987,9 @@ fill_scroll_event (GdkWindow *window, gint y, gint x_root, gint y_root, + gboolean has_deltas, + gdouble delta_x, + gdouble delta_y, GdkScrollDirection direction) { GdkWindowObject *private; @@ -999,6 +1009,9 @@ fill_scroll_event (GdkWindow *window, event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent); event->scroll.direction = direction; event->scroll.device = _gdk_display->core_pointer; + event->scroll.has_deltas = has_deltas; + event->scroll.delta_x = delta_x; + event->scroll.delta_y = delta_y; } static void @@ -1471,28 +1484,59 @@ gdk_event_translate (GdkEvent *event, case NSScrollWheel: { - float dx = [nsevent deltaX]; - float dy = [nsevent deltaY]; - GdkScrollDirection direction; - - if (dy != 0) - { - if (dy < 0.0) - direction = GDK_SCROLL_DOWN; - else - direction = GDK_SCROLL_UP; + GdkScrollDirection direction; + float dx; + float dy; - fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction); - } + if (gdk_quartz_osx_version() >= GDK_OSX_LION && + [(id ) nsevent hasPreciseScrollingDeltas]) + { + dx = [(id ) nsevent scrollingDeltaX]; + dy = [(id ) nsevent scrollingDeltaY]; - if (dx != 0) - { - if (dx < 0.0) - direction = GDK_SCROLL_RIGHT; + if (fabs (dy) > fabs (dx)) + { + if (dy < 0.0) + direction = GDK_SCROLL_DOWN; + else + direction = GDK_SCROLL_UP; + } else - direction = GDK_SCROLL_LEFT; + { + if (dx < 0.0) + direction = GDK_SCROLL_RIGHT; + else + direction = GDK_SCROLL_LEFT; + } - fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction); + fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, + TRUE, -dx, -dy, direction); + } + else + { + dx = [nsevent deltaX]; + dy = [nsevent deltaY]; + + if (dy != 0.0) + { + if (dy < 0.0) + direction = GDK_SCROLL_DOWN; + else + direction = GDK_SCROLL_UP; + + fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, + FALSE, 0.0, fabs (dy), direction); + } + else if (dx != 0.0) + { + if (dx < 0.0) + direction = GDK_SCROLL_RIGHT; + else + direction = GDK_SCROLL_LEFT; + + fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, + FALSE, fabs (dx), 0.0, direction); + } } } break; diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c index 245fbf6..d39f045 100644 --- a/gtk/gtkrange.c +++ b/gtk/gtkrange.c @@ -2561,7 +2561,7 @@ gtk_range_button_release (GtkWidget *widget, /** * _gtk_range_get_wheel_delta: * @range: a #GtkRange - * @direction: A #GdkScrollDirection + * @event: A #GdkEventScroll * * Returns a good step value for the mouse wheel. * @@ -2570,27 +2570,52 @@ gtk_range_button_release (GtkWidget *widget, * Since: 2.4 **/ gdouble -_gtk_range_get_wheel_delta (GtkRange *range, - GdkScrollDirection direction) +_gtk_range_get_wheel_delta (GtkRange *range, + GdkEventScroll *event) { GtkAdjustment *adj = range->adjustment; + gdouble dx, dy; gdouble delta; - if (GTK_IS_SCROLLBAR (range)) - delta = pow (adj->page_size, 2.0 / 3.0); + if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &dx, &dy)) + { + GtkAllocation allocation; + + gtk_widget_get_allocation (GTK_WIDGET (range), &allocation); + + if (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) == GTK_ORIENTATION_HORIZONTAL) + { + if (GTK_IS_SCROLLBAR (range) && adj->page_size > 0) + delta = dx * adj->page_size / allocation.width; + else + delta = dx * (adj->upper - adj->lower) / allocation.width; + } + else + { + if (GTK_IS_SCROLLBAR (range) && adj->page_size > 0) + delta = dy * adj->page_size / allocation.height; + else + delta = dy * (adj->upper - adj->lower) / allocation.height; + } + } else - delta = adj->step_increment * 2; - - if (direction == GDK_SCROLL_UP || - direction == GDK_SCROLL_LEFT) - delta = - delta; - + { + if (GTK_IS_SCROLLBAR (range)) + delta = pow (adj->page_size, 2.0 / 3.0); + else + delta = adj->step_increment * 2; + + if (event->direction == GDK_SCROLL_UP || + event->direction == GDK_SCROLL_LEFT) + delta = - delta; + } + if (range->inverted) delta = - delta; return delta; } - + static gboolean gtk_range_scroll_event (GtkWidget *widget, GdkEventScroll *event) @@ -2603,7 +2628,7 @@ gtk_range_scroll_event (GtkWidget *widget, gdouble delta; gboolean handled; - delta = _gtk_range_get_wheel_delta (range, event->direction); + delta = _gtk_range_get_wheel_delta (range, event); g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, adj->value + delta, diff --git a/gtk/gtkrange.h b/gtk/gtkrange.h index 5463140..c672acb 100644 --- a/gtk/gtkrange.h +++ b/gtk/gtkrange.h @@ -199,8 +199,8 @@ gint gtk_range_get_round_digits (GtkRange *range /* internal API */ -gdouble _gtk_range_get_wheel_delta (GtkRange *range, - GdkScrollDirection direction); +gdouble _gtk_range_get_wheel_delta (GtkRange *range, + GdkEventScroll *event); void _gtk_range_set_stop_values (GtkRange *range, gdouble *values, diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 1704d3c..2288b2f 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -1565,31 +1565,77 @@ static gboolean gtk_scrolled_window_scroll_event (GtkWidget *widget, GdkEventScroll *event) { - GtkWidget *range; + GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget); + gboolean handled = FALSE; + gdouble delta_x; + gdouble delta_y; g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); - if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN) - range = GTK_SCROLLED_WINDOW (widget)->vscrollbar; - else - range = GTK_SCROLLED_WINDOW (widget)->hscrollbar; + if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y)) + { + if (delta_x != 0.0 && scrolled_window->hscrollbar && + gtk_widget_get_visible (scrolled_window->hscrollbar)) + { + GtkAdjustment *adj; + gdouble new_value; + + adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)); + + new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x, + gtk_adjustment_get_lower (adj), + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj)); + + gtk_adjustment_set_value (adj, new_value); + + handled = TRUE; + } + + if (delta_y != 0.0 && scrolled_window->vscrollbar && + gtk_widget_get_visible (scrolled_window->vscrollbar)) + { + GtkAdjustment *adj; + gdouble new_value; + + adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)); + + new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y, + gtk_adjustment_get_lower (adj), + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj)); - if (range && gtk_widget_get_visible (range)) + gtk_adjustment_set_value (adj, new_value); + + handled = TRUE; + } + } + else { - GtkAdjustment *adj = GTK_RANGE (range)->adjustment; - gdouble delta, new_value; + GtkWidget *range; - delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event->direction); + if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN) + range = scrolled_window->vscrollbar; + else + range = scrolled_window->hscrollbar; - new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size); - - gtk_adjustment_set_value (adj, new_value); + if (range && gtk_widget_get_visible (range)) + { + GtkAdjustment *adj = GTK_RANGE (range)->adjustment; + gdouble delta, new_value; - return TRUE; + delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event); + + new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size); + + gtk_adjustment_set_value (adj, new_value); + + handled = TRUE; + } } - return FALSE; + return handled; } static gboolean -- 1.7.10.2 (Apple Git-33)