[gimp/wip/gradient-edit: 7/42] app: allow adding and removing sliders to/from a GimpToolLine



commit 41e4f1752e964be4db8dafb480d4e0ef5a348cc1
Author: Ell <ell_se yahoo com>
Date:   Sun Jul 23 19:06:11 2017 -0400

    app: allow adding and removing sliders to/from a GimpToolLine
    
    Add support for adding and removing sliders to/from a GimpToolLine,
    using three new signals:
    
      - can-add-slider:  Takes a double argument in the range [0,1],
        indicating a location along the line, and returns a boolean
        value, indicating whether a slider can be added at that
        location.
    
      - add-slider:  Takes a double argument in the range [0,1],
        indicating a location along the line, for which can-add-slider
        returned TRUE.  In response, should add a new slider at that
        location, and return its index, or a negative value if no
        slider was added.
    
      - remove-slider:  Takes a slider index.  In response, may remove
        the slider.
    
    On the UI side, when the cursor is close enough to the line, but
    not within the hit area of an existing handle, GimpToolLine checks
    if a slider can be added at the cursor position, using can-add-
    slider.  If a slider can be added, a dashed circle appears at the
    cursor position along the line, indicating where a slider will be
    added.  The cursor is added by clicking, which emits an add-slider
    signal; if the signal returns a slider index, the new slider is
    selected, and can be subsequently dragged.
    
    Removing a slider is done by either selecting the slider and
    pressing backspace (or delete, although we don't actually forward
    it to the tool atm,) or by "tearing" the slider: when dragging
    the slider, if the cursor is far enough from the liner, a dashed
    circle appears around the slider, and releasing the mouse removes
    the slider.

 app/core/gimpmarshal.list  |    3 +
 app/display/gimptoolline.c |  331 +++++++++++++++++++++++++++++++++++++-------
 app/display/gimptoolline.h |    8 +-
 3 files changed, 288 insertions(+), 54 deletions(-)
---
diff --git a/app/core/gimpmarshal.list b/app/core/gimpmarshal.list
index d9e0641..add1892 100644
--- a/app/core/gimpmarshal.list
+++ b/app/core/gimpmarshal.list
@@ -23,6 +23,7 @@
 #   BOOL        deprecated alias for BOOLEAN
 
 BOOLEAN: BOOLEAN
+BOOLEAN: DOUBLE
 BOOLEAN: ENUM, INT
 BOOLEAN: OBJECT
 BOOLEAN: OBJECT, POINTER
@@ -30,6 +31,8 @@ BOOLEAN: OBJECT, POINTER, STRING
 BOOLEAN: STRING
 BOOLEAN: STRING, FLAGS
 
+INT: DOUBLE
+
 VOID: BOOLEAN
 VOID: BOOLEAN, INT, INT, INT, INT
 VOID: BOXED
diff --git a/app/display/gimptoolline.c b/app/display/gimptoolline.c
index ae392fe..dfa3fba 100644
--- a/app/display/gimptoolline.c
+++ b/app/display/gimptoolline.c
@@ -33,6 +33,7 @@
 #include "display-types.h"
 
 #include "core/gimp-utils.h"
+#include "core/gimpmarshal.h"
 
 #include "widgets/gimpwidgets-utils.h"
 
@@ -40,6 +41,7 @@
 #include "gimpcanvashandle.h"
 #include "gimpcanvasline.h"
 #include "gimpdisplayshell.h"
+#include "gimpdisplayshell-cursor.h"
 #include "gimptoolline.h"
 
 #include "gimp-intl.h"
@@ -53,6 +55,11 @@
 #define SLIDER_HANDLE_SIZE   (ENDPOINT_HANDLE_SIZE * 2 / 3)
 #define HANDLE_CIRCLE_SCALE  1.8
 #define LINE_VICINITY        ((gint) (SLIDER_HANDLE_SIZE * HANDLE_CIRCLE_SCALE) / 2)
