[gimp/gimp-2-10] app: add option to show tool-button menu on hover



commit 04d17b78294effcf8bd2cedf0de53eb99d2c09b9
Author: Ell <ell_se yahoo com>
Date:   Thu Mar 26 12:19:27 2020 +0200

    app: add option to show tool-button menu on hover
    
    Add a "Menu mode" option to the toolbox preferences, which controls
    the menu behavior for tool-group buttons, and can be one of "Show
    on click" (current behavior), "Show on hover" (show the menu when
    hovering over the button), and "Show on hover in single column"
    (behaves like "Show on hover" when the toolbox has a single column,
    and "Show on click" otherwise) -- the latter is the default.
    
    Note that "Show on hover" requires the ability to remove the menu
    grab, which doesn't seem to work in GTK3, so this change is
    restricted to 2.10 for now.

 app/config/config-enums.c        |  31 ++++++
 app/config/config-enums.h        |  12 ++
 app/config/gimpguiconfig.c       |  15 +++
 app/config/gimpguiconfig.h       |  85 ++++++++-------
 app/config/gimprc-blurbs.h       |   3 +
 app/dialogs/preferences-dialog.c |  31 +++++-
 app/widgets/gimptoolbutton.c     | 230 ++++++++++++++++++++++++++++++++++++---
 app/widgets/gimptoolbutton.h     |  20 ++--
 app/widgets/gimptoolpalette.c    | 150 +++++++++++++++++++------
 9 files changed, 478 insertions(+), 99 deletions(-)
---
diff --git a/app/config/config-enums.c b/app/config/config-enums.c
index 70da0248a1..7c8c68e182 100644
--- a/app/config/config-enums.c
+++ b/app/config/config-enums.c
@@ -301,6 +301,37 @@ gimp_space_bar_action_get_type (void)
   return type;
 }
 
+GType
+gimp_tool_group_menu_mode_get_type (void)
+{
+  static const GEnumValue values[] =
+  {
+    { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK, "GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK", "click" },
+    { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER, "GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER", "hover" },
+    { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN, 
"GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN", "hover-single-column" },
+    { 0, NULL, NULL }
+  };
+
+  static const GimpEnumDesc descs[] =
+  {
+    { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK, NC_("tool-group-menu-mode", "Show on click"), NULL },
+    { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER, NC_("tool-group-menu-mode", "Show on hover"), NULL },
+    { GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN, NC_("tool-group-menu-mode", "Show on hover in 
single-column"), NULL },
+    { 0, NULL, NULL }
+  };
+
+  static GType type = 0;
+
+  if (G_UNLIKELY (! type))
+    {
+      type = g_enum_register_static ("GimpToolGroupMenuMode", values);
+      gimp_type_set_translation_context (type, "tool-group-menu-mode");
+      gimp_enum_set_value_descriptions (type, descs);
+    }
+
+  return type;
+}
+
 GType
 gimp_window_hint_get_type (void)
 {
diff --git a/app/config/config-enums.h b/app/config/config-enums.h
index 3881dbabc7..345ef75893 100644
--- a/app/config/config-enums.h
+++ b/app/config/config-enums.h
@@ -135,6 +135,18 @@ typedef enum
 } GimpSpaceBarAction;
 
 
+#define GIMP_TYPE_TOOL_GROUP_MENU_MODE (gimp_tool_group_menu_mode_get_type ())
+
+GType gimp_tool_group_menu_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+  GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK,               /*< desc="Show on click"                   >*/
+  GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER,               /*< desc="Show on hover"                   >*/
+  GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN  /*< desc="Show on hover in single-column"  >*/
+} GimpToolGroupMenuMode;
+
+
 #define GIMP_TYPE_WINDOW_HINT (gimp_window_hint_get_type ())
 
 GType gimp_window_hint_get_type (void) G_GNUC_CONST;
diff --git a/app/config/gimpguiconfig.c b/app/config/gimpguiconfig.c
index 81ebf52305..79ff839be2 100644
--- a/app/config/gimpguiconfig.c
+++ b/app/config/gimpguiconfig.c
@@ -77,6 +77,7 @@ enum
   PROP_TOOLBOX_IMAGE_AREA,
   PROP_TOOLBOX_WILBER,
   PROP_TOOLBOX_GROUPS,
+  PROP_TOOLBOX_GROUP_MENU_MODE,
   PROP_THEME_PATH,
   PROP_THEME,
   PROP_ICON_THEME_PATH,
@@ -331,6 +332,14 @@ gimp_gui_config_class_init (GimpGuiConfigClass *klass)
                             TRUE,
                             GIMP_PARAM_STATIC_STRINGS);
 
