app: show a menu path to advertize how to "Rasterize" and blink the selected layer.

This is the result of a UX session with Aryeom. Just showing a message
forbidding editing of non-rasterized text/link/vector layers is
problematic, because it doesn't help people understand how to unblock
their situation (if they really want to just edit directly the layer).
Additionally we are now blinking the layer.

A possible alternative could have been to pop a dialog up, with the
same message but also with a quick-action button to allow rasterize in a
click (similar to how we are popping a dialog up to revert the
rasterization when clicking on a text layer with the text tool or a
vector layer with the path tool). The problem is that even though the
need to edit directly a non-raster layer arises from time to time, most
of the time, when you use such layers, you don't intend to edit these
(unlike editing text/path with matching tools, you more often wanted to
edit the relevant data).
Therefore it is more often than not just a mistake when you try to paint
directly on such a layer. I.e. that very often, you were intending to
paint on another layer, or add a new layer above your non-raster layer.
Therefore a dialog popping up every time you made such a mistake would
be annoying and workflow-breaking. A simple error message and some
blinking leave for a fluid process.
This commit is contained in:
Jehan 2025-10-15 14:42:28 +02:00
parent 50fd97b28b
commit 0f65d3923e
5 changed files with 118 additions and 59 deletions

View file

@ -754,23 +754,29 @@ gimp_tool_button_press (GimpTool *tool,
! (state & gimp_get_extend_selection_mask ()));
if (! GIMP_IS_SOURCE_TOOL (tool) || ! constrain_only)
{
/* TRANSLATORS: this is a menu path. Make sure you use
* the same translations you used for the "Rasterize"
* action under the "Layer" menu.
*/
gchar *menu_path = _("Layer > Rasterize");
if (gimp_item_is_text_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Text layers must be rasterized "
"before they can be painted on."));
/* TRANSLATORS: the string between parentheses will be a menu path. */
gimp_tool_message (tool, display, _("Text layers must be rasterized (%s)."), menu_path);
gimp_tools_blink_item (display->gimp, GIMP_ITEM (drawable));
}
else if (gimp_item_is_link_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Link layers must be rasterized "
"before they can be painted on."));
/* TRANSLATORS: the string between parentheses will be a menu path. */
gimp_tool_message (tool, display, _("Link layers must be rasterized (%s)."), menu_path);
gimp_tools_blink_item (display->gimp, GIMP_ITEM (drawable));
}
else if (gimp_item_is_vector_layer (GIMP_ITEM (drawable)))
{
gimp_tool_message_literal (tool, display,
_("Vector layers must be rasterized "
"before they can be painted on."));
/* TRANSLATORS: the string between parentheses will be a menu path. */
gimp_tool_message (tool, display, _("Vector layers must be rasterized (%s)."), menu_path);
gimp_tools_blink_item (display->gimp, GIMP_ITEM (drawable));
}
else
{

View file

@ -39,6 +39,9 @@
#include "gimptools-utils.h"
static GimpItemTreeView * gimp_tools_get_tree_view_for (Gimp *gimp,
GimpItem *item);
/* public functions */
@ -46,39 +49,28 @@ void
gimp_tools_blink_lock_box (Gimp *gimp,
GimpItem *item)
{
GtkWidget *dockable;
GimpItemTreeView *view;
GdkMonitor *monitor;
const gchar *identifier;
g_return_if_fail (GIMP_IS_GIMP (gimp));
g_return_if_fail (GIMP_IS_ITEM (item));
if (GIMP_IS_LAYER (item))
identifier = "gimp-layer-list";
else if (GIMP_IS_CHANNEL (item))
identifier = "gimp-channel-list";
else if (GIMP_IS_PATH (item))
identifier = "gimp-path-list";
else
return;
monitor = gimp_get_monitor_at_pointer ();
dockable = gimp_window_strategy_show_dockable_dialog (
GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)),
gimp,
gimp_dialog_factory_get_singleton (),
monitor,
identifier);
if (! dockable)
return;
view = GIMP_ITEM_TREE_VIEW (gtk_bin_get_child (GTK_BIN (dockable)));
view = gimp_tools_get_tree_view_for (gimp, item);
gimp_item_tree_view_blink_lock (view, item);
}
void
gimp_tools_blink_item (Gimp *gimp,
GimpItem *item)
{
GimpItemTreeView *view;
g_return_if_fail (GIMP_IS_GIMP (gimp));
g_return_if_fail (GIMP_IS_ITEM (item));
view = gimp_tools_get_tree_view_for (gimp, item);
gimp_item_tree_view_blink_item (view, item);
}
void
gimp_tools_show_tool_options (Gimp *gimp)
{
@ -93,3 +85,42 @@ gimp_tools_show_tool_options (Gimp *gimp)
gimp_dialog_factory_get_singleton (),
monitor, "gimp-tool-options");
}
/* Private functions */
static GimpItemTreeView *
gimp_tools_get_tree_view_for (Gimp *gimp,
GimpItem *item)
{
GtkWidget *dockable;
GimpItemTreeView *view;
GdkMonitor *monitor;
const gchar *identifier;
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
if (GIMP_IS_LAYER (item))
identifier = "gimp-layer-list";
else if (GIMP_IS_CHANNEL (item))
identifier = "gimp-channel-list";
else if (GIMP_IS_PATH (item))
identifier = "gimp-path-list";
else
g_return_val_if_reached (NULL);
monitor = gimp_get_monitor_at_pointer ();
dockable = gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)),
gimp,
gimp_dialog_factory_get_singleton (),
monitor,
identifier);
if (! dockable)
return NULL;
view = GIMP_ITEM_TREE_VIEW (gtk_bin_get_child (GTK_BIN (dockable)));
return view;
}