+#define SLIDER_TEAR_DISTANCE (5 * LINE_VICINITY)
+
+
+/* hover-only "handles" */
+#define HOVER_NEW_SLIDER     (GIMP_TOOL_LINE_HANDLE_NONE - 1)
 
 
 typedef enum
@@ -76,6 +83,9 @@ enum
 
 enum
 {
+  CAN_ADD_SLIDER,
+  ADD_SLIDER,
+  REMOVE_SLIDER,
   SELECTION_CHANGED,
   LAST_SIGNAL
 };
@@ -99,6 +109,8 @@ struct _GimpToolLinePrivate
   gdouble            mouse_x;
   gdouble            mouse_y;
   gint               hover;
+  gdouble            new_slider_value;
+  gboolean           remove_slider;
   GimpToolLineGrab   grab;
 
   GimpCanvasItem    *line;
@@ -161,6 +173,11 @@ static GimpControllerSlider *
 static GimpCanvasItem *
                 gimp_tool_line_get_handle      (GimpToolLine          *line,
                                                 gint                   handle);
+static gdouble  gimp_tool_line_project_point   (GimpToolLine          *line,
+                                                gdouble                x,
+                                                gdouble                y,
+                                                gboolean               constrain,
+                                                gdouble               *dist);
 
 static gboolean
                gimp_tool_line_selection_motion (GimpToolLine          *line,
@@ -205,6 +222,36 @@ gimp_tool_line_class_init (GimpToolLineClass *klass)
   widget_class->motion_modifier = gimp_tool_line_motion_modifier;
   widget_class->get_cursor      = gimp_tool_line_get_cursor;
 
+  line_signals[CAN_ADD_SLIDER] =
+    g_signal_new ("can-add-slider",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GimpToolLineClass, can_add_slider),
+                  NULL, NULL,
+                  gimp_marshal_BOOLEAN__DOUBLE,
+                  G_TYPE_BOOLEAN, 1,
+                  G_TYPE_DOUBLE);
+
+  line_signals[ADD_SLIDER] =
+    g_signal_new ("add-slider",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GimpToolLineClass, add_slider),
+                  NULL, NULL,
+                  gimp_marshal_INT__DOUBLE,
+                  G_TYPE_INT, 1,
+                  G_TYPE_DOUBLE);
+
+  line_signals[REMOVE_SLIDER] =
+    g_signal_new ("remove-slider",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (GimpToolLineClass, remove_slider),
+                  NULL, NULL,
+                  gimp_marshal_VOID__INT,
+                  G_TYPE_NONE, 1,
+                  G_TYPE_INT);
+
   line_signals[SELECTION_CHANGED] =
     g_signal_new ("selection-changed",
                   G_TYPE_FROM_CLASS (klass),
@@ -544,7 +591,8 @@ gimp_tool_line_button_press (GimpToolWidget      *widget,
   GimpToolLine        *line    = GIMP_TOOL_LINE (widget);
   GimpToolLinePrivate *private = line->private;
 
-  private->grab = GRAB_NONE;
+  private->grab          = GRAB_NONE;
+  private->remove_slider = FALSE;
 
   private->saved_x1 = private->x1;
   private->saved_y1 = private->y1;
@@ -557,12 +605,31 @@ gimp_tool_line_button_press (GimpToolWidget      *widget,
         gimp_tool_line_get_slider (line, private->hover)->value;
     }
 
-  if (private->hover != GIMP_TOOL_LINE_HANDLE_NONE)
+  if (private->hover > GIMP_TOOL_LINE_HANDLE_NONE)
     {
       gimp_tool_line_set_selection (line, private->hover);
 
       private->grab = GRAB_SELECTION;
     }
+  else if (private->hover == HOVER_NEW_SLIDER)
+    {
+      gint slider;
+
+      g_signal_emit (line, line_signals[ADD_SLIDER], 0,
+                     private->new_slider_value, &slider);
+
+      g_return_val_if_fail (slider < (gint) private->sliders->len, FALSE);
+
+      if (slider >= 0)
+        {
+          gimp_tool_line_set_selection (line, slider);
+
+          private->saved_slider_value =
+            gimp_tool_line_get_slider (line, private->selection)->value;
+
+          private->grab = GRAB_SELECTION;
+        }
+    }
   else if (state & GRAB_LINE_MASK)
     {
       private->grab = GRAB_LINE;
@@ -590,26 +657,34 @@ gimp_tool_line_button_release (GimpToolWidget        *widget,
 {
   GimpToolLine        *line    = GIMP_TOOL_LINE (widget);
   GimpToolLinePrivate *private = line->private;
+  GimpToolLineGrab     grab    = private->grab;
 
-  if (release_type == GIMP_BUTTON_RELEASE_CANCEL &&
-      private->grab != GRAB_NONE)
+  private->grab = GRAB_NONE;
+
+  if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
     {
-      if (private->grab == GRAB_SELECTION &&
-          GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection))
+      if (grab != GRAB_NONE)
         {
-          gimp_tool_line_get_slider (line, private->selection)->value =
-            private->saved_slider_value;
-        }
+          if (grab == GRAB_SELECTION &&
+              GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection))
+            {
+              gimp_tool_line_get_slider (line, private->selection)->value =
+                private->saved_slider_value;
+            }
 
-      g_object_set (line,
-                    "x1", private->saved_x1,
-                    "y1", private->saved_y1,
-                    "x2", private->saved_x2,
-                    "y2", private->saved_y2,
-                    NULL);
+          g_object_set (line,
+                        "x1", private->saved_x1,
+                        "y1", private->saved_y1,
+                        "x2", private->saved_x2,
+                        "y2", private->saved_y2,
+                        NULL);
+        }
+    }
+  else if (grab == GRAB_SELECTION && private->remove_slider)
+    {
+      g_signal_emit (line, line_signals[REMOVE_SLIDER], 0,
+                     private->selection);
     }
-
-  private->grab = GRAB_NONE;
 }
 
 void
@@ -687,6 +762,35 @@ gimp_tool_line_hover (GimpToolWidget   *widget,
               private->hover = i;
             }
         }