+  GIMP_CONFIG_PROP_ENUM (object_class, PROP_TOOLBOX_GROUP_MENU_MODE,
+                         "toolbox-group-menu-mode",
+                         "Toolbox group menu mode",
+                         TOOLBOX_GROUP_MENU_MODE_BLURB,
+                         GIMP_TYPE_TOOL_GROUP_MENU_MODE,
+                         GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN,
+                         GIMP_PARAM_STATIC_STRINGS);
+
   path = gimp_config_build_data_path ("themes");
   GIMP_CONFIG_PROP_PATH (object_class, PROP_THEME_PATH,
                          "theme-path",
@@ -680,6 +689,9 @@ gimp_gui_config_set_property (GObject      *object,
     case PROP_TOOLBOX_GROUPS:
       gui_config->toolbox_groups = g_value_get_boolean (value);
       break;
+    case PROP_TOOLBOX_GROUP_MENU_MODE:
+      gui_config->toolbox_group_menu_mode = g_value_get_enum (value);
+      break;
      case PROP_THEME_PATH:
       g_free (gui_config->theme_path);
       gui_config->theme_path = g_value_dup_string (value);
@@ -877,6 +889,9 @@ gimp_gui_config_get_property (GObject    *object,
     case PROP_TOOLBOX_GROUPS:
       g_value_set_boolean (value, gui_config->toolbox_groups);
       break;
+    case PROP_TOOLBOX_GROUP_MENU_MODE:
+      g_value_set_enum (value, gui_config->toolbox_group_menu_mode);
+      break;
     case PROP_THEME_PATH:
       g_value_set_string (value, gui_config->theme_path);
       break;
diff --git a/app/config/gimpguiconfig.h b/app/config/gimpguiconfig.h
index 5f15495981..b2a25ee50a 100644
--- a/app/config/gimpguiconfig.h
+++ b/app/config/gimpguiconfig.h
@@ -39,48 +39,49 @@ typedef struct _GimpGuiConfigClass GimpGuiConfigClass;
 
 struct _GimpGuiConfig
 {
-  GimpDisplayConfig    parent_instance;
-
-  gboolean             edit_non_visible;
-  gboolean             move_tool_changes_active;
-  gint                 filter_tool_max_recent;
-  gboolean             filter_tool_use_last_settings;
-  gboolean             filter_tool_show_color_options;
-  gboolean             trust_dirty_flag;
-  gboolean             save_device_status;
-  gboolean             devices_share_tool;
-  gboolean             save_session_info;
-  gboolean             restore_session;
-  gboolean             restore_monitor;
-  gboolean             save_tool_options;
-  gboolean             compact_sliders;
-  gboolean             show_tooltips;
-  gboolean             tearoff_menus;
-  gboolean             can_change_accels;
-  gboolean             save_accels;
-  gboolean             restore_accels;
-  gint                 last_opened_size;
-  guint64              max_new_image_size;
-  gboolean             toolbox_color_area;
-  gboolean             toolbox_foo_area;
-  gboolean             toolbox_image_area;
-  gboolean             toolbox_wilber;
-  gboolean             toolbox_groups;
-  gchar               *theme_path;
-  gchar               *theme;
-  gchar               *icon_theme_path;
-  gchar               *icon_theme;
-  GimpIconSize         icon_size;
-  gboolean             use_help;
-  gboolean             show_help_button;
-  gchar               *help_locales;
-  GimpHelpBrowserType  help_browser;
-  gboolean             user_manual_online;
-  gchar               *user_manual_online_uri;
-  gboolean             search_show_unavailable;
-  gint                 action_history_size;
-  GimpWindowHint       dock_window_hint;
-  GimpHandedness       cursor_handedness;
+  GimpDisplayConfig      parent_instance;
+
+  gboolean               edit_non_visible;
+  gboolean               move_tool_changes_active;
+  gint                   filter_tool_max_recent;
+  gboolean               filter_tool_use_last_settings;
+  gboolean               filter_tool_show_color_options;
+  gboolean               trust_dirty_flag;
+  gboolean               save_device_status;
+  gboolean               devices_share_tool;
+  gboolean               save_session_info;
+  gboolean               restore_session;
+  gboolean               restore_monitor;
+  gboolean               save_tool_options;
+  gboolean               compact_sliders;
+  gboolean               show_tooltips;
+  gboolean               tearoff_menus;
+  gboolean               can_change_accels;
+  gboolean               save_accels;
+  gboolean               restore_accels;
+  gint                   last_opened_size;
+  guint64                max_new_image_size;
+  gboolean               toolbox_color_area;
+  gboolean               toolbox_foo_area;
+  gboolean               toolbox_image_area;
+  gboolean               toolbox_wilber;
+  gboolean               toolbox_groups;
+  GimpToolGroupMenuMode  toolbox_group_menu_mode;
+  gchar                 *theme_path;
+  gchar                 *theme;
+  gchar                 *icon_theme_path;
+  gchar                 *icon_theme;
+  GimpIconSize           icon_size;
+  gboolean               use_help;
+  gboolean               show_help_button;
+  gchar                 *help_locales;
+  GimpHelpBrowserType    help_browser;
+  gboolean               user_manual_online;
+  gchar                 *user_manual_online_uri;
+  gboolean               search_show_unavailable;
+  gint                   action_history_size;
+  GimpWindowHint         dock_window_hint;
+  GimpHandedness         cursor_handedness;
 
   /* experimental playground */
   gboolean             playground_npd_tool;
diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h
index 00646d60e4..bc8c85aba8 100644
--- a/app/config/gimprc-blurbs.h
+++ b/app/config/gimprc-blurbs.h
@@ -682,6 +682,9 @@ _("Show the current foreground and background colors in the toolbox.")
 #define TOOLBOX_FOO_AREA_BLURB \
 _("Show the currently selected brush, pattern and gradient in the toolbox.")
 
+#define TOOLBOX_GROUP_MENU_MODE_BLURB \
+_("Menu mode of grouped tools.")
+
 #define TOOLBOX_GROUPS_BLURB \
 _("Use a single toolbox button for grouped tools.")
 
diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c
index 3c3fda1eba..9b8d80f706 100644
--- a/app/dialogs/preferences-dialog.c
+++ b/app/dialogs/preferences-dialog.c
@@ -2160,10 +2160,33 @@ prefs_dialog_new (Gimp       *gimp,
   gtk_box_pack_start (GTK_BOX (vbox2), separator, FALSE, FALSE, 0);
   gtk_widget_show (separator);
 
-  prefs_check_button_add_with_icon (object, "toolbox-groups",
-                                    _("Use tool _groups"),
-                                    NULL,
-                                    GTK_BOX (vbox2), size_group);
+  button = prefs_check_button_add_with_icon (object, "toolbox-groups",
+                                             _("Use tool _groups"),
+                                             NULL,
+                                             GTK_BOX (vbox2), size_group);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new (NULL);
+  gtk_misc_set_padding (GTK_MISC (label), 2, 2);
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  gtk_widget_show (label);
+
+  gtk_size_group_add_widget (size_group, label);
+
+  vbox3 = prefs_frame_new (NULL, GTK_CONTAINER (hbox), TRUE);
+  g_object_bind_property (button, "active",
+                          vbox3,  "sensitive",
+                          G_BINDING_SYNC_CREATE);
+
+  table = prefs_table_new (1, GTK_CONTAINER (vbox3));
+
+  prefs_enum_combo_box_add (object, "toolbox-group-menu-mode", 0, 0,
+                            _("_Menu mode:"),
+                            GTK_TABLE (table), 0,
+                            NULL);
 
   g_clear_object (&size_group);
 
diff --git a/app/widgets/gimptoolbutton.c b/app/widgets/gimptoolbutton.c
index 33e99fdc8f..1ff4a9698a 100644
--- a/app/widgets/gimptoolbutton.c
+++ b/app/widgets/gimptoolbutton.c
@@ -59,7 +59,8 @@ enum
 {
   PROP_0,
   PROP_TOOLBOX,
-  PROP_TOOL_ITEM
+  PROP_TOOL_ITEM,
+  PROP_SHOW_MENU_ON_HOVER
 };
 
 
@@ -67,14 +68,17 @@ struct _GimpToolButtonPrivate
 {
   GimpToolbox  *toolbox;
   GimpToolItem *tool_item;
+  gboolean      show_menu_on_hover;
 
   GtkWidget    *palette;
 
   GtkWidget    *menu;
   GHashTable   *menu_items;
+  gint          menu_idle_id;
   gint          menu_timeout_id;
   gint          menu_timeout_button;
   guint32       menu_timeout_time;
+  gint          menu_select_idle_id;
 };
 
 
@@ -98,6 +102,12 @@ static gboolean     gimp_tool_button_expose              (GtkWidget           *w
 
 static void         gimp_tool_button_toggled             (GtkToggleToolButton *toggle_tool_button);
 
+static gboolean     gimp_tool_button_enter_notify        (GtkWidget           *widget,
+                                                          GdkEventCrossing    *event,
+                                                          GimpToolButton      *tool_button);
+static gboolean     gimp_tool_button_leave_notify        (GtkWidget           *widget,
+                                                          GdkEventCrossing    *event,
+                                                          GimpToolButton      *tool_button);
 static gboolean     gimp_tool_button_button_press        (GtkWidget           *widget,
                                                           GdkEventButton      *event,
                                                           GimpToolButton      *tool_button);
@@ -130,12 +140,16 @@ static void         gimp_tool_button_icon_size_notify    (GtkToolPalette      *p
                                                           const GParamSpec    *pspec,
                                                           GimpToolButton      *tool_button);
 
+static gboolean     gimp_tool_button_menu_enter_notify   (GtkMenu             *menu,
+                                                          GdkEventCrossing    *event,
+                                                          GimpToolButton      *tool_button);
 static gboolean     gimp_tool_button_menu_leave_notify   (GtkMenu             *menu,
                                                           GdkEventCrossing    *event,
                                                           GimpToolButton      *tool_button);
 static void         gimp_tool_button_menu_deactivate     (GtkMenu             *menu,
                                                           GimpToolButton      *tool_button);
 
+static gboolean     gimp_tool_button_menu_idle           (GimpToolButton      *tool_button);
 static gboolean     gimp_tool_button_menu_timeout        (GimpToolButton      *tool_button);
 
 static void         gimp_tool_button_update              (GimpToolButton      *tool_button);
@@ -195,6 +209,12 @@ gimp_tool_button_class_init (GimpToolButtonClass *klass)
                                                         NULL, NULL,
                                                         GIMP_TYPE_TOOL_ITEM,
                                                         GIMP_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class, PROP_SHOW_MENU_ON_HOVER,
+                                   g_param_spec_boolean ("show-menu-on-hover",
+                                                         NULL, NULL,
+                                                         FALSE,
+                                                         GIMP_PARAM_READWRITE));
 }
 
 static void
@@ -222,6 +242,14 @@ gimp_tool_button_constructed (GObject *object)
   gtk_widget_add_events (gtk_bin_get_child (GTK_BIN (tool_button)),
                          GDK_SCROLL_MASK);
 
+  g_signal_connect (gtk_bin_get_child (GTK_BIN (tool_button)),
+                    "enter-notify-event",
+                    G_CALLBACK (gimp_tool_button_enter_notify),
+                    tool_button);
+  g_signal_connect (gtk_bin_get_child (GTK_BIN (tool_button)),
+                    "leave-notify-event",
+                    G_CALLBACK (gimp_tool_button_leave_notify),
+                    tool_button);
   g_signal_connect (gtk_bin_get_child (GTK_BIN (tool_button)),
                     "button-press-event",
                     G_CALLBACK (gimp_tool_button_button_press),
@@ -271,6 +299,11 @@ gimp_tool_button_set_property (GObject      *object,
       gimp_tool_button_set_tool_item (tool_button, g_value_get_object (value));
       break;
 
+    case PROP_SHOW_MENU_ON_HOVER:
+      gimp_tool_button_set_show_menu_on_hover (tool_button,
+                                               g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -295,6 +328,10 @@ gimp_tool_button_get_property (GObject    *object,
       g_value_set_object (value, tool_button->priv->tool_item);
       break;
 
+    case PROP_SHOW_MENU_ON_HOVER:
+      g_value_set_boolean (value, tool_button->priv->show_menu_on_hover);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -423,18 +460,73 @@ gimp_tool_button_toggled (GtkToggleToolButton *toggle_tool_button)
     }
 }
 
+static gboolean
+gimp_tool_button_enter_notify (GtkWidget        *widget,
+                               GdkEventCrossing *event,
+                               GimpToolButton   *tool_button)
+{
+  if (tool_button->priv->menu                            &&
+      tool_button->priv->show_menu_on_hover              &&
+      ! gtk_widget_get_visible (tool_button->priv->menu) &&
+      event->mode == GDK_CROSSING_NORMAL                 &&
+      event->state == 0)
+    {
+      if (tool_button->priv->menu_idle_id)
+        {
+          g_source_remove (tool_button->priv->menu_idle_id);
+
+          tool_button->priv->menu_idle_id = 0;
+        }
+
+      gimp_tool_button_show_menu (tool_button, 0, event->time);
+    }
+
+  return FALSE;
+}
+
+static gboolean
+gimp_tool_button_leave_notify (GtkWidget        *widget,
+                               GdkEventCrossing *event,
+                               GimpToolButton   *tool_button)
+{
+  if (tool_button->priv->menu               &&
+      tool_button->priv->show_menu_on_hover &&
+      gtk_widget_get_visible (tool_button->priv->menu))
+    {
+      if (event->mode == GDK_CROSSING_NORMAL)
+        {
+          if (! tool_button->priv->menu_idle_id)
+            {
+              tool_button->priv->menu_idle_id = g_idle_add (
+                (GSourceFunc) gimp_tool_button_menu_idle,
+                tool_button);
+            }
+        }
+      else
+        {
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
 static gboolean
 gimp_tool_button_button_press (GtkWidget      *widget,
                                GdkEventButton *event,
                                GimpToolButton *tool_button)
 {
-  if (GIMP_IS_TOOL_GROUP (tool_button->priv->tool_item))
+  if (tool_button->priv->menu)
     {
-      if (gdk_event_triggers_context_menu ((GdkEvent *) event) &&
-          gimp_tool_button_show_menu (tool_button,
-                                      event->button, event->time))
+      if (gtk_widget_get_visible (tool_button->priv->menu))
         {
-          return TRUE;
+          gtk_menu_shell_deactivate (GTK_MENU_SHELL (tool_button->priv->menu));
+        }
+      else if (gdk_event_triggers_context_menu ((GdkEvent *) event) ||
+               tool_button->priv->show_menu_on_hover)
+        {
+          return gimp_tool_button_show_menu (tool_button,
+                                             event->button, event->time);
         }
       else if (event->type == GDK_BUTTON_PRESS && event->button == 1 &&
                ! tool_button->priv->menu_timeout_id)
@@ -602,6 +694,25 @@ gimp_tool_button_icon_size_notify (GtkToolPalette   *palette,
   gimp_tool_button_reconstruct_menu (tool_button);
 }
 
+static gboolean
+gimp_tool_button_menu_enter_notify (GtkMenu          *menu,
+                                    GdkEventCrossing *event,
+                                    GimpToolButton   *tool_button)
+{
+  if (tool_button->priv->show_menu_on_hover &&
+      event->mode == GDK_CROSSING_NORMAL)
+    {
+      if (tool_button->priv->menu_idle_id)
+        {
+          g_source_remove (tool_button->priv->menu_idle_id);
+
+          tool_button->priv->menu_idle_id = 0;
+        }
+    }
+
+  return FALSE;
+}
+
 static gboolean
 gimp_tool_button_menu_leave_notify (GtkMenu          *menu,
                                     GdkEventCrossing *event,
@@ -611,6 +722,14 @@ gimp_tool_button_menu_leave_notify (GtkMenu          *menu,
       gtk_widget_get_visible (tool_button->priv->menu))
     {
       gimp_tool_button_update_menu (tool_button);
+
+      if (tool_button->priv->show_menu_on_hover &&
+          ! tool_button->priv->menu_idle_id)
+        {
+          tool_button->priv->menu_idle_id = g_idle_add (
+            (GSourceFunc) gimp_tool_button_menu_idle,
+            tool_button);
+        }
     }
 
   return FALSE;
@@ -628,9 +747,26 @@ static void
 gimp_tool_button_menu_deactivate (GtkMenu        *menu,
                                   GimpToolButton *tool_button)
 {
+  if (tool_button->priv->menu_select_idle_id)
+    {
+      g_source_remove (tool_button->priv->menu_select_idle_id);
+
+      tool_button->priv->menu_select_idle_id = 0;
+    }
+
   g_idle_add (gimp_tool_button_menu_deactivate_idle, NULL);
 }
 
+static gboolean
+gimp_tool_button_menu_idle (GimpToolButton *tool_button)
+{
+  tool_button->priv->menu_idle_id = 0;
+
+  gtk_menu_shell_deactivate (GTK_MENU_SHELL (tool_button->priv->menu));
+
+  return G_SOURCE_REMOVE;
+}
+
 static gboolean
 gimp_tool_button_menu_timeout (GimpToolButton *tool_button)
 {
@@ -664,14 +800,21 @@ gimp_tool_button_update (GimpToolButton *tool_button)
     tool_info ? gimp_viewable_get_icon_name (GIMP_VIEWABLE (tool_info)) :
                 NULL);
 
-  if (action)
+  if (! tool_button->priv->menu || ! tool_button->priv->show_menu_on_hover)
     {
-      gimp_widget_set_accel_help (GTK_WIDGET (tool_button), action);
-    }
-  else if (tool_info)
-    {
-      gimp_help_set_help_data (GTK_WIDGET (tool_button),
-                               tool_info->tooltip, tool_info->help_id);
+      if (action)
+        {
+          gimp_widget_set_accel_help (GTK_WIDGET (tool_button), action);
+        }
+      else if (tool_info)
+        {
+          gimp_help_set_help_data (GTK_WIDGET (tool_button),
+                                   tool_info->tooltip, tool_info->help_id);
+        }
+      else
+        {
+          gimp_help_set_help_data (GTK_WIDGET (tool_button), NULL, NULL);
+        }
     }
   else
     {
@@ -823,6 +966,9 @@ gimp_tool_button_reconstruct_menu (GimpToolButton *tool_button)
       gtk_menu_attach_to_widget (GTK_MENU (tool_button->priv->menu),
                                  GTK_WIDGET (tool_button), NULL);
 
+      g_signal_connect (tool_button->priv->menu, "enter-notify-event",
+                        G_CALLBACK (gimp_tool_button_menu_enter_notify),
+                        tool_button);
       g_signal_connect (tool_button->priv->menu, "leave-notify-event",
                         G_CALLBACK (gimp_tool_button_menu_leave_notify),
                         tool_button);
@@ -857,12 +1003,26 @@ gimp_tool_button_destroy_menu (GimpToolButton *tool_button)
 
       g_clear_pointer (&tool_button->priv->menu_items, g_hash_table_unref);
 
+      if (tool_button->priv->menu_idle_id)
+        {
+          g_source_remove (tool_button->priv->menu_idle_id);
+
+          tool_button->priv->menu_idle_id = 0;
+        }
+
       if (tool_button->priv->menu_timeout_id)
         {
           g_source_remove (tool_button->priv->menu_timeout_id);
 
           tool_button->priv->menu_timeout_id = 0;
         }
+
+      if (tool_button->priv->menu_select_idle_id)
+        {
+          g_source_remove (tool_button->priv->menu_select_idle_id);
+
+          tool_button->priv->menu_select_idle_id = 0;
+        }
     }
 }
 
@@ -877,6 +1037,16 @@ gimp_tool_button_show_menu_position_func (GtkMenu        *menu,
                              menu, GTK_POS_RIGHT, x, y);
 }
 
+static gboolean
+gimp_tool_button_show_menu_select_idle (GimpToolButton *tool_button)
+{
+  tool_button->priv->menu_select_idle_id = 0;
+
+  gimp_tool_button_update_menu (tool_button);
+
+  return G_SOURCE_REMOVE;
+}
+
 static gboolean
 gimp_tool_button_show_menu (GimpToolButton *tool_button,
                             gint            button,
@@ -895,7 +1065,15 @@ gimp_tool_button_show_menu (GimpToolButton *tool_button,
     tool_button,
     button, activate_time);
 
-  gimp_tool_button_update_menu (tool_button);
+  if (tool_button->priv->show_menu_on_hover)
+    gtk_grab_remove (tool_button->priv->menu);
+
+  if (! tool_button->priv->menu_select_idle_id)
+    {
+      tool_button->priv->menu_select_idle_id = g_idle_add (
+        (GSourceFunc) gimp_tool_button_show_menu_select_idle,
+        tool_button);
+    }
 
   return TRUE;
 }
@@ -1050,3 +1228,27 @@ gimp_tool_button_get_tool_info (GimpToolButton *tool_button)
 
   return NULL;
 }
+
+void
+gimp_tool_button_set_show_menu_on_hover (GimpToolButton *tool_button,
+                                         gboolean        show_menu_on_hover)
+{
+  g_return_if_fail (GIMP_IS_TOOL_BUTTON (tool_button));
+
+  if (show_menu_on_hover != tool_button->priv->show_menu_on_hover)
+    {
+      tool_button->priv->show_menu_on_hover = show_menu_on_hover;
+
+      gimp_tool_button_update (tool_button);
+
+      g_object_notify (G_OBJECT (tool_button), "show-menu-on-hover");
+    }
+}
+
+gboolean
+gimp_tool_button_get_show_menu_on_hover (GimpToolButton *tool_button)
+{
+  g_return_val_if_fail (GIMP_IS_TOOL_BUTTON (tool_button), FALSE);
+
+  return tool_button->priv->show_menu_on_hover;
+}
diff --git a/app/widgets/gimptoolbutton.h b/app/widgets/gimptoolbutton.h
index b53d48a27a..ae010e8c21 100644
--- a/app/widgets/gimptoolbutton.h
+++ b/app/widgets/gimptoolbutton.h
@@ -46,18 +46,22 @@ struct _GimpToolButtonClass
 };
 
 
-GType          gimp_tool_button_get_type      (void) G_GNUC_CONST;
+GType          gimp_tool_button_get_type               (void) G_GNUC_CONST;
 
-GtkToolItem  * gimp_tool_button_new           (GimpToolbox    *toolbox,
-                                               GimpToolItem   *tool_item);
+GtkToolItem  * gimp_tool_button_new                    (GimpToolbox    *toolbox,
+                                                        GimpToolItem   *tool_item);
 
-GimpToolbox  * gimp_tool_button_get_toolbox   (GimpToolButton *tool_button);
+GimpToolbox  * gimp_tool_button_get_toolbox            (GimpToolButton *tool_button);
 
-void           gimp_tool_button_set_tool_item (GimpToolButton *tool_button,
-                                               GimpToolItem   *tool_item);
-GimpToolItem * gimp_tool_button_get_tool_item (GimpToolButton *tool_button);
+void           gimp_tool_button_set_tool_item          (GimpToolButton *tool_button,
+                                                        GimpToolItem   *tool_item);
+GimpToolItem * gimp_tool_button_get_tool_item          (GimpToolButton *tool_button);
 
-GimpToolInfo * gimp_tool_button_get_tool_info (GimpToolButton *tool_button);
+GimpToolInfo * gimp_tool_button_get_tool_info          (GimpToolButton *tool_button);
+
+void           gimp_tool_button_set_show_menu_on_hover (GimpToolButton *tool_button,
+                                                        gboolean        show_menu_on_hover);
+gboolean       gimp_tool_button_get_show_menu_on_hover (GimpToolButton *tool_button);
 
 
 #endif /* __GIMP_TOOL_BUTTON_H__ */
diff --git a/app/widgets/gimptoolpalette.c b/app/widgets/gimptoolpalette.c
index 1f96d6d7a3..b5a27a6552 100644
--- a/app/widgets/gimptoolpalette.c
+++ b/app/widgets/gimptoolpalette.c
@@ -64,30 +64,36 @@ struct _GimpToolPalettePrivate
 #define GET_PRIVATE(p) ((GimpToolPalettePrivate *) gimp_tool_palette_get_instance_private ((GimpToolPalette 
*) (p)))
 
 
-static void   gimp_tool_palette_finalize            (GObject        *object);
-
-static void   gimp_tool_palette_size_allocate       (GtkWidget       *widget,
-                                                     GtkAllocation   *allocation);
-static void   gimp_tool_palette_style_set           (GtkWidget       *widget,
-                                                     GtkStyle        *previous_style);
-
-static void   gimp_tool_palette_tool_add            (GimpContainer   *container,
-                                                     GimpToolItem    *tool_item,
-                                                     GimpToolPalette *palette);
-static void   gimp_tool_palette_tool_remove         (GimpContainer   *container,
-                                                     GimpToolItem    *tool_item,
-                                                     GimpToolPalette *palette);
-static void   gimp_tool_palette_tool_reorder        (GimpContainer   *container,
-                                                     GimpToolItem    *tool_item,
-                                                     gint             index,
-                                                     GimpToolPalette *palette);
-
-static void   gimp_tool_palette_config_size_changed (GimpGuiConfig   *config,
-                                                     GimpToolPalette *palette);
-
-static void   gimp_tool_palette_add_button          (GimpToolPalette *palette,
-                                                     GimpToolItem    *tool_item,
-                                                     gint             index);
+static void       gimp_tool_palette_finalize                  (GObject          *object);
+
+static void       gimp_tool_palette_size_allocate             (GtkWidget        *widget,
+                                                               GtkAllocation    *allocation);
+static void       gimp_tool_palette_style_set                 (GtkWidget        *widget,
+                                                               GtkStyle         *previous_style);
+
+static void       gimp_tool_palette_tool_add                  (GimpContainer    *container,
+                                                               GimpToolItem     *tool_item,
+                                                               GimpToolPalette  *palette);
+static void       gimp_tool_palette_tool_remove               (GimpContainer    *container,
+                                                               GimpToolItem     *tool_item,
+                                                               GimpToolPalette  *palette);
+static void       gimp_tool_palette_tool_reorder              (GimpContainer    *container,
+                                                               GimpToolItem     *tool_item,
+                                                               gint              index,
+                                                               GimpToolPalette  *palette);
+
+static void       gimp_tool_palette_config_menu_mode_notify   (GimpGuiConfig    *config,
+                                                               const GParamSpec *pspec,
+                                                               GimpToolPalette  *palette);
+static void       gimp_tool_palette_config_size_changed       (GimpGuiConfig    *config,
+                                                               GimpToolPalette  *palette);
+
+static void       gimp_tool_palette_add_button                (GimpToolPalette *palette,
+                                                               GimpToolItem    *tool_item,
+                                                               gint             index);
+
+static gboolean   gimp_tool_palette_get_show_menu_on_hover    (GimpToolPalette *palette);
+static void       gimp_tool_palette_update_show_menu_on_hover (GimpToolPalette *palette);
 
 
 G_DEFINE_TYPE_WITH_PRIVATE (GimpToolPalette, gimp_tool_palette,
@@ -144,9 +150,16 @@ gimp_tool_palette_finalize (GObject *object)
       GimpContext *context = gimp_toolbox_get_context (private->toolbox);
 
       if (context)
-        g_signal_handlers_disconnect_by_func (context->gimp->config,
-                                              G_CALLBACK (gimp_tool_palette_config_size_changed),
-                                              object);
+        {
+          g_signal_handlers_disconnect_by_func (
+            context->gimp->config,
+            G_CALLBACK (gimp_tool_palette_config_menu_mode_notify),
+            object);
+          g_signal_handlers_disconnect_by_func (
+            context->gimp->config,
+            G_CALLBACK (gimp_tool_palette_config_size_changed),
+            object);
+        }
     }
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -156,13 +169,14 @@ static void
 gimp_tool_palette_size_allocate (GtkWidget     *widget,
                                  GtkAllocation *allocation)
 {
+  GimpToolPalette        *palette = GIMP_TOOL_PALETTE (widget);
   GimpToolPalettePrivate *private = GET_PRIVATE (widget);
   gint                    button_width;
   gint                    button_height;
 
   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
 
-  if (gimp_tool_palette_get_button_size (GIMP_TOOL_PALETTE (widget),
+  if (gimp_tool_palette_get_button_size (palette,
                                          &button_width, &button_height))
     {
       GimpToolItem   *tool_item;
@@ -195,6 +209,8 @@ gimp_tool_palette_size_allocate (GtkWidget     *widget,
 
           gtk_widget_set_size_request (widget, -1,
                                        tool_rows * button_height);
+
+          gimp_tool_palette_update_show_menu_on_hover (palette);
         }
     }
 }
@@ -260,10 +276,17 @@ gimp_tool_palette_set_toolbox (GimpToolPalette *palette,
   if (private->toolbox)
     {
       context = gimp_toolbox_get_context (private->toolbox);
-      g_signal_handlers_disconnect_by_func (GIMP_GUI_CONFIG (context->gimp->config),
-                                            G_CALLBACK (gimp_tool_palette_config_size_changed),
-                                            palette);
+
+      g_signal_handlers_disconnect_by_func (
+        GIMP_GUI_CONFIG (context->gimp->config),
+        G_CALLBACK (gimp_tool_palette_config_menu_mode_notify),
+        palette);
+      g_signal_handlers_disconnect_by_func (
+        GIMP_GUI_CONFIG (context->gimp->config),
+        G_CALLBACK (gimp_tool_palette_config_size_changed),
+        palette);
     }
+
   private->toolbox = toolbox;
 
   context = gimp_toolbox_get_context (toolbox);
@@ -293,6 +316,12 @@ gimp_tool_palette_set_toolbox (GimpToolPalette *palette,
                            G_CALLBACK (gimp_tool_palette_tool_reorder),
                            palette, 0);
 
+  g_signal_connect (GIMP_GUI_CONFIG (context->gimp->config),
+                    "notify::toolbox-group-menu-mode",
+                    G_CALLBACK (gimp_tool_palette_config_menu_mode_notify),
+                    palette);
+  gimp_tool_palette_update_show_menu_on_hover (palette);
+
   /* Update the toolbox icon size on config change. */
   g_signal_connect (GIMP_GUI_CONFIG (context->gimp->config),
                     "size-changed",
@@ -385,6 +414,14 @@ gimp_tool_palette_tool_reorder (GimpContainer   *container,
     }
 }
 
+static void
+gimp_tool_palette_config_menu_mode_notify (GimpGuiConfig    *config,
+                                           const GParamSpec *pspec,
+                                           GimpToolPalette  *palette)
+{
+  gimp_tool_palette_update_show_menu_on_hover (palette);
+}
+
 static void
 gimp_tool_palette_config_size_changed (GimpGuiConfig   *config,
                                        GimpToolPalette *palette)
@@ -433,6 +470,9 @@ gimp_tool_palette_add_button (GimpToolPalette *palette,
   tool_button = gimp_tool_button_new (private->toolbox, tool_item);
   gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (private->group),
                               tool_button, index);
+  gimp_tool_button_set_show_menu_on_hover (
+    GIMP_TOOL_BUTTON (tool_button),
+    gimp_tool_palette_get_show_menu_on_hover (palette));
   gtk_widget_show (GTK_WIDGET (tool_button));
 
   g_object_bind_property (tool_item,   "shown",
@@ -452,3 +492,51 @@ gimp_tool_palette_add_button (GimpToolPalette *palette,
 
   g_hash_table_insert (private->buttons, tool_item, tool_button);
 }
+
+static gboolean
+gimp_tool_palette_get_show_menu_on_hover (GimpToolPalette *palette)
+{
+  GimpToolPalettePrivate *private = GET_PRIVATE (palette);
+
+  if (private->toolbox)
+    {
+      GimpContext *context = gimp_toolbox_get_context (private->toolbox);
+
+      if (context)
+        {
+          GimpGuiConfig *config = GIMP_GUI_CONFIG (context->gimp->config);
+
+          switch (config->toolbox_group_menu_mode)
+            {
+            case GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_CLICK:
+              return FALSE;
+
+            case GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER:
+              return TRUE;
+
+            case GIMP_TOOL_GROUP_MENU_MODE_SHOW_ON_HOVER_SINGLE_COLUMN:
+              return private->tool_columns == 1;
+            }
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+gimp_tool_palette_update_show_menu_on_hover (GimpToolPalette *palette)
+{
+  GimpToolPalettePrivate *private = GET_PRIVATE (palette);
+  GHashTableIter          iter;
+  GimpToolButton         *tool_button;
+  gboolean                show_menu_on_hover;
+
+  show_menu_on_hover = gimp_tool_palette_get_show_menu_on_hover (palette);
+
+  g_hash_table_iter_init (&iter, private->buttons);
+
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &tool_button))
+    {
+      gimp_tool_button_set_show_menu_on_hover (tool_button, show_menu_on_hover);
+    }
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]