View file

@ -20,5 +20,7 @@
void gimp_tools_blink_lock_box (Gimp *gimp,
GimpItem *item);
void gimp_tools_blink_item (Gimp *gimp,
GimpItem *item);
void gimp_tools_show_tool_options (Gimp *gimp);

View file

@ -262,6 +262,9 @@ static gboolean gimp_item_tree_view_search_clicked (GtkWidget
static gboolean gimp_item_tree_view_move_cursor (GimpItemTreeView *tree_view,
guint keyval,
GdkModifierType modifiers);
static void gimp_item_tree_view_blink_item_column (GimpItemTreeView *view,
GimpItem *item,
gint column);
G_DEFINE_TYPE_WITH_CODE (GimpItemTreeView, gimp_item_tree_view,
@ -1014,32 +1017,14 @@ void
gimp_item_tree_view_blink_lock (GimpItemTreeView *view,
GimpItem *item)
{
GtkTreeIter *iter;
GtkTreePath *path;
GdkRectangle rect;
gimp_item_tree_view_blink_item_column (view, item, 1);
}
g_return_if_fail (GIMP_IS_ITEM_TREE_VIEW (view));
g_return_if_fail (GIMP_IS_ITEM (item));
/* Find the item in the tree view. */
iter = _gimp_container_view_lookup (GIMP_CONTAINER_VIEW (view),
GIMP_VIEWABLE (item));
path = gtk_tree_model_get_path (GIMP_CONTAINER_TREE_VIEW (view)->model, iter);
/* Scroll dockable to make sure the cell is showing. */
gtk_tree_view_scroll_to_cell (GIMP_CONTAINER_TREE_VIEW (view)->view, path,
gtk_tree_view_get_column (GIMP_CONTAINER_TREE_VIEW (view)->view, 1),
FALSE, 0.0, 0.0);
/* Now blink the lock cell of the specified item. */
gtk_tree_view_get_cell_area (GIMP_CONTAINER_TREE_VIEW (view)->view, path,
gtk_tree_view_get_column (GIMP_CONTAINER_TREE_VIEW (view)->view, 1),
&rect);
gtk_tree_view_convert_bin_window_to_widget_coords (GIMP_CONTAINER_TREE_VIEW (view)->view,
rect.x, rect.y, &rect.x, &rect.y);
gimp_widget_blink_rect (GTK_WIDGET (GIMP_CONTAINER_TREE_VIEW (view)->view), &rect);
gtk_tree_path_free (path);
void
gimp_item_tree_view_blink_item (GimpItemTreeView *view,
GimpItem *item)
{
gimp_item_tree_view_blink_item_column (view, item, 3);
}
GtkWidget *
@ -2444,3 +2429,36 @@ gimp_item_tree_view_move_cursor (GimpItemTreeView *view,
return TRUE;
}
static void
gimp_item_tree_view_blink_item_column (GimpItemTreeView *view,
GimpItem *item,
gint column)
{
GtkTreeIter *iter;
GtkTreePath *path;
GdkRectangle rect;
g_return_if_fail (GIMP_IS_ITEM_TREE_VIEW (view));
g_return_if_fail (GIMP_IS_ITEM (item));
/* Find the item in the tree view. */
iter = _gimp_container_view_lookup (GIMP_CONTAINER_VIEW (view),
GIMP_VIEWABLE (item));
path = gtk_tree_model_get_path (GIMP_CONTAINER_TREE_VIEW (view)->model, iter);
/* Scroll dockable to make sure the cell is showing. */
gtk_tree_view_scroll_to_cell (GIMP_CONTAINER_TREE_VIEW (view)->view, path,
gtk_tree_view_get_column (GIMP_CONTAINER_TREE_VIEW (view)->view, column),
FALSE, 0.0, 0.0);
/* Now blink the specified column. */
gtk_tree_view_get_cell_area (GIMP_CONTAINER_TREE_VIEW (view)->view, path,
gtk_tree_view_get_column (GIMP_CONTAINER_TREE_VIEW (view)->view, column),
&rect);
gtk_tree_view_convert_bin_window_to_widget_coords (GIMP_CONTAINER_TREE_VIEW (view)->view,
rect.x, rect.y, &rect.x, &rect.y);
gimp_widget_blink_rect (GTK_WIDGET (GIMP_CONTAINER_TREE_VIEW (view)->view), &rect);
gtk_tree_path_free (path);
}

View file

@ -139,6 +139,8 @@ void gimp_item_tree_view_add_lock (GimpItemTreeView *view,
const gchar *help_id);
void gimp_item_tree_view_blink_lock (GimpItemTreeView *view,
GimpItem *item);
void gimp_item_tree_view_blink_item (GimpItemTreeView *view,
GimpItem *item);
GtkWidget * gimp_item_tree_view_get_new_button (GimpItemTreeView *view);
GtkWidget * gimp_item_tree_view_get_delete_button (GimpItemTreeView *view);