From 0f65d3923ee8ecdbb66abba95187ae924f3f6ab3 Mon Sep 17 00:00:00 2001 From: Jehan Date: Wed, 15 Oct 2025 14:42:28 +0200 Subject: [PATCH] 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. --- app/tools/gimptool.c | 24 ++++++---- app/tools/gimptools-utils.c | 81 +++++++++++++++++++++++----------- app/tools/gimptools-utils.h | 2 + app/widgets/gimpitemtreeview.c | 68 +++++++++++++++++----------- app/widgets/gimpitemtreeview.h | 2 + 5 files changed, 118 insertions(+), 59 deletions(-) diff --git a/app/tools/gimptool.c b/app/tools/gimptool.c index 263b79bc2a..727c851391 100644 --- a/app/tools/gimptool.c +++ b/app/tools/gimptool.c @@ -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 { diff --git a/app/tools/gimptools-utils.c b/app/tools/gimptools-utils.c index 9c337c5ebb..d0b09d6b42 100644 --- a/app/tools/gimptools-utils.c +++ b/app/tools/gimptools-utils.c @@ -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; +} diff --git a/app/tools/gimptools-utils.h b/app/tools/gimptools-utils.h index bee6f4d49f..5525a78547 100644 --- a/app/tools/gimptools-utils.h +++ b/app/tools/gimptools-utils.h @@ -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); diff --git a/app/widgets/gimpitemtreeview.c b/app/widgets/gimpitemtreeview.c index ecac03469c..9bc56181a8 100644 --- a/app/widgets/gimpitemtreeview.c +++ b/app/widgets/gimpitemtreeview.c @@ -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); +} diff --git a/app/widgets/gimpitemtreeview.h b/app/widgets/gimpitemtreeview.h index 227b30a1be..82977158b3 100644 --- a/app/widgets/gimpitemtreeview.h +++ b/app/widgets/gimpitemtreeview.h @@ -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);