+
+      if (private->hover == GIMP_TOOL_LINE_HANDLE_NONE)
+        {
+          gboolean constrain;
+          gdouble  value;
+          gdouble  dist;
+
+          constrain = (state & gimp_get_constrain_behavior_mask ()) != 0;
+
+          value = gimp_tool_line_project_point (line,
+                                                private->mouse_x,
+                                                private->mouse_y,
+                                                constrain,
+                                                &dist);
+
+          if (value >= 0.0 && value <= 1.0 && dist <= LINE_VICINITY)
+            {
+              gboolean can_add;
+
+              g_signal_emit (line, line_signals[CAN_ADD_SLIDER], 0,
+                             value, &can_add);
+
+              if (can_add)
+                {
+                  private->hover            = HOVER_NEW_SLIDER;
+                  private->new_slider_value = value;
+                }
+            }
+        }
     }
 
   gimp_tool_line_update_handles (line);
@@ -812,6 +916,15 @@ gimp_tool_line_key_press (GimpToolWidget *widget,
             }
         }
       return TRUE;
+
+    case GDK_KEY_BackSpace:
+    case GDK_KEY_Delete:
+      if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection))
+        {
+          g_signal_emit (line, line_signals[REMOVE_SLIDER], 0,
+                         private->selection);
+        }
+      return TRUE;
     }
 
   return GIMP_TOOL_WIDGET_CLASS (parent_class)->key_press (widget, kevent);
@@ -853,7 +966,22 @@ gimp_tool_line_get_cursor (GimpToolWidget     *widget,
   else if (private->grab  == GRAB_SELECTION ||
            private->hover >  GIMP_TOOL_LINE_HANDLE_NONE)
     {
-      *modifier = GIMP_CURSOR_MODIFIER_MOVE;
+      if (private->grab == GRAB_SELECTION                      &&
+          GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection) &&
+          private->remove_slider)
+        {
+          *modifier = GIMP_CURSOR_MODIFIER_MINUS;
+        }
+      else
+        {
+          *modifier = GIMP_CURSOR_MODIFIER_MOVE;
+        }
+
+      return TRUE;
+    }
+  else if (private->hover == HOVER_NEW_SLIDER)
+    {
+      *modifier = GIMP_CURSOR_MODIFIER_PLUS;
 
       return TRUE;
     }
