app: add a "visible" property and API to GimpCanvasItem

and use it to set visibility of guides and sample points, so this
reression is fixed.

- return NULL extents for invisible items and never draw them
- do nothing for invisible items in more places, like the group

While hacking this, it turned out that it was a braino to collect an
item's region before and after a change in
dispatch_properties_changed(), so a new update had to be devised:

- add a "change count" and new API begin_change()/end_change()
- in begin_change(), remember the item's extents before the change
- in end_change(), combine old and current extents and emit "update"
- add some protected function to emit "update", and to figure if
  it makes sense at all to emit "update" on an item.
This commit is contained in:
Michael Natterer
2010-10-01 20:41:27 +02:00
parent f0d01f611c
commit 10111126ae
5 changed files with 186 additions and 70 deletions

View File

@ -230,7 +230,8 @@ gimp_canvas_group_child_update (GimpCanvasItem *item,
GdkRegion *region, GdkRegion *region,
GimpCanvasGroup *group) GimpCanvasGroup *group)
{ {
g_signal_emit_by_name (group, "update", region); if (_gimp_canvas_item_needs_update (GIMP_CANVAS_ITEM (group)))
_gimp_canvas_item_update (GIMP_CANVAS_ITEM (group), region);
} }
@ -251,7 +252,6 @@ gimp_canvas_group_add_item (GimpCanvasGroup *group,
GimpCanvasItem *item) GimpCanvasItem *item)
{ {
GimpCanvasGroupPrivate *private; GimpCanvasGroupPrivate *private;
GdkRegion *region;
g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); g_return_if_fail (GIMP_IS_CANVAS_GROUP (group));
g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); g_return_if_fail (GIMP_IS_CANVAS_ITEM (item));
@ -267,12 +267,15 @@ gimp_canvas_group_add_item (GimpCanvasGroup *group,
private->items = g_list_append (private->items, g_object_ref (item)); private->items = g_list_append (private->items, g_object_ref (item));
region = gimp_canvas_item_get_extents (item); if (_gimp_canvas_item_needs_update (GIMP_CANVAS_ITEM (group)))
if (region)
{ {
g_signal_emit_by_name (group, "update", region); GdkRegion *region = gimp_canvas_item_get_extents (item);
gdk_region_destroy (region);
if (region)
{
_gimp_canvas_item_update (GIMP_CANVAS_ITEM (group), region);
gdk_region_destroy (region);
}
} }
g_signal_connect (item, "update", g_signal_connect (item, "update",
@ -285,7 +288,6 @@ gimp_canvas_group_remove_item (GimpCanvasGroup *group,
GimpCanvasItem *item) GimpCanvasItem *item)
{ {
GimpCanvasGroupPrivate *private; GimpCanvasGroupPrivate *private;
GdkRegion *region;
g_return_if_fail (GIMP_IS_CANVAS_GROUP (group)); g_return_if_fail (GIMP_IS_CANVAS_GROUP (group));
g_return_if_fail (GIMP_IS_CANVAS_ITEM (item)); g_return_if_fail (GIMP_IS_CANVAS_ITEM (item));
@ -296,12 +298,15 @@ gimp_canvas_group_remove_item (GimpCanvasGroup *group,
private->items = g_list_remove (private->items, item); private->items = g_list_remove (private->items, item);
region = gimp_canvas_item_get_extents (item); if (_gimp_canvas_item_needs_update (GIMP_CANVAS_ITEM (group)))
if (region)
{ {
g_signal_emit_by_name (group, "update", region); GdkRegion *region = gimp_canvas_item_get_extents (item);
gdk_region_destroy (region);
if (region)
{
_gimp_canvas_item_update (GIMP_CANVAS_ITEM (group), region);
gdk_region_destroy (region);
}
} }
g_signal_handlers_disconnect_by_func (item, g_signal_handlers_disconnect_by_func (item,

View File

@ -38,6 +38,7 @@ enum
{ {
PROP_0, PROP_0,
PROP_SHELL, PROP_SHELL,
PROP_VISIBLE,
PROP_LINE_CAP, PROP_LINE_CAP,
PROP_HIGHLIGHT PROP_HIGHLIGHT
}; };
@ -54,10 +55,13 @@ typedef struct _GimpCanvasItemPrivate GimpCanvasItemPrivate;
struct _GimpCanvasItemPrivate struct _GimpCanvasItemPrivate
{ {
GimpDisplayShell *shell; GimpDisplayShell *shell;
gboolean visible;
cairo_line_cap_t line_cap; cairo_line_cap_t line_cap;
gboolean highlight; gboolean highlight;
gint suspend_stroking; gint suspend_stroking;
gint suspend_filling; gint suspend_filling;
gint change_count;
GdkRegion *change_region;
}; };
#define GET_PRIVATE(item) \ #define GET_PRIVATE(item) \
@ -136,6 +140,12 @@ gimp_canvas_item_class_init (GimpCanvasItemClass *klass)
GIMP_PARAM_READWRITE | GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY)); G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_VISIBLE,
g_param_spec_boolean ("visible",
NULL, NULL,
TRUE,
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_LINE_CAP, g_object_class_install_property (object_class, PROP_LINE_CAP,
g_param_spec_int ("line-cap", g_param_spec_int ("line-cap",
NULL, NULL, NULL, NULL,
@ -159,10 +169,13 @@ gimp_canvas_item_init (GimpCanvasItem *item)
GimpCanvasItemPrivate *private = GET_PRIVATE (item); GimpCanvasItemPrivate *private = GET_PRIVATE (item);
private->shell = NULL; private->shell = NULL;
private->visible = TRUE;
private->line_cap = CAIRO_LINE_CAP_ROUND; private->line_cap = CAIRO_LINE_CAP_ROUND;
private->highlight = FALSE; private->highlight = FALSE;
private->suspend_stroking = 0; private->suspend_stroking = 0;
private->suspend_filling = 0; private->suspend_filling = 0;
private->change_count = 1; /* avoid emissions during construction */
private->change_region = NULL;
} }
static void static void
@ -172,6 +185,8 @@ gimp_canvas_item_constructed (GObject *object)
g_assert (GIMP_IS_DISPLAY_SHELL (private->shell)); g_assert (GIMP_IS_DISPLAY_SHELL (private->shell));
private->change_count = 0; /* undo hack from init() */
if (G_OBJECT_CLASS (parent_class)->constructed) if (G_OBJECT_CLASS (parent_class)->constructed)
G_OBJECT_CLASS (parent_class)->constructed (object); G_OBJECT_CLASS (parent_class)->constructed (object);
} }
@ -189,6 +204,9 @@ gimp_canvas_item_set_property (GObject *object,
case PROP_SHELL: case PROP_SHELL:
private->shell = g_value_get_object (value); /* don't ref */ private->shell = g_value_get_object (value); /* don't ref */
break; break;
case PROP_VISIBLE:
private->visible = g_value_get_boolean (value);
break;
case PROP_LINE_CAP: case PROP_LINE_CAP:
private->line_cap = g_value_get_int (value); private->line_cap = g_value_get_int (value);
break; break;
@ -215,6 +233,9 @@ gimp_canvas_item_get_property (GObject *object,
case PROP_SHELL: case PROP_SHELL:
g_value_set_object (value, private->shell); g_value_set_object (value, private->shell);
break; break;
case PROP_VISIBLE:
g_value_set_boolean (value, private->visible);
break;
case PROP_LINE_CAP: case PROP_LINE_CAP:
g_value_set_int (value, private->line_cap); g_value_set_int (value, private->line_cap);
break; break;
@ -235,28 +256,13 @@ gimp_canvas_item_dispatch_properties_changed (GObject *object,
{ {
GimpCanvasItem *item = GIMP_CANVAS_ITEM (object); GimpCanvasItem *item = GIMP_CANVAS_ITEM (object);
if (g_signal_has_handler_pending (object, item_signals[UPDATE], 0, FALSE)) G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object,
n_pspecs,
pspecs);
if (_gimp_canvas_item_needs_update (item))
{ {
GdkRegion *before; GdkRegion *region = gimp_canvas_item_get_extents (item);
GdkRegion *region;
before = gimp_canvas_item_get_extents (item);
G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object,
n_pspecs,
pspecs);
region = gimp_canvas_item_get_extents (item);
if (! region)
{
region = before;
}
else if (before)
{
gdk_region_union (region, before);
gdk_region_destroy (before);
}
if (region) if (region)
{ {
@ -265,12 +271,6 @@ gimp_canvas_item_dispatch_properties_changed (GObject *object,
gdk_region_destroy (region); gdk_region_destroy (region);
} }
} }
else
{
G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object,
n_pspecs,
pspecs);
}
} }
static void static void
@ -333,11 +333,12 @@ gimp_canvas_item_draw (GimpCanvasItem *item,
private = GET_PRIVATE (item); private = GET_PRIVATE (item);
cairo_save (cr); if (private->visible)
{
GIMP_CANVAS_ITEM_GET_CLASS (item)->draw (item, private->shell, cr); cairo_save (cr);
GIMP_CANVAS_ITEM_GET_CLASS (item)->draw (item, private->shell, cr);
cairo_restore (cr); cairo_restore (cr);
}
} }
GdkRegion * GdkRegion *
@ -349,7 +350,42 @@ gimp_canvas_item_get_extents (GimpCanvasItem *item)
private = GET_PRIVATE (item); private = GET_PRIVATE (item);
return GIMP_CANVAS_ITEM_GET_CLASS (item)->get_extents (item, private->shell); if (private->visible)
return GIMP_CANVAS_ITEM_GET_CLASS (item)->get_extents (item, private->shell);
return NULL;
}
void
gimp_canvas_item_set_visible (GimpCanvasItem *item,
gboolean visible)
{
GimpCanvasItemPrivate *private;
g_return_if_fail (GIMP_IS_CANVAS_ITEM (item));
private = GET_PRIVATE (item);
if (private->visible != visible)
{
gimp_canvas_item_begin_change (item);
g_object_set (G_OBJECT (item),
"visible", visible,
NULL);
gimp_canvas_item_end_change (item);
}
}
gboolean
gimp_canvas_item_get_visible (GimpCanvasItem *item)
{
GimpCanvasItemPrivate *private;
g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE);
private = GET_PRIVATE (item);
return private->visible;
} }
void void
@ -362,14 +398,13 @@ gimp_canvas_item_set_line_cap (GimpCanvasItem *item,
private = GET_PRIVATE (item); private = GET_PRIVATE (item);
line_cap = CLAMP (line_cap,
CAIRO_LINE_CAP_BUTT,
CAIRO_LINE_CAP_SQUARE);
if (private->line_cap != line_cap) if (private->line_cap != line_cap)
{ {
private->line_cap = line_cap; gimp_canvas_item_begin_change (item);
g_object_notify (G_OBJECT (item), "line-cap"); g_object_set (G_OBJECT (item),
"line-cap", line_cap,
NULL);
gimp_canvas_item_end_change (item);
} }
} }
@ -383,12 +418,11 @@ gimp_canvas_item_set_highlight (GimpCanvasItem *item,
private = GET_PRIVATE (item); private = GET_PRIVATE (item);
highlight = highlight ? TRUE : FALSE;
if (private->highlight != highlight) if (private->highlight != highlight)
{ {
private->highlight = highlight; g_object_set (G_OBJECT (item),
g_object_notify (G_OBJECT (item), "highlight"); "highlight", highlight,
NULL);
} }
} }
@ -404,6 +438,63 @@ gimp_canvas_item_get_highlight (GimpCanvasItem *item)
return private->highlight; return private->highlight;
} }
void
gimp_canvas_item_begin_change (GimpCanvasItem *item)
{
GimpCanvasItemPrivate *private;
g_return_if_fail (GIMP_IS_CANVAS_ITEM (item));
private = GET_PRIVATE (item);
private->change_count++;
if (private->change_count == 1 &&
g_signal_has_handler_pending (item, item_signals[UPDATE], 0, FALSE))
{
private->change_region = gimp_canvas_item_get_extents (item);
}
}
void
gimp_canvas_item_end_change (GimpCanvasItem *item)
{
GimpCanvasItemPrivate *private;
g_return_if_fail (GIMP_IS_CANVAS_ITEM (item));
private = GET_PRIVATE (item);
g_return_if_fail (private->change_count > 0);
private->change_count--;
if (private->change_count == 0 &&
g_signal_has_handler_pending (item, item_signals[UPDATE], 0, FALSE))
{
GdkRegion *region = gimp_canvas_item_get_extents (item);
if (! region)
{
region = private->change_region;
}
else if (private->change_region)
{
gdk_region_union (region, private->change_region);
gdk_region_destroy (private->change_region);
}
private->change_region = NULL;
if (region)
{
g_signal_emit (item, item_signals[UPDATE], 0,
region);
gdk_region_destroy (region);
}
}
}
void void
gimp_canvas_item_suspend_stroking (GimpCanvasItem *item) gimp_canvas_item_suspend_stroking (GimpCanvasItem *item)
{ {
@ -459,6 +550,23 @@ gimp_canvas_item_resume_filling (GimpCanvasItem *item)
/* protected functions */ /* protected functions */
void
_gimp_canvas_item_update (GimpCanvasItem *item,
GdkRegion *region)
{
g_signal_emit (item, item_signals[UPDATE], 0,
region);
}
gboolean
_gimp_canvas_item_needs_update (GimpCanvasItem *item)
{
GimpCanvasItemPrivate *private = GET_PRIVATE (item);
return (private->change_count == 0 &&
g_signal_has_handler_pending (item, item_signals[UPDATE], 0, FALSE));
}
void void
_gimp_canvas_item_stroke (GimpCanvasItem *item, _gimp_canvas_item_stroke (GimpCanvasItem *item,
cairo_t *cr) cairo_t *cr)

View File

@ -70,6 +70,10 @@ void gimp_canvas_item_draw (GimpCanvasItem *item,
cairo_t *cr); cairo_t *cr);
GdkRegion * gimp_canvas_item_get_extents (GimpCanvasItem *item); GdkRegion * gimp_canvas_item_get_extents (GimpCanvasItem *item);
void gimp_canvas_item_set_visible (GimpCanvasItem *item,
gboolean visible);
gboolean gimp_canvas_item_get_visible (GimpCanvasItem *item);
void gimp_canvas_item_set_line_cap (GimpCanvasItem *item, void gimp_canvas_item_set_line_cap (GimpCanvasItem *item,
cairo_line_cap_t line_cap); cairo_line_cap_t line_cap);
@ -77,6 +81,9 @@ void gimp_canvas_item_set_highlight (GimpCanvasItem *item,
gboolean highlight); gboolean highlight);
gboolean gimp_canvas_item_get_highlight (GimpCanvasItem *item); gboolean gimp_canvas_item_get_highlight (GimpCanvasItem *item);
void gimp_canvas_item_begin_change (GimpCanvasItem *item);
void gimp_canvas_item_end_change (GimpCanvasItem *item);
void gimp_canvas_item_suspend_stroking (GimpCanvasItem *item); void gimp_canvas_item_suspend_stroking (GimpCanvasItem *item);
void gimp_canvas_item_resume_stroking (GimpCanvasItem *item); void gimp_canvas_item_resume_stroking (GimpCanvasItem *item);
@ -86,6 +93,9 @@ void gimp_canvas_item_resume_filling (GimpCanvasItem *item);
/* protected */ /* protected */
void _gimp_canvas_item_update (GimpCanvasItem *item,
GdkRegion *region);
gboolean _gimp_canvas_item_needs_update (GimpCanvasItem *item);
void _gimp_canvas_item_stroke (GimpCanvasItem *item, void _gimp_canvas_item_stroke (GimpCanvasItem *item,
cairo_t *cr); cairo_t *cr);
void _gimp_canvas_item_fill (GimpCanvasItem *item, void _gimp_canvas_item_fill (GimpCanvasItem *item,

View File

@ -40,6 +40,7 @@
#include "widgets/gimpwidgets-utils.h" #include "widgets/gimpwidgets-utils.h"
#include "gimpcanvas.h" #include "gimpcanvas.h"
#include "gimpcanvasitem.h"
#include "gimpdisplay.h" #include "gimpdisplay.h"
#include "gimpdisplayshell.h" #include "gimpdisplayshell.h"
#include "gimpdisplayshell-appearance.h" #include "gimpdisplayshell-appearance.h"
@ -295,7 +296,6 @@ gimp_display_shell_set_show_guides (GimpDisplayShell *shell,
gboolean show) gboolean show)
{ {
GimpDisplayOptions *options; GimpDisplayOptions *options;
GimpImage *image;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
@ -303,12 +303,7 @@ gimp_display_shell_set_show_guides (GimpDisplayShell *shell,
g_object_set (options, "show-guides", show, NULL); g_object_set (options, "show-guides", show, NULL);
image = gimp_display_get_image (shell->display); gimp_canvas_item_set_visible (shell->guides, show);
if (image && gimp_image_get_guides (image))
{
gimp_display_shell_expose_full (shell);
}
appearance_set_action_active (shell, "view-show-guides", show); appearance_set_action_active (shell, "view-show-guides", show);
} }
@ -357,7 +352,6 @@ gimp_display_shell_set_show_sample_points (GimpDisplayShell *shell,
gboolean show) gboolean show)
{ {
GimpDisplayOptions *options; GimpDisplayOptions *options;
GimpImage *image;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
@ -365,12 +359,7 @@ gimp_display_shell_set_show_sample_points (GimpDisplayShell *shell,
g_object_set (options, "show-sample-points", show, NULL); g_object_set (options, "show-sample-points", show, NULL);
image = gimp_display_get_image (shell->display); gimp_canvas_item_set_visible (shell->sample_points, show);
if (image && gimp_image_get_sample_points (image))
{
gimp_display_shell_expose_full (shell);
}
appearance_set_action_active (shell, "view-show-sample-points", show); appearance_set_action_active (shell, "view-show-sample-points", show);
} }

View File

@ -572,10 +572,12 @@ gimp_display_shell_guide_move_handler (GimpImage *image,
item = gimp_canvas_proxy_group_get_item (group, guide); item = gimp_canvas_proxy_group_get_item (group, guide);
gimp_canvas_item_begin_change (item);
g_object_set (item, g_object_set (item,
"orientation", gimp_guide_get_orientation (guide), "orientation", gimp_guide_get_orientation (guide),
"position", gimp_guide_get_position (guide), "position", gimp_guide_get_position (guide),
NULL); NULL);
gimp_canvas_item_end_change (item);
} }
static void static void
@ -647,10 +649,12 @@ gimp_display_shell_sample_point_move_handler (GimpImage *image,
item = gimp_canvas_proxy_group_get_item (group, sample_point); item = gimp_canvas_proxy_group_get_item (group, sample_point);
gimp_canvas_item_begin_change (item);
g_object_set (item, g_object_set (item,
"x", sample_point->x, "x", sample_point->x,
"y", sample_point->y, "y", sample_point->y,
NULL); NULL);
gimp_canvas_item_end_change (item);
} }
static void static void