app: add persistent handle selection to GimpToolLine

En route to on-canvas gradient editing, add support for persistent
handle selection to GimpToolLine (a handle being either an endpoint
or a slider).  Handles are selected through clicking, however,
unlike before, the selection persists after the mouse is released.
A new "selection" property specifies the currently-selected handle
(who knows, maybe in the future we'll add multi-selection), and a
new "selection-changed" signal is emitted when the selection changes.

The visual feedback has been changed to better suit the new behavior,
and the behaviors yet to be added:  The selected handle is marked
using highlighting; the highlighting doesn't change while hovering
over other handles.  Only the hit-test circle is used as hover
indication, however, we use a fixed-size circle, and only show the
circle for the currently hovered-over handle -- no more trippy
expanding circles :)

A few minor changes along the way:

  - The selected handle is now the (first) one that's closest to the
    cursor, instead of the first one to pass hit-testing.

  - We don't move the selectd handle upon button-press, only upon
    motion, so that handles can be selected without moving them.

  - Show a MOVE cursor modifier when hovering over a handle.
This commit is contained in:
Ell 2017-07-19 14:48:09 -04:00
parent 042650e18c
commit d4cb1623ea
2 changed files with 386 additions and 268 deletions

View File

@ -35,6 +35,7 @@
#include "widgets/gimpwidgets-utils.h"
#include "gimpcanvasgroup.h"
#include "gimpcanvashandle.h"
#include "gimpcanvasline.h"
#include "gimpdisplayshell.h"
@ -43,22 +44,22 @@
#include "gimp-intl.h"
#define SHOW_LINE TRUE
#define ENDPOINT_GRIP_HANDLE_TYPE GIMP_HANDLE_CROSS
#define ENDPOINT_GRIP_HANDLE_SIZE GIMP_CANVAS_HANDLE_SIZE_CROSS
#define SLIDER_GRIP_HANDLE_TYPE GIMP_HANDLE_FILLED_DIAMOND
#define SLIDER_GRIP_HANDLE_SIZE (ENDPOINT_GRIP_HANDLE_SIZE * 2 / 3)
#define SHOW_LINE TRUE
#define GRAB_LINE_MASK GDK_MOD1_MASK
#define ENDPOINT_HANDLE_TYPE GIMP_HANDLE_CROSS
#define ENDPOINT_HANDLE_SIZE GIMP_CANVAS_HANDLE_SIZE_CROSS
#define SLIDER_HANDLE_TYPE GIMP_HANDLE_FILLED_DIAMOND
#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)
typedef enum
{
/* POINT_NONE evaluates to FALSE */
POINT_NONE = 0,
POINT_START,
POINT_END,
POINT_BOTH,
POINT_SLIDER
} GimpToolLinePoint;
GRAB_NONE,
GRAB_SELECTION,
GRAB_LINE
} GimpToolLineGrab;
enum
{
@ -68,9 +69,16 @@ enum
PROP_X2,
PROP_Y2,
PROP_SLIDERS,
PROP_SELECTION,
PROP_STATUS_TITLE,
};
enum
{
SELECTION_CHANGED,
LAST_SIGNAL
};
struct _GimpToolLinePrivate
{
gdouble x1;
@ -78,6 +86,7 @@ struct _GimpToolLinePrivate
gdouble x2;
gdouble y2;
GArray *sliders;
gint selection;
gchar *status_title;
gdouble saved_x1;
@ -88,17 +97,15 @@ struct _GimpToolLinePrivate
gdouble mouse_x;
gdouble mouse_y;
GimpToolLinePoint point;
gint slider_index;
gboolean point_grabbed;
gint hover;
GimpToolLineGrab grab;
GimpCanvasItem *line;
GimpCanvasItem *start_handle_circle;
GimpCanvasItem *start_handle_grip;
GimpCanvasItem *end_handle_circle;
GimpCanvasItem *end_handle_grip;
GArray *slider_handle_circles;
GArray *slider_handle_grips;
GimpCanvasItem *start_handle;
GimpCanvasItem *end_handle;
GimpCanvasItem *slider_group;
GArray *slider_handles;
GimpCanvasItem *handle_circle;
};
@ -148,7 +155,12 @@ static gboolean gimp_tool_line_get_cursor (GimpToolWidget *widget,
static GimpControllerSlider *
gimp_tool_line_get_slider (GimpToolLine *line,
gint slider);
static gboolean gimp_tool_line_point_motion (GimpToolLine *line,
static GimpCanvasItem *
gimp_tool_line_get_handle (GimpToolLine *line,
gint handle);
static gboolean
gimp_tool_line_selection_motion (GimpToolLine *line,
gboolean constrain);
static void gimp_tool_line_update_handles (GimpToolLine *line);
@ -157,11 +169,18 @@ static void gimp_tool_line_update_status (GimpToolLine *line,
GdkModifierType state,
gboolean proximity);
static gboolean gimp_tool_line_handle_hit (GimpCanvasItem *handle,
gdouble x,
gdouble y,
gdouble *min_dist);
G_DEFINE_TYPE (GimpToolLine, gimp_tool_line, GIMP_TYPE_TOOL_WIDGET)
#define parent_class gimp_tool_line_parent_class
static guint line_signals[LAST_SIGNAL] = { 0, };
static void
gimp_tool_line_class_init (GimpToolLineClass *klass)
@ -182,6 +201,15 @@ 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[SELECTION_CHANGED] =
g_signal_new ("selection-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpToolLineClass, selection_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_object_class_install_property (object_class, PROP_X1,
g_param_spec_double ("x1", NULL, NULL,
-GIMP_MAX_IMAGE_SIZE,
@ -215,6 +243,14 @@ gimp_tool_line_class_init (GimpToolLineClass *klass)
G_TYPE_ARRAY,
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_SELECTION,
g_param_spec_int ("selection", NULL, NULL,
GIMP_TOOL_LINE_HANDLE_NONE,
G_MAXINT,
GIMP_TOOL_LINE_HANDLE_NONE,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class, PROP_STATUS_TITLE,
g_param_spec_string ("status-title",
NULL, NULL,
@ -236,10 +272,12 @@ gimp_tool_line_init (GimpToolLine *line)
private->sliders = g_array_new (FALSE, FALSE, sizeof (GimpControllerSlider));
private->slider_handle_circles = g_array_new (FALSE, TRUE,
sizeof (GimpCanvasItem *));
private->slider_handle_grips = g_array_new (FALSE, TRUE,
sizeof (GimpCanvasItem *));
private->selection = GIMP_TOOL_LINE_HANDLE_NONE;
private->hover = GIMP_TOOL_LINE_HANDLE_NONE;
private->grab = GRAB_NONE;
private->slider_handles = g_array_new (FALSE, TRUE,
sizeof (GimpCanvasItem *));
}
static void
@ -259,40 +297,36 @@ gimp_tool_line_constructed (GObject *object)
gimp_canvas_item_set_visible (private->line, SHOW_LINE);
private->start_handle_circle =
private->start_handle =
gimp_tool_widget_add_handle (widget,
ENDPOINT_HANDLE_TYPE,
private->x1,
private->y1,
ENDPOINT_HANDLE_SIZE,
ENDPOINT_HANDLE_SIZE,
GIMP_HANDLE_ANCHOR_CENTER);
private->end_handle =
gimp_tool_widget_add_handle (widget,
ENDPOINT_HANDLE_TYPE,
private->x2,
private->y2,
ENDPOINT_HANDLE_SIZE,
ENDPOINT_HANDLE_SIZE,
GIMP_HANDLE_ANCHOR_CENTER);
private->slider_group =
gimp_canvas_group_new (gimp_tool_widget_get_shell (widget));
gimp_tool_widget_add_item (widget, private->slider_group);
g_object_unref (private->slider_group);
private->handle_circle =
gimp_tool_widget_add_handle (widget,
GIMP_HANDLE_CIRCLE,
private->x1,
private->y1,
2 * ENDPOINT_GRIP_HANDLE_SIZE,
2 * ENDPOINT_GRIP_HANDLE_SIZE,
GIMP_HANDLE_ANCHOR_CENTER);
private->start_handle_grip =
gimp_tool_widget_add_handle (widget,
ENDPOINT_GRIP_HANDLE_TYPE,
private->x1,
private->y1,
ENDPOINT_GRIP_HANDLE_SIZE,
ENDPOINT_GRIP_HANDLE_SIZE,
GIMP_HANDLE_ANCHOR_CENTER);
private->end_handle_circle =
gimp_tool_widget_add_handle (widget,
GIMP_HANDLE_CIRCLE,
private->x2,
private->y2,
2 * ENDPOINT_GRIP_HANDLE_SIZE,
2 * ENDPOINT_GRIP_HANDLE_SIZE,
GIMP_HANDLE_ANCHOR_CENTER);
private->end_handle_grip =
gimp_tool_widget_add_handle (widget,
ENDPOINT_GRIP_HANDLE_TYPE,
private->x2,
private->y2,
ENDPOINT_GRIP_HANDLE_SIZE,
ENDPOINT_GRIP_HANDLE_SIZE,
ENDPOINT_HANDLE_SIZE * HANDLE_CIRCLE_SCALE,
ENDPOINT_HANDLE_SIZE * HANDLE_CIRCLE_SCALE,
GIMP_HANDLE_ANCHOR_CENTER);
gimp_tool_line_changed (widget);
@ -306,8 +340,7 @@ gimp_tool_line_finalize (GObject *object)
g_clear_pointer (&private->sliders, g_array_unref);
g_clear_pointer (&private->status_title, g_free);
g_clear_pointer (&private->slider_handle_circles, g_array_unref);
g_clear_pointer (&private->slider_handle_grips, g_array_unref);
g_clear_pointer (&private->slider_handles, g_array_unref);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -337,10 +370,41 @@ gimp_tool_line_set_property (GObject *object,
break;
case PROP_SLIDERS:
g_return_if_fail (g_value_get_boxed (value) != NULL);
{
GArray *sliders = g_value_dup_boxed (value);
g_array_unref (private->sliders);
private->sliders = g_value_dup_boxed (value);
g_return_if_fail (sliders != NULL);
if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection) &&
sliders->len != private->sliders->len)
{
gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE);
}
if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->hover))
private->hover = GIMP_TOOL_LINE_HANDLE_NONE;
g_array_unref (private->sliders);
private->sliders = sliders;
}
break;
case PROP_SELECTION:
{
gint selection = g_value_get_int (value);
g_return_if_fail (selection < (gint) private->sliders->len);
if (selection != private->selection)
{
private->selection = selection;
if (private->grab == GRAB_SELECTION)
private->grab = GRAB_NONE;
g_signal_emit (line, line_signals[SELECTION_CHANGED], 0);
}
}
break;
case PROP_STATUS_TITLE:
@ -384,6 +448,10 @@ gimp_tool_line_get_property (GObject *object,
g_value_set_boxed (value, private->sliders);
break;
case PROP_SELECTION:
g_value_set_int (value, private->selection);
break;
case PROP_STATUS_TITLE:
g_value_set_string (value, private->status_title);
break;
@ -407,73 +475,54 @@ gimp_tool_line_changed (GimpToolWidget *widget)
private->x2,
private->y2);
gimp_canvas_handle_set_position (private->start_handle_circle,
private->x1,
private->y1);
gimp_canvas_handle_set_position (private->start_handle_grip,
gimp_canvas_handle_set_position (private->start_handle,
private->x1,
private->y1);
gimp_canvas_handle_set_position (private->end_handle_circle,
private->x2,
private->y2);
gimp_canvas_handle_set_position (private->end_handle_grip,
gimp_canvas_handle_set_position (private->end_handle,
private->x2,
private->y2);
/* remove excessive slider handles */
for (i = private->sliders->len; i < private->slider_handle_circles->len; i++)
for (i = private->sliders->len; i < private->slider_handles->len; i++)
{
gimp_tool_widget_remove_item (widget,
g_array_index (private->slider_handle_circles,
GimpCanvasItem *, i));
gimp_tool_widget_remove_item (widget,
g_array_index (private->slider_handle_grips,
GimpCanvasItem *, i));
gimp_canvas_group_remove_item (GIMP_CANVAS_GROUP (private->slider_group),
gimp_tool_line_get_handle (line, i));
}
g_array_set_size (private->slider_handle_circles, private->sliders->len);
g_array_set_size (private->slider_handle_grips, private->sliders->len);
g_array_set_size (private->slider_handles, private->sliders->len);
for (i = 0; i < private->sliders->len; i++)
{
gdouble value;
gdouble x;
gdouble y;
GimpCanvasItem **circle;
GimpCanvasItem **grip;
GimpCanvasItem **handle;
value = gimp_tool_line_get_slider (line, i)->value;
x = private->x1 + (private->x2 - private->x1) * value;
y = private->y1 + (private->y2 - private->y1) * value;
circle = &g_array_index (private->slider_handle_circles,
GimpCanvasItem *, i);
grip = &g_array_index (private->slider_handle_grips,
GimpCanvasItem *, i);
handle = &g_array_index (private->slider_handles, GimpCanvasItem *, i);
if (*circle)
if (*handle)
{
gimp_canvas_handle_set_position (*circle, x, y);
gimp_canvas_handle_set_position (*grip, x, y);
gimp_canvas_handle_set_position (*handle, x, y);
}
else
{
*circle = gimp_tool_widget_add_handle (widget,
GIMP_HANDLE_CIRCLE,
x,
y,
2 * SLIDER_GRIP_HANDLE_SIZE,
2 * SLIDER_GRIP_HANDLE_SIZE,
GIMP_HANDLE_ANCHOR_CENTER);
*grip = gimp_tool_widget_add_handle (widget,
SLIDER_GRIP_HANDLE_TYPE,
x,
y,
SLIDER_GRIP_HANDLE_SIZE,
SLIDER_GRIP_HANDLE_SIZE,
GIMP_HANDLE_ANCHOR_CENTER);
*handle = gimp_canvas_handle_new (gimp_tool_widget_get_shell (widget),
SLIDER_HANDLE_TYPE,
GIMP_HANDLE_ANCHOR_CENTER,
x,
y,
SLIDER_HANDLE_SIZE,
SLIDER_HANDLE_SIZE);
gimp_canvas_group_add_item (GIMP_CANVAS_GROUP (private->slider_group),
*handle);
g_object_unref (*handle);
}
}
@ -491,30 +540,41 @@ gimp_tool_line_button_press (GimpToolWidget *widget,
GimpToolLine *line = GIMP_TOOL_LINE (widget);
GimpToolLinePrivate *private = line->private;
if (private->point != POINT_NONE)
private->grab = GRAB_NONE;
private->saved_x1 = private->x1;
private->saved_y1 = private->y1;
private->saved_x2 = private->x2;
private->saved_y2 = private->y2;
if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->hover))
{
private->saved_x1 = private->x1;
private->saved_y1 = private->y1;
private->saved_x2 = private->x2;
private->saved_y2 = private->y2;
if (private->point == POINT_SLIDER)
{
private->saved_slider_value =
gimp_tool_line_get_slider (line, private->slider_index)->value;
}
private->point_grabbed = TRUE;
gimp_tool_line_point_motion (line,
state & gimp_get_constrain_behavior_mask ());
return private->point;
private->saved_slider_value =
gimp_tool_line_get_slider (line, private->hover)->value;
}
if (private->hover != GIMP_TOOL_LINE_HANDLE_NONE)
{
gimp_tool_line_set_selection (line, private->hover);
private->grab = GRAB_SELECTION;
}
else if (state & GRAB_LINE_MASK)
{
private->grab = GRAB_LINE;
}
if (grab == GRAB_NONE)
{
private->hover = GIMP_TOOL_LINE_HANDLE_NONE;
gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE);
}
gimp_tool_line_update_handles (line);
gimp_tool_line_update_status (line, state, TRUE);
return 0;
return private->grab != GRAB_NONE;
}
void
@ -527,11 +587,13 @@ gimp_tool_line_button_release (GimpToolWidget *widget,
GimpToolLine *line = GIMP_TOOL_LINE (widget);
GimpToolLinePrivate *private = line->private;
if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
if (release_type == GIMP_BUTTON_RELEASE_CANCEL &&
private->grab != GRAB_NONE)
{
if (private->point == POINT_SLIDER)
if (private->grab == GRAB_SELECTION &&
GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection))
{
gimp_tool_line_get_slider (line, private->slider_index)->value =
gimp_tool_line_get_slider (line, private->selection)->value =
private->saved_slider_value;
}
@ -543,7 +605,7 @@ gimp_tool_line_button_release (GimpToolWidget *widget,
NULL);
}
private->point_grabbed = FALSE;
private->grab = GRAB_NONE;
}
void
@ -560,7 +622,7 @@ gimp_tool_line_motion (GimpToolWidget *widget,
private->mouse_x = coords->x;
private->mouse_y = coords->y;
if (private->point == POINT_BOTH)
if (private->grab == GRAB_LINE)
{
g_object_set (line,
"x1", private->x1 + diff_x,
@ -573,7 +635,7 @@ gimp_tool_line_motion (GimpToolWidget *widget,
{
gboolean constrain = (state & gimp_get_constrain_behavior_mask ()) != 0;
gimp_tool_line_point_motion (line, constrain);
gimp_tool_line_selection_motion (line, constrain);
}
gimp_tool_line_update_status (line, state, TRUE);
@ -592,51 +654,38 @@ gimp_tool_line_hover (GimpToolWidget *widget,
private->mouse_x = coords->x;
private->mouse_y = coords->y;
gimp_tool_line_update_handles (line);
private->hover = GIMP_TOOL_LINE_HANDLE_NONE;
private->point = POINT_NONE;
if (state & GDK_MOD1_MASK)
if (! (state & GRAB_LINE_MASK))
{
private->point = POINT_BOTH;
}
else
{
/* give sliders precedence over the endpoints, since they're smaller */
for (i = private->sliders->len - 1; i >= 0; i--)
/* find the closest handle to the cursor */
gdouble min_dist = G_MAXDOUBLE;
gint first_handle = private->sliders->len - 1;
/* skip the sliders if the two endpoints are the same, in particular so
* that if the line is created during a button-press event (as in the
* blend tool), the end endpoint is dragged, instead of a slider.
*/
if (private->x1 == private->x2 && private->y1 == private->y2)
first_handle = -1;
for (i = first_handle; i > GIMP_TOOL_LINE_HANDLE_NONE; i--)
{
GimpCanvasItem *circle;
GimpCanvasItem *handle;
circle = g_array_index (private->slider_handle_circles,
GimpCanvasItem *, i);
handle = gimp_tool_line_get_handle (line, i);
if (gimp_canvas_item_hit (circle, private->mouse_x, private->mouse_y))
{
private->point = POINT_SLIDER;
private->slider_index = i;
break;
}
}
if (private->point == POINT_NONE)
{
if (gimp_canvas_item_hit (private->end_handle_circle,
private->mouse_x,
private->mouse_y))
{
private->point = POINT_END;
}
else if (gimp_canvas_item_hit (private->start_handle_circle,
if (gimp_tool_line_handle_hit (handle,
private->mouse_x,
private->mouse_y))
private->mouse_y,
&min_dist))
{
private->point = POINT_START;
private->hover = i;
}
}
}
gimp_tool_line_update_hilight (line);
gimp_tool_line_update_handles (line);
gimp_tool_line_update_status (line, state, proximity);
}
@ -650,7 +699,7 @@ gimp_tool_line_motion_modifier (GimpToolWidget *widget,
if (key == gimp_get_constrain_behavior_mask ())
{
gimp_tool_line_point_motion (line, press);
gimp_tool_line_selection_motion (line, press);
gimp_tool_line_update_status (line, state, TRUE);
}
@ -667,7 +716,14 @@ gimp_tool_line_get_cursor (GimpToolWidget *widget,
GimpToolLine *line = GIMP_TOOL_LINE (widget);
GimpToolLinePrivate *private = line->private;
if (private->point == POINT_BOTH)
if (private->grab ==GRAB_LINE || (state & GRAB_LINE_MASK))
{
*modifier = GIMP_CURSOR_MODIFIER_MOVE;
return TRUE;
}
else if (private->grab == GRAB_SELECTION ||
private->hover > GIMP_TOOL_LINE_HANDLE_NONE)
{
*modifier = GIMP_CURSOR_MODIFIER_MOVE;
@ -688,17 +744,49 @@ gimp_tool_line_get_slider (GimpToolLine *line,
return &g_array_index (private->sliders, GimpControllerSlider, slider);
}
static GimpCanvasItem *
gimp_tool_line_get_handle (GimpToolLine *line,
gint handle)
{
GimpToolLinePrivate *private = line->private;
switch (handle)
{
case GIMP_TOOL_LINE_HANDLE_NONE:
return NULL;
case GIMP_TOOL_LINE_HANDLE_START:
return private->start_handle;
case GIMP_TOOL_LINE_HANDLE_END:
return private->end_handle;
default:
g_assert (handle >= 0 &&
handle < (gint) private->slider_handles->len);
return g_array_index (private->slider_handles,
GimpCanvasItem *, handle);
}
}
static gboolean
gimp_tool_line_point_motion (GimpToolLine *line,
gboolean constrain)
gimp_tool_line_selection_motion (GimpToolLine *line,
gboolean constrain)
{
GimpToolLinePrivate *private = line->private;
gdouble x = private->mouse_x;
gdouble y = private->mouse_y;
switch (private->point)
if (private->grab != GRAB_SELECTION)
return FALSE;
switch (private->selection)
{
case POINT_START:
case GIMP_TOOL_LINE_HANDLE_NONE:
g_assert_not_reached ();
case GIMP_TOOL_LINE_HANDLE_START:
if (constrain)
gimp_constrain_line (private->x2, private->y2,
&x, &y,
@ -710,7 +798,7 @@ gimp_tool_line_point_motion (GimpToolLine *line,
NULL);
return TRUE;
case POINT_END:
case GIMP_TOOL_LINE_HANDLE_END:
if (constrain)
gimp_constrain_line (private->x1, private->y1,
&x, &y,
@ -722,7 +810,7 @@ gimp_tool_line_point_motion (GimpToolLine *line,
NULL);
return TRUE;
case POINT_SLIDER:
default:
{
gdouble length_sqr;
@ -737,7 +825,7 @@ gimp_tool_line_point_motion (GimpToolLine *line,
GimpControllerSlider *slider;
gdouble value;
slider = gimp_tool_line_get_slider (line, private->slider_index);
slider = gimp_tool_line_get_slider (line, private->selection);
/* project the cursor position onto the line */
value = (private->x2 - private->x1) * (x - private->x1) +
@ -761,82 +849,41 @@ gimp_tool_line_point_motion (GimpToolLine *line,
return TRUE;
}
default:
break;
}
return FALSE;
}
static void
gimp_tool_line_update_handles (GimpToolLine *line)
{
GimpToolLinePrivate *private = line->private;
gboolean start_visible, end_visible;
gint start_diameter, end_diameter;
gint i;
GimpCanvasItem *handle;
gboolean visible;
/* Calculate handle visibility */
if (private->point_grabbed)
{
start_visible = FALSE;
end_visible = FALSE;
}
else
{
start_diameter = gimp_canvas_handle_calc_size (private->start_handle_circle,
private->mouse_x,
private->mouse_y,
0,
2 * ENDPOINT_GRIP_HANDLE_SIZE);
start_visible = start_diameter > 2;
handle = gimp_tool_line_get_handle (line, private->hover);
end_diameter = gimp_canvas_handle_calc_size (private->end_handle_circle,
private->mouse_x,
private->mouse_y,
0,
2 * ENDPOINT_GRIP_HANDLE_SIZE);
end_visible = end_diameter > 2;
visible = handle && private->grab == GRAB_NONE;
if (visible)
{
gdouble x;
gdouble y;
gint width;
gint height;
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);
width *= HANDLE_CIRCLE_SCALE;
height *= HANDLE_CIRCLE_SCALE;
gimp_canvas_handle_set_position (private->handle_circle, x, y);
gimp_canvas_handle_set_size (private->handle_circle, width, height);
}
gimp_canvas_item_set_visible (private->start_handle_circle, start_visible);
gimp_canvas_item_set_visible (private->end_handle_circle, end_visible);
if (start_visible)
gimp_canvas_handle_set_size (private->start_handle_circle,
start_diameter, start_diameter);
if (end_visible)
gimp_canvas_handle_set_size (private->end_handle_circle,
end_diameter, end_diameter);
for (i = 0; i < private->sliders->len; i++)
{
GimpCanvasItem *circle;
gboolean visible;
gint diameter;
circle = g_array_index (private->slider_handle_circles,
GimpCanvasItem *, i);
if (private->point_grabbed)
visible = FALSE;
else
{
diameter = gimp_canvas_handle_calc_size (circle,
private->mouse_x,
private->mouse_y,
0,
2 * SLIDER_GRIP_HANDLE_SIZE);
visible = diameter > 2;
}
gimp_canvas_item_set_visible (circle, visible);
if (visible)
gimp_canvas_handle_set_size (circle, diameter, diameter);
}
gimp_canvas_item_set_visible (private->handle_circle, visible);
}
static void
@ -845,32 +892,15 @@ gimp_tool_line_update_hilight (GimpToolLine *line)
GimpToolLinePrivate *private = line->private;
gint i;
gimp_canvas_item_set_highlight (private->start_handle_circle,
private->point == POINT_START);
gimp_canvas_item_set_highlight (private->start_handle_grip,
private->point == POINT_START);
gimp_canvas_item_set_highlight (private->end_handle_circle,
private->point == POINT_END);
gimp_canvas_item_set_highlight (private->end_handle_grip,
private->point == POINT_END);
for (i = 0; i < private->sliders->len; i++)
for (i = GIMP_TOOL_LINE_HANDLE_NONE + 1;
i < (gint) private->sliders->len;
i++)
{
GimpCanvasItem *circle;
GimpCanvasItem *grip;
gboolean highlight;
GimpCanvasItem *handle;
circle = g_array_index (private->slider_handle_circles,
GimpCanvasItem *, i);
grip = g_array_index (private->slider_handle_grips,
GimpCanvasItem *, i);
handle = gimp_tool_line_get_handle (line, i);
highlight =
(private->point == POINT_SLIDER && private->slider_index == i);
gimp_canvas_item_set_highlight (circle, highlight);
gimp_canvas_item_set_highlight (grip, highlight);
gimp_canvas_item_set_highlight (handle, i == private->selection);
}
}
@ -907,6 +937,40 @@ gimp_tool_line_update_status (GimpToolLine *line,
}
}
static gboolean
gimp_tool_line_handle_hit (GimpCanvasItem *handle,
gdouble x,
gdouble y,
gdouble *min_dist)
{
gdouble handle_x;
gdouble handle_y;
gint handle_width;
gint handle_height;
gint radius;
gdouble dist;
gimp_canvas_handle_get_position (handle, &handle_x, &handle_y);
gimp_canvas_handle_get_size (handle, &handle_width, &handle_height);
radius = ((gint) (handle_width * HANDLE_CIRCLE_SCALE)) / 2;
radius = MAX (radius, LINE_VICINITY);
dist = gimp_canvas_item_transform_distance (handle,
x, y, handle_x, handle_y);
if (dist <= radius && dist < *min_dist)
{
*min_dist = dist;
return TRUE;
}
else
{
return FALSE;
}
}
/* public functions */
@ -940,6 +1004,12 @@ gimp_tool_line_set_sliders (GimpToolLine *line,
private = line->private;
if (GIMP_TOOL_LINE_HANDLE_IS_SLIDER (private->selection) &&
private->sliders->len != n_sliders)
{
gimp_tool_line_set_selection (line, GIMP_TOOL_LINE_HANDLE_NONE);
}
g_array_set_size (private->sliders, n_sliders);
memcpy (private->sliders->data, sliders,
@ -964,3 +1034,33 @@ gimp_tool_line_get_sliders (GimpToolLine *line,
return (const GimpControllerSlider *) private->sliders->data;
}
void
gimp_tool_line_set_selection (GimpToolLine *line,
gint handle)
{
GimpToolLinePrivate *private;
g_return_if_fail (GIMP_IS_TOOL_LINE (line));
private = line->private;
g_return_if_fail (handle >= GIMP_TOOL_LINE_HANDLE_NONE &&
handle < (gint) private->sliders->len);
g_object_set (line,
"selection", handle,
NULL);
}
gint
gimp_tool_line_get_selection (GimpToolLine *line)
{
GimpToolLinePrivate *private;
g_return_val_if_fail (GIMP_IS_TOOL_LINE (line), GIMP_TOOL_LINE_HANDLE_NONE);
private = line->private;
return private->selection;
}

View File

@ -25,6 +25,17 @@
#include "gimptoolwidget.h"
/* in the context of GimpToolLine, "handle" is a collective term for either an
* endpoint or a slider. a handle value may be either a (nonnegative) slider
* index, or one of the values below:
*/
#define GIMP_TOOL_LINE_HANDLE_NONE (-3)
#define GIMP_TOOL_LINE_HANDLE_START (-2)
#define GIMP_TOOL_LINE_HANDLE_END (-1)
#define GIMP_TOOL_LINE_HANDLE_IS_SLIDER(handle) ((handle) >= 0)
#define GIMP_TYPE_TOOL_LINE (gimp_tool_line_get_type ())
#define GIMP_TOOL_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_LINE, GimpToolLine))
#define GIMP_TOOL_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_LINE, GimpToolLineClass))
@ -47,22 +58,29 @@ struct _GimpToolLine
struct _GimpToolLineClass
{
GimpToolWidgetClass parent_class;
/* signals */
void (* selection_changed) (GimpToolLine *line);
};
GType gimp_tool_line_get_type (void) G_GNUC_CONST;
GType gimp_tool_line_get_type (void) G_GNUC_CONST;
GimpToolWidget * gimp_tool_line_new (GimpDisplayShell *shell,
gdouble x1,
gdouble y1,
gdouble x2,
gdouble y2);
GimpToolWidget * gimp_tool_line_new (GimpDisplayShell *shell,
gdouble x1,
gdouble y1,
gdouble x2,
gdouble y2);
void gimp_tool_line_set_sliders (GimpToolLine *line,
const GimpControllerSlider *sliders,
gint n_sliders);
const GimpControllerSlider * gimp_tool_line_get_sliders (GimpToolLine *line,
gint *n_sliders);
void gimp_tool_line_set_sliders (GimpToolLine *line,
const GimpControllerSlider *sliders,
gint n_sliders);
const GimpControllerSlider * gimp_tool_line_get_sliders (GimpToolLine *line,
gint *n_sliders);
void gimp_tool_line_set_selection (GimpToolLine *line,
gint handle);
gint gimp_tool_line_get_selection (GimpToolLine *line);
#endif /* __GIMP_TOOL_LINE_H__ */