@@ -898,6 +1026,58 @@ gimp_tool_line_get_handle (GimpToolLine *line,
     }
 }
 
+static gdouble
+gimp_tool_line_project_point (GimpToolLine *line,
+                              gdouble       x,
+                              gdouble       y,
+                              gboolean      constrain,
+                              gdouble      *dist)
+{
+  GimpToolLinePrivate *private = line->private;
+  gdouble              length_sqr;
+  gdouble              value   = 0.0;
+
+  length_sqr = SQR (private->x2 - private->x1) +
+               SQR (private->y2 - private->y1);
+
+  /* don't calculate the projection for 0-length lines, since we'll just get
+   * NaN.
+   */
+  if (length_sqr > 0.0)
+    {
+      value  = (private->x2 - private->x1) * (x - private->x1) +
+               (private->y2 - private->y1) * (y - private->y1);
+      value /= length_sqr;
+
+      if (dist)
+        {
+          gdouble px;
+          gdouble py;
+
+          px = private->x1 + (private->x2 - private->x1) * value;
+          py = private->y1 + (private->y2 - private->y1) * value;
+
+          *dist = gimp_canvas_item_transform_distance (private->line,
+                                                       x,  y,
+                                                       px, py);
+        }
+
+      if (constrain)
+        value = RINT (12.0 * value) / 12.0;
+    }
+  else
+    {
+      if (dist)
+        {
+          *dist = gimp_canvas_item_transform_distance (private->line,
+                                                       x,           y,
+                                                       private->x1, private->y1);
+        }
+    }
+
+  return value;
+}
+
 static gboolean
 gimp_tool_line_selection_motion (GimpToolLine *line,
                                  gboolean      constrain)
