Commit bac7dac4 authored by Ell's avatar Ell

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.
parent 86954037
......@@ -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
......
......@@ -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);
......
......@@ -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);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment