[gtk/wip/exalm/buttons] menubutton: Add a way to always show arrow with image buttons




commit 9fa7d11a764eaf3067e493ed2e0452efb3e304fe
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Mon Jun 21 15:13:16 2021 +0500

    menubutton: Add a way to always show arrow with image buttons
    
    Make sure the button still has the .image-button style class with an icon,
    also add it to the initial state with only an arrow. Add a new
    .arrow-button style class for the icon+arrow state so it's possible to
    style it.
    
    Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3501

 gtk/gtkmenubutton.c | 163 +++++++++++++++++++++++++++++++++++++++++++++-------
 gtk/gtkmenubutton.h |   6 ++
 2 files changed, 148 insertions(+), 21 deletions(-)
---
diff --git a/gtk/gtkmenubutton.c b/gtk/gtkmenubutton.c
index 16831d33b1..0822f1d759 100644
--- a/gtk/gtkmenubutton.c
+++ b/gtk/gtkmenubutton.c
@@ -66,6 +66,10 @@
  * `GtkMenuButton` has a single CSS node with name `menubutton`
  * which contains a `button` node with a `.toggle` style class.
  *
+ * If the button contains only an icon or an arrow, it will have the
+ * `.image-button` style class, if it contains both, it will have the
+ * `.arrow-button` style class.
+ *
  * Inside the toggle button content, there is an `arrow` node for
  * the indicator, which will carry one of the `.none`, `.up`, `.down`,
  * `.left` or `.right` style classes to indicate the direction that
@@ -85,6 +89,7 @@
 #include "gtkactionable.h"
 #include "gtkbuiltiniconprivate.h"
 #include "gtkintl.h"
+#include "gtkimage.h"
 #include "gtkmain.h"
 #include "gtkmenubutton.h"
 #include "gtkmenubuttonprivate.h"
@@ -115,8 +120,10 @@ struct _GtkMenuButton
   GDestroyNotify create_popup_destroy_notify;
 
   GtkWidget *label_widget;
+  GtkWidget *image_widget;
   GtkWidget *arrow_widget;
   GtkArrowType arrow_type;
+  gboolean always_show_arrow;
 
   gboolean primary;
 };
@@ -133,6 +140,7 @@ enum
   PROP_DIRECTION,
   PROP_POPOVER,
   PROP_ICON_NAME,
+  PROP_ALWAYS_SHOW_ARROW,
   PROP_LABEL,
   PROP_USE_UNDERLINE,
   PROP_HAS_FRAME,
