From 62490218ee9c265730e8aa95494c7a7fd2ef4bc1 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Wed, 6 Apr 2022 18:47:14 +0300 Subject: [PATCH 1/5] widgets: Fix support for smooth scrolling in gradient editor Smooth scrolling requires that we keep the scroll (or zoom, as is in this case) state as a floating-point number. The scroll deltas are often less than one, thus storing scroll state as an integer will effectively ignore most of the scroll events. --- app/widgets/gimpgradienteditor.c | 2 +- app/widgets/gimpgradienteditor.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/widgets/gimpgradienteditor.c b/app/widgets/gimpgradienteditor.c index d4eaeeaecb1..9c3e8c634df 100644 --- a/app/widgets/gimpgradienteditor.c +++ b/app/widgets/gimpgradienteditor.c @@ -879,7 +879,7 @@ gradient_editor_scrollbar_update (GtkAdjustment *adjustment, gchar *str1; gchar *str2; - str1 = g_strdup_printf (_("Zoom factor: %d:1"), + str1 = g_strdup_printf (_("Zoom factor: %f:1"), editor->zoom_factor); str2 = g_strdup_printf (_("Displaying [%0.4f, %0.4f]"), diff --git a/app/widgets/gimpgradienteditor.h b/app/widgets/gimpgradienteditor.h index 8ecc5b2e1a8..e3f939d36b0 100644 --- a/app/widgets/gimpgradienteditor.h +++ b/app/widgets/gimpgradienteditor.h @@ -60,7 +60,7 @@ struct _GimpGradientEditor GtkWidget *control; /* Zoom and scrollbar */ - guint zoom_factor; + gdouble zoom_factor; GtkAdjustment *scroll_data; /* Gradient view */ -- GitLab From ecb20099603a6c83960cbadb9e16a2881441590b Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Wed, 6 Apr 2022 18:47:13 +0300 Subject: [PATCH 2/5] widgets: Implement support for zoom by pitch in gradient editor --- app/widgets/gimpgradienteditor.c | 67 +++++++++++++++++++++++++++++++- app/widgets/gimpgradienteditor.h | 2 + 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/app/widgets/gimpgradienteditor.c b/app/widgets/gimpgradienteditor.c index 9c3e8c634df..1ec8bca5cc2 100644 --- a/app/widgets/gimpgradienteditor.c +++ b/app/widgets/gimpgradienteditor.c @@ -96,7 +96,8 @@ GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | \ GDK_SCROLL_MASK | \ - GDK_SMOOTH_SCROLL_MASK) + GDK_SMOOTH_SCROLL_MASK | \ + GDK_TOUCHPAD_GESTURE_MASK) #define GRAD_CONTROL_EVENT_MASK (GDK_EXPOSURE_MASK | \ GDK_LEAVE_NOTIFY_MASK | \ @@ -178,6 +179,14 @@ static void view_pick_color (GimpGradientEditor *editor, GimpColorPickState pick_state, gint x); +static void view_zoom_gesture_begin (GtkGestureZoom *gesture, + GdkEventSequence *sequence, + GimpGradientEditor *editor); + +static void view_zoom_gesture_update (GtkGestureZoom *gesture, + GdkEventSequence *sequence, + GimpGradientEditor *editor); + /* Gradient control functions */ static gboolean control_events (GtkWidget *widget, @@ -340,6 +349,18 @@ gimp_gradient_editor_init (GimpGradientEditor *editor) gradient_editor_drop_color, editor); + editor->zoom_gesture = gtk_gesture_zoom_new (GTK_WIDGET (data_editor->view)); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (editor->zoom_gesture), + GTK_PHASE_CAPTURE); + + + g_signal_connect (editor->zoom_gesture, "begin", + G_CALLBACK (view_zoom_gesture_begin), + editor); + g_signal_connect (editor->zoom_gesture, "update", + G_CALLBACK (view_zoom_gesture_update), + editor); + /* Gradient control */ editor->control = gtk_drawing_area_new (); gtk_widget_set_size_request (editor->control, -1, GRAD_CONTROL_HEIGHT); @@ -450,6 +471,8 @@ gimp_gradient_editor_dispose (GObject *object) gtk_dialog_response (GTK_DIALOG (editor->color_dialog), GTK_RESPONSE_CANCEL); + g_clear_object (&editor->zoom_gesture); + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -672,7 +695,7 @@ gimp_gradient_editor_zoom (GimpGradientEditor *editor, delta = ABS (delta); } - else + else if (zoom_type != GIMP_ZOOM_PINCH) { delta = 1.0; } @@ -717,6 +740,26 @@ gimp_gradient_editor_zoom (GimpGradientEditor *editor, page_size = 1.0; break; + case GIMP_ZOOM_PINCH: + if (delta > 0.0) + editor->zoom_factor = editor->zoom_factor * (1.0 + delta); + else if (delta < 0.0) + editor->zoom_factor = editor->zoom_factor / (1.0 + -delta); + else + return; + + if (editor->zoom_factor < 1) + editor->zoom_factor = 1; + + page_size = 1.0 / editor->zoom_factor; + value = old_value + (old_page_size - page_size) / 2.0; + + if (value < 0.0) + value = 0.0; + else if ((value + page_size) > 1.0) + value = 1.0 - page_size; + break; + case GIMP_ZOOM_SMOOTH: /* can't happen, see above switch() */ break; } @@ -1306,6 +1349,26 @@ view_pick_color (GimpGradientEditor *editor, g_free (str3); } +static void +view_zoom_gesture_begin (GtkGestureZoom *gesture, + GdkEventSequence *sequence, + GimpGradientEditor *editor) +{ + editor->last_zoom_scale = gtk_gesture_zoom_get_scale_delta (gesture); +} + +static void +view_zoom_gesture_update (GtkGestureZoom *gesture, + GdkEventSequence *sequence, + GimpGradientEditor *editor) +{ + gdouble current_scale = gtk_gesture_zoom_get_scale_delta (gesture); + gdouble delta = (current_scale - editor->last_zoom_scale) / editor->last_zoom_scale; + editor->last_zoom_scale = current_scale; + + gimp_gradient_editor_zoom (editor, GIMP_ZOOM_PINCH, delta); +} + /***** Gradient control functions *****/ static gboolean diff --git a/app/widgets/gimpgradienteditor.h b/app/widgets/gimpgradienteditor.h index e3f939d36b0..31219dfdf3b 100644 --- a/app/widgets/gimpgradienteditor.h +++ b/app/widgets/gimpgradienteditor.h @@ -62,6 +62,8 @@ struct _GimpGradientEditor /* Zoom and scrollbar */ gdouble zoom_factor; GtkAdjustment *scroll_data; + GtkGesture *zoom_gesture; + gdouble last_zoom_scale; /* Gradient view */ gint view_last_x; -- GitLab From 8d0e6efa109f5719b25aeed05471003b79900829 Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Wed, 6 Apr 2022 18:47:15 +0300 Subject: [PATCH 3/5] widgets: Reset view_last_x whevener gradient editor leaves focus --- app/widgets/gimpgradienteditor.c | 2 ++ app/widgets/gimpgradienteditor.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/widgets/gimpgradienteditor.c b/app/widgets/gimpgradienteditor.c index 1ec8bca5cc2..e1691bb930a 100644 --- a/app/widgets/gimpgradienteditor.c +++ b/app/widgets/gimpgradienteditor.c @@ -1146,6 +1146,7 @@ view_events (GtkWidget *widget, { case GDK_LEAVE_NOTIFY: gradient_editor_set_hint (editor, NULL, NULL, NULL, NULL); + editor->view_last_x = -1; break; case GDK_MOTION_NOTIFY: @@ -1388,6 +1389,7 @@ control_events (GtkWidget *widget, { case GDK_LEAVE_NOTIFY: gradient_editor_set_hint (editor, NULL, NULL, NULL, NULL); + editor->control_last_x = -1; break; case GDK_BUTTON_PRESS: diff --git a/app/widgets/gimpgradienteditor.h b/app/widgets/gimpgradienteditor.h index 31219dfdf3b..09dcc50a89c 100644 --- a/app/widgets/gimpgradienteditor.h +++ b/app/widgets/gimpgradienteditor.h @@ -66,7 +66,7 @@ struct _GimpGradientEditor gdouble last_zoom_scale; /* Gradient view */ - gint view_last_x; + gint view_last_x; /* -1 if mouse has left focus */ gboolean view_button_down; /* Gradient control */ @@ -76,7 +76,7 @@ struct _GimpGradientEditor GradientEditorDragMode control_drag_mode; /* What is being dragged? */ guint32 control_click_time; /* Time when mouse was pressed */ gboolean control_compress; /* Compressing/expanding handles */ - gint control_last_x; /* Last mouse position when dragging */ + gint control_last_x; /* Last mouse position, -1 if out of focus */ gdouble control_last_gx; /* Last position (wrt gradient) when dragging */ gdouble control_orig_pos; /* Original click position when dragging */ -- GitLab From ae3c4c3577a0664f6311d3c3f68c0c77452c871c Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Wed, 6 Apr 2022 18:47:16 +0300 Subject: [PATCH 4/5] widgets: Implement zoom focus in gradient editor Zoom focus centers the point that we're zooming into at the location of the mouse pointer. Default zoom focus value is 0.5 which results in previous behavior. --- app/actions/gradient-editor-commands.c | 2 +- app/widgets/gimpgradienteditor.c | 72 ++++++++++++++++++++++---- app/widgets/gimpgradienteditor.h | 3 +- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/app/actions/gradient-editor-commands.c b/app/actions/gradient-editor-commands.c index 01864061a86..cb30c8cda85 100644 --- a/app/actions/gradient-editor-commands.c +++ b/app/actions/gradient-editor-commands.c @@ -660,7 +660,7 @@ gradient_editor_zoom_cmd_callback (GimpAction *action, GimpGradientEditor *editor = GIMP_GRADIENT_EDITOR (data); GimpZoomType zoom_type = (GimpZoomType) g_variant_get_int32 (value); - gimp_gradient_editor_zoom (editor, zoom_type, 1.0); + gimp_gradient_editor_zoom (editor, zoom_type, 1.0, 0.5); } diff --git a/app/widgets/gimpgradienteditor.c b/app/widgets/gimpgradienteditor.c index e1691bb930a..c721dd834ec 100644 --- a/app/widgets/gimpgradienteditor.c +++ b/app/widgets/gimpgradienteditor.c @@ -187,6 +187,8 @@ static void view_zoom_gesture_update (GtkGestureZoom *gesture, GdkEventSequence *sequence, GimpGradientEditor *editor); +static gdouble view_get_normalized_last_x_pos (GimpGradientEditor *editor); + /* Gradient control functions */ static gboolean control_events (GtkWidget *widget, @@ -226,6 +228,8 @@ static double control_move (GimpGradientEditor *editor, GimpGradientSegment *range_r, gdouble delta); +static gdouble control_get_normalized_last_x_pos (GimpGradientEditor *editor); + /* Control update/redraw functions */ static void control_update (GimpGradientEditor *editor, @@ -674,7 +678,8 @@ gimp_gradient_editor_edit_right_color (GimpGradientEditor *editor) void gimp_gradient_editor_zoom (GimpGradientEditor *editor, GimpZoomType zoom_type, - gdouble delta) + gdouble delta, + gdouble zoom_focus_x) { GtkAdjustment *adjustment; gdouble old_value; @@ -713,7 +718,7 @@ gimp_gradient_editor_zoom (GimpGradientEditor *editor, editor->zoom_factor += delta; page_size = 1.0 / editor->zoom_factor; - value = old_value + (old_page_size - page_size) / 2.0; + value = old_value + (old_page_size - page_size) * zoom_focus_x; break; case GIMP_ZOOM_OUT_MORE: @@ -724,7 +729,7 @@ gimp_gradient_editor_zoom (GimpGradientEditor *editor, editor->zoom_factor -= delta; page_size = 1.0 / editor->zoom_factor; - value = old_value - (page_size - old_page_size) / 2.0; + value = old_value - (page_size - old_page_size) * zoom_focus_x; if (value < 0.0) value = 0.0; @@ -752,7 +757,7 @@ gimp_gradient_editor_zoom (GimpGradientEditor *editor, editor->zoom_factor = 1; page_size = 1.0 / editor->zoom_factor; - value = old_value + (old_page_size - page_size) / 2.0; + value = old_value + (old_page_size - page_size) * zoom_focus_x; if (value < 0.0) value = 0.0; @@ -1210,16 +1215,19 @@ view_events (GtkWidget *widget, switch (sevent->direction) { case GDK_SCROLL_UP: - gimp_gradient_editor_zoom (editor, GIMP_ZOOM_IN, 1.0); + gimp_gradient_editor_zoom (editor, GIMP_ZOOM_IN, 1.0, + view_get_normalized_last_x_pos (editor)); break; case GDK_SCROLL_DOWN: - gimp_gradient_editor_zoom (editor, GIMP_ZOOM_OUT, 1.0); + gimp_gradient_editor_zoom (editor, GIMP_ZOOM_OUT, 1.0, + view_get_normalized_last_x_pos (editor)); break; case GDK_SCROLL_SMOOTH: gdk_event_get_scroll_deltas (event, NULL, &delta); - gimp_gradient_editor_zoom (editor, GIMP_ZOOM_SMOOTH, delta); + gimp_gradient_editor_zoom (editor, GIMP_ZOOM_SMOOTH, delta, + view_get_normalized_last_x_pos (editor)); break; default: @@ -1367,7 +1375,26 @@ view_zoom_gesture_update (GtkGestureZoom *gesture, gdouble delta = (current_scale - editor->last_zoom_scale) / editor->last_zoom_scale; editor->last_zoom_scale = current_scale; - gimp_gradient_editor_zoom (editor, GIMP_ZOOM_PINCH, delta); + gimp_gradient_editor_zoom (editor, GIMP_ZOOM_PINCH, delta, + view_get_normalized_last_x_pos (editor)); +} + +static gdouble +view_get_normalized_last_x_pos (GimpGradientEditor *editor) +{ + GtkAllocation allocation; + gdouble normalized; + if (editor->view_last_x < 0) + return 0.5; + + gtk_widget_get_allocation (GIMP_DATA_EDITOR (editor)->view, &allocation); + normalized = (double) editor->view_last_x / (allocation.width - 1); + + if (normalized < 0) + return 0; + if (normalized > 1.0) + return 1; + return normalized; } /***** Gradient control functions *****/ @@ -1427,16 +1454,19 @@ control_events (GtkWidget *widget, switch (sevent->direction) { case GDK_SCROLL_UP: - gimp_gradient_editor_zoom (editor, GIMP_ZOOM_IN, 1.0); + gimp_gradient_editor_zoom (editor, GIMP_ZOOM_IN, 1.0, + control_get_normalized_last_x_pos (editor)); break; case GDK_SCROLL_DOWN: - gimp_gradient_editor_zoom (editor, GIMP_ZOOM_OUT, 1.0); + gimp_gradient_editor_zoom (editor, GIMP_ZOOM_OUT, 1.0, + control_get_normalized_last_x_pos (editor)); break; case GDK_SCROLL_SMOOTH: gdk_event_get_scroll_deltas (event, NULL, &delta); - gimp_gradient_editor_zoom (editor, GIMP_ZOOM_SMOOTH, delta); + gimp_gradient_editor_zoom (editor, GIMP_ZOOM_SMOOTH, delta, + control_get_normalized_last_x_pos (editor)); break; default: @@ -2024,6 +2054,26 @@ control_move (GimpGradientEditor *editor, /*****/ +static gdouble +control_get_normalized_last_x_pos (GimpGradientEditor *editor) +{ + GtkAllocation allocation; + gdouble normalized; + if (editor->control_last_x < 0) + return 0.5; + + gtk_widget_get_allocation (editor->control, &allocation); + normalized = (double) editor->control_last_x / (allocation.width - 1); + + if (normalized < 0) + return 0; + if (normalized > 1.0) + return 1; + return normalized; +} + +/*****/ + static void control_update (GimpGradientEditor *editor, GimpGradient *gradient, diff --git a/app/widgets/gimpgradienteditor.h b/app/widgets/gimpgradienteditor.h index 09dcc50a89c..98dfff27f2e 100644 --- a/app/widgets/gimpgradienteditor.h +++ b/app/widgets/gimpgradienteditor.h @@ -115,7 +115,8 @@ void gimp_gradient_editor_edit_right_color (GimpGradientEditor *editor) void gimp_gradient_editor_zoom (GimpGradientEditor *editor, GimpZoomType zoom_type, - gdouble delta); + gdouble delta, + gdouble zoom_focus_x); #endif /* __GIMP_GRADIENT_EDITOR_H__ */ -- GitLab From 40743c90a2cd6ab3b1f1a2b3b7fe5c1aae829e7a Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Thu, 7 Apr 2022 00:15:19 +0300 Subject: [PATCH 5/5] widgets: Fix out of bounds zoom in gradient editor When zooming using scroll events it is possible to zoom out of gradient bounds because the check for out of bounds zoom happens with the zoom value before the delta is applied. The correct way is to limit the zoom value after the delta is applied. --- app/widgets/gimpgradienteditor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/widgets/gimpgradienteditor.c b/app/widgets/gimpgradienteditor.c index c721dd834ec..d40b67a91c4 100644 --- a/app/widgets/gimpgradienteditor.c +++ b/app/widgets/gimpgradienteditor.c @@ -723,11 +723,11 @@ gimp_gradient_editor_zoom (GimpGradientEditor *editor, case GIMP_ZOOM_OUT_MORE: case GIMP_ZOOM_OUT: - if (editor->zoom_factor <= 1) - return; - editor->zoom_factor -= delta; + if (editor->zoom_factor < 1) + editor->zoom_factor = 1; + page_size = 1.0 / editor->zoom_factor; value = old_value - (page_size - old_page_size) * zoom_focus_x; -- GitLab