@@ -940,40 +1120,49 @@ gimp_tool_line_selection_motion (GimpToolLine *line,
 
     default:
       {
-        gdouble length_sqr;
+        GimpDisplayShell     *shell;
+        GimpControllerSlider *slider;
+        gdouble               value;
+        gdouble               dist;
 
-        length_sqr = SQR (private->x2 - private->x1) +
-                     SQR (private->y2 - private->y1);
+        shell = gimp_tool_widget_get_shell (GIMP_TOOL_WIDGET (line));
 
-        /* don't change slider values of 0-length lines, since we'll just get
-         * NaN.
-         */
-        if (length_sqr > 0.0)
-          {
-            GimpControllerSlider *slider;
-            gdouble               value;
+        slider = gimp_tool_line_get_slider (line, private->selection);
 
-            slider = gimp_tool_line_get_slider (line, private->selection);
+        /* project the cursor position onto the line */
+        value = gimp_tool_line_project_point (line, x, y, constrain, &dist);
 
-            /* project the cursor position onto the line */
-            value  = (private->x2 - private->x1) * (x - private->x1) +
-                     (private->y2 - private->y1) * (y - private->y1);
-            value /= length_sqr;
+        value = CLAMP (value, slider->min, slider->max);
+        value = CLAMP (value, 0.0,         1.0);
 
-            if (constrain)
-              value = RINT (12.0 * value) / 12.0;
+        value = fabs (value); /* avoid negative zero */
 
-            value = CLAMP (value, slider->min, slider->max);
-            value = CLAMP (value, 0.0, 1.0);
+        slider->value = value;
 
-            value = fabs (value); /* avoid negative zero */
+        g_object_set (line,
+                      "sliders", private->sliders,
+                      NULL);
 
-            slider->value = value;
+        /* slider tearing */
+        private->remove_slider = dist > SLIDER_TEAR_DISTANCE;
 
-            g_object_set (line,
-                          "sliders", private->sliders,
-                          NULL);
-          }
+        /* eek! */
+        {
+          GimpCursorType     cursor;
+          GimpToolCursorType tool_cursor;
+          GimpCursorModifier modifier;
+
+          cursor      = shell->current_cursor;
+          tool_cursor = shell->tool_cursor;
+          modifier    = GIMP_CURSOR_MODIFIER_NONE;
+
+          gimp_tool_line_get_cursor (GIMP_TOOL_WIDGET (line), NULL, 0,
+                                     &cursor, &tool_cursor, &modifier);
+
+          gimp_display_shell_set_cursor (shell, cursor, tool_cursor, modifier);
+        }
+
+        gimp_tool_line_update_handles (line);
 
         return TRUE;
       }
@@ -984,22 +1173,53 @@ static void
 gimp_tool_line_update_handles (GimpToolLine *line)
 {
   GimpToolLinePrivate *private = line->private;
-  GimpCanvasItem      *handle;
   gboolean             visible;
 
-  handle = gimp_tool_line_get_handle (line, private->hover);
-
-  visible = handle && private->grab == GRAB_NONE;
+  visible = (private->grab == GRAB_NONE                    &&
+             private->hover != GIMP_TOOL_LINE_HANDLE_NONE) ||
+            (private->grab == GRAB_SELECTION               &&
+             private->remove_slider);
 
   if (visible)
     {
-      gdouble x;
-      gdouble y;
-      gint    width;
-      gint    height;
+      gdouble  x;
+      gdouble  y;
+      gint     width;
+      gint     height;
+      gboolean dashed;
+
+      if (private->grab == GRAB_NONE && private->hover == HOVER_NEW_SLIDER)
+        {
+          /* new slider */
+          x = private->x1 +
+              (private->x2 - private->x1) * private->new_slider_value;
+          y = private->y1 +
+              (private->y2 - private->y1) * private->new_slider_value;
+
+          width = height = SLIDER_HANDLE_SIZE;
 
-      gimp_canvas_handle_get_position (handle, &x,     &y);
-      gimp_canvas_handle_get_size     (handle, &width, &height);
+          dashed = TRUE;
+        }
+      else
+        {
+          GimpCanvasItem *handle;
+
+          if (private->grab == GRAB_SELECTION)
+            {
+              /* tear slider */
+              handle = gimp_tool_line_get_handle (line, private->selection);
+              dashed = TRUE;
+            }
+          else
+            {
+              /* hover over handle */
+              handle = gimp_tool_line_get_handle (line, private->hover);
+              dashed = FALSE;
+            }
+
+          gimp_canvas_handle_get_position (handle, &x,     &y);
+          gimp_canvas_handle_get_size     (handle, &width, &height);
+        }
 
       width   = MAX (width,  SLIDER_HANDLE_SIZE);
       height  = MAX (height, SLIDER_HANDLE_SIZE);
@@ -1009,6 +1229,11 @@ gimp_tool_line_update_handles (GimpToolLine *line)
 
       gimp_canvas_handle_set_position (private->handle_circle, x,     y);
       gimp_canvas_handle_set_size     (private->handle_circle, width, height);
+
+      g_object_set (private->handle_circle,
+                    "type", dashed ? GIMP_HANDLE_DASHED_CIRCLE :
+                                     GIMP_HANDLE_CIRCLE,
+                    NULL);
     }
 
   gimp_canvas_item_set_visible (private->handle_circle, visible);
diff --git a/app/display/gimptoolline.h b/app/display/gimptoolline.h
index e4f21a4..d9a557c 100644
--- a/app/display/gimptoolline.h
+++ b/app/display/gimptoolline.h
@@ -60,7 +60,13 @@ struct _GimpToolLineClass
   GimpToolWidgetClass  parent_class;
 
   /*  signals  */
-  void (* selection_changed) (GimpToolLine *line);
+  gboolean (* can_add_slider)    (GimpToolLine *line,
+                                  gdouble       value);
+  gint     (* add_slider)        (GimpToolLine *line,
+                                  gdouble       value);
+  void     (* remove_slider)     (GimpToolLine *line,
+                                  gint          slider);
+  void     (* selection_changed) (GimpToolLine *line);
 };
 
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]