@@ -168,6 +176,9 @@ gtk_menu_button_set_property (GObject      *object,
       case PROP_ICON_NAME:
         gtk_menu_button_set_icon_name (self, g_value_get_string (value));
         break;
+      case PROP_ALWAYS_SHOW_ARROW:
+        gtk_menu_button_set_always_show_arrow (self, g_value_get_boolean (value));
+        break;
       case PROP_LABEL:
         gtk_menu_button_set_label (self, g_value_get_string (value));
         break;
@@ -207,6 +218,9 @@ gtk_menu_button_get_property (GObject    *object,
       case PROP_ICON_NAME:
         g_value_set_string (value, gtk_menu_button_get_icon_name (GTK_MENU_BUTTON (object)));
         break;
+      case PROP_ALWAYS_SHOW_ARROW:
+        g_value_set_boolean (value, gtk_menu_button_get_always_show_arrow (self));
+        break;
       case PROP_LABEL:
         g_value_set_string (value, gtk_menu_button_get_label (GTK_MENU_BUTTON (object)));
         break;
@@ -411,6 +425,20 @@ gtk_menu_button_class_init (GtkMenuButtonClass *klass)
                            NULL,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
+  /**
+   * GtkMenuButton:always-show-arrow: (attributes org.gtk.Property.get=gtk_menu_button_get_always_show_arrow 
org.gtk.Property.set=gtk_menu_button_set_always_show_arrow)
+   *
+   * Whether to show a dropdown arrow even when using an icon.
+   *
+   * Since: 4.4
+   */
+  menu_button_props[PROP_ALWAYS_SHOW_ARROW] =
+      g_param_spec_boolean ("always-show-arrow",
+                            P_("Always Show Arrow"),
+                            P_("Whether to show a dropdown arrow even when using an icon"),
+                            FALSE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
   /**
    * GtkMenuButton:label: (attributes org.gtk.Property.get=gtk_menu_button_get_label 
org.gtk.Property.set=gtk_menu_button_set_label)
    *
@@ -504,12 +532,48 @@ set_arrow_type (GtkWidget    *arrow,
     gtk_widget_hide (arrow);
 }
 
+static void
+update_style_classes (GtkMenuButton *menu_button)
+{
+  if (menu_button->arrow_widget == gtk_button_get_child (GTK_BUTTON (menu_button->button)) ||
+      (menu_button->image_widget && !menu_button->always_show_arrow))
+    gtk_widget_add_css_class (menu_button->button, "image-button");
+  else
+    gtk_widget_remove_css_class (menu_button->button, "image-button");
+
+  if (menu_button->image_widget && menu_button->always_show_arrow)
+    gtk_widget_add_css_class (menu_button->button, "arrow-button");
+  else
+    gtk_widget_remove_css_class (menu_button->button, "arrow-button");
+}
+
+static void
+update_arrow (GtkMenuButton *menu_button)
+{
+  gboolean has_only_arrow, is_text_button;
+
+  if (menu_button->arrow_widget == NULL)
+    return;
+
+  has_only_arrow = menu_button->arrow_widget == gtk_button_get_child (GTK_BUTTON (menu_button->button));
+  is_text_button = menu_button->label_widget != NULL;
+
+  set_arrow_type (menu_button->arrow_widget,
+                  menu_button->arrow_type,
+                  has_only_arrow ||
+                  ((is_text_button || menu_button->always_show_arrow) &&
+                   (menu_button->arrow_type != GTK_ARROW_NONE)));
+
+  update_style_classes (menu_button);
+}
+
 static void
 add_arrow (GtkMenuButton *self)
 {
   GtkWidget *arrow;
 
   arrow = gtk_builtin_icon_new ("arrow");
+  gtk_widget_set_halign (arrow, GTK_ALIGN_CENTER);
   set_arrow_type (arrow, self->arrow_type, TRUE);
   gtk_button_set_child (GTK_BUTTON (self->button), arrow);
   self->arrow_widget = arrow;
@@ -524,6 +588,7 @@ gtk_menu_button_init (GtkMenuButton *self)
   gtk_widget_set_parent (self->button, GTK_WIDGET (self));
   g_signal_connect_swapped (self->button, "toggled", G_CALLBACK (gtk_menu_button_toggled), self);
   add_arrow (self);
+  update_style_classes (self);
 
   gtk_widget_set_sensitive (self->button, FALSE);
 
@@ -691,8 +756,6 @@ void
 gtk_menu_button_set_direction (GtkMenuButton *menu_button,
                                GtkArrowType   direction)
 {
-  gboolean is_image_button;
-
   g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
 
   if (menu_button->arrow_type == direction)
@@ -701,14 +764,7 @@ gtk_menu_button_set_direction (GtkMenuButton *menu_button,
   menu_button->arrow_type = direction;
   g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_DIRECTION]);
 
-  /* Is it custom content? We don't change that */
-  is_image_button = menu_button->label_widget == NULL;
-  if (is_image_button && (menu_button->arrow_widget != gtk_button_get_child (GTK_BUTTON 
(menu_button->button))))
-    return;
-
-  set_arrow_type (menu_button->arrow_widget,
-                  menu_button->arrow_type,
-                  is_image_button || (menu_button->arrow_type != GTK_ARROW_NONE));
+  update_arrow (menu_button);
   update_popover_direction (menu_button);
 }
 
@@ -841,6 +897,8 @@ void
 gtk_menu_button_set_icon_name (GtkMenuButton *menu_button,
                                const char    *icon_name)
 {
+  GtkWidget *box, *image_widget, *arrow;
+
   g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
 
   if (!g_strcmp0 (gtk_menu_button_get_icon_name (menu_button), icon_name))
@@ -851,7 +909,25 @@ gtk_menu_button_set_icon_name (GtkMenuButton *menu_button,
   if (gtk_menu_button_get_label (menu_button))
     g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_LABEL]);
 
-  gtk_button_set_icon_name (GTK_BUTTON (menu_button->button), icon_name);
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
+
+  image_widget = g_object_new (GTK_TYPE_IMAGE,
+                               "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
+                               "icon-name", icon_name,
+                               NULL);
+  menu_button->image_widget = image_widget;
+
+  arrow = gtk_builtin_icon_new ("arrow");
+  menu_button->arrow_widget = arrow;
+
+  gtk_box_append (GTK_BOX (box), image_widget);
+  gtk_box_append (GTK_BOX (box), arrow);
+  gtk_button_set_child (GTK_BUTTON (menu_button->button), box);
+
+  menu_button->label_widget = NULL;
+
+  update_arrow (menu_button);
 
   g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_ICON_NAME]);
 
@@ -871,7 +947,55 @@ gtk_menu_button_get_icon_name (GtkMenuButton *menu_button)
 {
   g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL);
 
-  return gtk_button_get_icon_name (GTK_BUTTON (menu_button->button));
+  if (menu_button->image_widget)
+    return gtk_image_get_icon_name (GTK_IMAGE (menu_button->image_widget));
+
+  return NULL;
+}
+
+/**
+ * gtk_menu_button_set_always_show_arrow: (attributes org.gtk.Method.set_property=always-show-arrow)
+ * @menu_button: a `GtkMenuButton`
+ * @always_show_arrow: hether to show a dropdown arrow even when using an icon
+ *
+ * Sets whether to show a dropdown arrow even when using an icon.
+ *
+ * Since: 4.4
+ */
+void
+gtk_menu_button_set_always_show_arrow (GtkMenuButton *menu_button,
+                                       gboolean       always_show_arrow)
+{
+  g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
+
+  always_show_arrow = !!always_show_arrow;
+
+  if (always_show_arrow == menu_button->always_show_arrow)
+    return;
+
+  menu_button->always_show_arrow = always_show_arrow;
+
+  update_arrow (menu_button);
+
+  g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_ALWAYS_SHOW_ARROW]);
+}
+
+/**
+ * gtk_menu_button_get_always_show_arrow: (attributes org.gtk.Method.get_property=always-show-arrow)
+ * @menu_button: a `GtkMenuButton`
+ *
+ * Gets whether to show a dropdown arrow even when using an icon.
+ *
+ * Returns: whether to show a dropdown arrow even when using an icon
+ *
+ * Since: 4.4
+ */
+gboolean
+gtk_menu_button_get_always_show_arrow (GtkMenuButton *menu_button)
+{
+  g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), FALSE);
+
+  return menu_button->always_show_arrow;
 }
 
 /**
@@ -908,12 +1032,15 @@ gtk_menu_button_set_label (GtkMenuButton *menu_button,
   gtk_widget_set_halign (label_widget, GTK_ALIGN_CENTER);
   arrow = gtk_builtin_icon_new ("arrow");
   menu_button->arrow_widget = arrow;
-  set_arrow_type (arrow, menu_button->arrow_type, menu_button->arrow_type != GTK_ARROW_NONE);
   gtk_box_append (GTK_BOX (box), label_widget);
   gtk_box_append (GTK_BOX (box), arrow);
   gtk_button_set_child (GTK_BUTTON (menu_button->button), box);
   menu_button->label_widget = label_widget;
 
+  menu_button->image_widget = NULL;
+
+  update_arrow (menu_button);
+
   g_object_notify_by_pspec (G_OBJECT (menu_button), menu_button_props[PROP_LABEL]);
 
   g_object_thaw_notify (G_OBJECT (menu_button));
@@ -930,16 +1057,10 @@ gtk_menu_button_set_label (GtkMenuButton *menu_button,
 const char *
 gtk_menu_button_get_label (GtkMenuButton *menu_button)
 {
-  GtkWidget *child;
-
   g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL);
 
-  child = gtk_button_get_child (GTK_BUTTON (menu_button->button));
-  if (GTK_IS_BOX (child))
-    {
-      child = gtk_widget_get_first_child (child);
-      return gtk_label_get_label (GTK_LABEL (child));
-    }
+  if (menu_button->label_widget)
+    return gtk_label_get_label (GTK_LABEL (menu_button->label_widget));
 
   return NULL;
 }
diff --git a/gtk/gtkmenubutton.h b/gtk/gtkmenubutton.h
index af71beedd6..c3855b6c28 100644
--- a/gtk/gtkmenubutton.h
+++ b/gtk/gtkmenubutton.h
@@ -80,6 +80,12 @@ void         gtk_menu_button_set_icon_name (GtkMenuButton *menu_button,
 GDK_AVAILABLE_IN_ALL
 const char * gtk_menu_button_get_icon_name (GtkMenuButton *menu_button);
 
+GDK_AVAILABLE_IN_ALL
+void         gtk_menu_button_set_always_show_arrow (GtkMenuButton *menu_button,
+                                                    gboolean       always_show_arrow);
+GDK_AVAILABLE_IN_ALL
+gboolean     gtk_menu_button_get_always_show_arrow (GtkMenuButton *menu_button);
+
 GDK_AVAILABLE_IN_ALL
 void         gtk_menu_button_set_label (GtkMenuButton *menu_button,
                                         const char    *label);


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