[gtk/wip/exalm/buttons: 1/4] button: Allow label and icon-name to be set together




commit 4398b1db2884900fa1c002a1ffb6767964d9e308
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Mon Aug 30 17:34:52 2021 +0500

    button: Allow label and icon-name to be set together
    
    Make label and icon-name not unset each other, have a GtkBox inside the
    button when using those and show/hide them conditionally, like the
    GtkMenuButton arrow.
    
    Update GtkStackSwitcher buttons to use the standard API instead of
    constructing contents manually. Update related styles.
    
    Update GtkShortcutsSection to not peek into button internals.
    
    Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/4205

 gtk/gtkbutton.c                | 135 ++++++++++++++++++++++-------------------
 gtk/gtkshortcutssection.c      |   6 +-
 gtk/gtkstackswitcher.c         |  56 +++++------------
 gtk/theme/Default/_common.scss |  22 ++++---
 4 files changed, 99 insertions(+), 120 deletions(-)
---
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 49f0681767..0ca7eff717 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -36,11 +36,12 @@
  *
  * # CSS nodes
  *
- * `GtkButton` has a single CSS node with name button. The node will get the
- * style classes .image-button or .text-button, if the content is just an
- * image or label, respectively. It may also receive the .flat style class.
- * When activating a button via the keyboard, the button will temporarily
- * gain the .keyboard-activating style class.
+ * `GtkButton` has a single CSS node with name `button`. The node will get the
+ * style classes `.image-button` and/or `.text-button`, if the content is set
+ * via [property@Gtk.Button:label] and/or [property@Gtk.Button:icon-name]. It
+ * may also receive the `.flat` style class. When activating a button via the
+ * keyboard, the button will temporarily gain the `.keyboard-activating` style
+ * class.
  *
  * Other style classes that are commonly used with `GtkButton` include
  * .suggested-action and .destructive-action. In special cases, buttons
@@ -66,6 +67,7 @@
 #include "gtkcheckbutton.h"
 #include "gtkgestureclick.h"
 #include "gtkeventcontrollerkey.h"
+#include "gtkbox.h"
 #include "gtkbinlayout.h"
 #include "gtkimage.h"
 #include "gtkintl.h"
@@ -87,6 +89,8 @@
 struct _GtkButtonPrivate
 {
   GtkWidget             *child;
+  GtkWidget             *icon;
+  GtkWidget             *label;
 
   GtkActionHelper       *action_helper;
 
@@ -120,8 +124,7 @@ enum {
 };
 
 enum {
-  LABEL_CHILD,
-  ICON_CHILD,
+  ICON_LABEL_CHILD,
   WIDGET_CHILD
 };
 
@@ -145,7 +148,7 @@ static void gtk_button_state_flags_changed (GtkWidget     *widget,
                                             GtkStateFlags  previous_state);
 static void gtk_button_do_release      (GtkButton             *button,
                                         gboolean               emit_clicked);
-static void gtk_button_set_child_type (GtkButton *button, guint child_type);
+static void gtk_button_update_style_classes (GtkButton *button);
 
 static void gtk_button_buildable_iface_init      (GtkBuildableIface *iface);
 static void gtk_button_actionable_iface_init     (GtkActionableInterface *iface);
@@ -400,29 +403,19 @@ key_controller_key_released_cb (GtkEventControllerKey *controller,
 }
 
 static void
-gtk_button_set_child_type (GtkButton *button, guint child_type)
+gtk_button_update_style_classes (GtkButton *button)
 {
   GtkButtonPrivate *priv = gtk_button_get_instance_private (button);
 
-  if (priv->child_type == child_type)
-    return;
-
-  if (child_type == LABEL_CHILD)
+  if (priv->label && gtk_widget_get_visible (priv->label))
     gtk_widget_add_css_class (GTK_WIDGET (button), "text-button");
   else
     gtk_widget_remove_css_class (GTK_WIDGET (button), "text-button");
 
-  if (child_type == ICON_CHILD)
+  if (priv->icon && gtk_widget_get_visible (priv->icon))
     gtk_widget_add_css_class (GTK_WIDGET (button), "image-button");
   else
     gtk_widget_remove_css_class (GTK_WIDGET (button), "image-button");
-
-  if (child_type != LABEL_CHILD)
-    g_object_notify_by_pspec (G_OBJECT (button), props[PROP_LABEL]);
-  else if (child_type != ICON_CHILD)
-    g_object_notify_by_pspec (G_OBJECT (button), props[PROP_ICON_NAME]);
-
-  priv->child_type = child_type;
 }
 
 static void
@@ -815,6 +808,37 @@ gtk_button_finish_activate (GtkButton *button,
     g_signal_emit (button, button_signals[CLICKED], 0);
 }
 
+static void
+create_default_child (GtkButton *button)
+{
+  GtkButtonPrivate *priv = gtk_button_get_instance_private (button);
+
+  g_clear_pointer (&priv->child, gtk_widget_unparent);
+
+  priv->icon = g_object_new (GTK_TYPE_IMAGE,
+                             "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
+                             NULL);
+  gtk_widget_set_valign (priv->icon, GTK_ALIGN_CENTER);
+  gtk_widget_hide (priv->icon);
+
+  priv->label = gtk_label_new (NULL);
+  gtk_widget_hide (priv->label);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label), GTK_WIDGET (button));
+
+  if (priv->use_underline)
+    gtk_label_set_use_underline (GTK_LABEL (priv->label), priv->use_underline);
+
+  priv->child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_widget_set_halign (priv->child, GTK_ALIGN_CENTER);
+  gtk_box_append (GTK_BOX (priv->child), priv->icon);
+  gtk_box_append (GTK_BOX (priv->child), priv->label);
+  gtk_widget_set_parent (priv->child, GTK_WIDGET (button));
+
+  priv->child_type = ICON_LABEL_CHILD;
+
+  g_object_notify_by_pspec (G_OBJECT (button), props[PROP_CHILD]);
+}
+
 /**
  * gtk_button_set_label: (attributes org.gtk.Method.set_property=label)
  * @button: a `GtkButton`
@@ -829,31 +853,21 @@ gtk_button_set_label (GtkButton   *button,
                       const char *label)
 {
   GtkButtonPrivate *priv = gtk_button_get_instance_private (button);
-  GtkWidget *child;
 
   g_return_if_fail (GTK_IS_BUTTON (button));
 
-  if (priv->child_type != LABEL_CHILD || priv->child == NULL)
-    {
-      child = gtk_label_new (NULL);
-      if (priv->use_underline)
-        {
-          gtk_label_set_use_underline (GTK_LABEL (child), priv->use_underline);
-          gtk_label_set_mnemonic_widget (GTK_LABEL (child), GTK_WIDGET (button));
-        }
-      if (GTK_IS_CHECK_BUTTON (button))
-        gtk_label_set_xalign (GTK_LABEL (child), 0.0);
-
-      gtk_button_set_child (button,  child);
-    }
+  if (priv->child_type != ICON_LABEL_CHILD || priv->child == NULL)
+    create_default_child (button);
 
-  gtk_label_set_label (GTK_LABEL (priv->child), label);
-  gtk_button_set_child_type (button, LABEL_CHILD);
+  gtk_label_set_label (GTK_LABEL (priv->label), label);
+  gtk_widget_set_visible (priv->label, label && label[0]);
 
   gtk_accessible_update_property (GTK_ACCESSIBLE (button),
                                   GTK_ACCESSIBLE_PROPERTY_LABEL, label,
                                   -1);
 
+  gtk_button_update_style_classes (button);
+
   g_object_notify_by_pspec (G_OBJECT (button), props[PROP_LABEL]);
 }
 
@@ -877,8 +891,8 @@ gtk_button_get_label (GtkButton *button)
 
   g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
 
-  if (priv->child_type == LABEL_CHILD)
-    return gtk_label_get_label (GTK_LABEL (priv->child));
+  if (priv->child_type == ICON_LABEL_CHILD)
+    return gtk_label_get_label (GTK_LABEL (priv->label));
 
   return NULL;
 }
@@ -905,11 +919,8 @@ gtk_button_set_use_underline (GtkButton *button,
 
   if (use_underline != priv->use_underline)
     {
-      if (priv->child_type == LABEL_CHILD)
-        {
-          gtk_label_set_use_underline (GTK_LABEL (priv->child), use_underline);
-          gtk_label_set_mnemonic_widget (GTK_LABEL (priv->child), GTK_WIDGET (button));
-        }
+      if (priv->child_type == ICON_LABEL_CHILD)
+        gtk_label_set_use_underline (GTK_LABEL (priv->label), use_underline);
 
       priv->use_underline = use_underline;
       g_object_notify_by_pspec (G_OBJECT (button), props[PROP_USE_UNDERLINE]);
@@ -966,25 +977,14 @@ gtk_button_set_icon_name (GtkButton  *button,
   g_return_if_fail (GTK_IS_BUTTON (button));
   g_return_if_fail (icon_name != NULL);
 
-  if (priv->child_type != ICON_CHILD || priv->child == NULL)
-    {
-      GtkWidget *child = g_object_new (GTK_TYPE_IMAGE,
-                                       "accessible-role", GTK_ACCESSIBLE_ROLE_PRESENTATION,
-                                       "icon-name", icon_name,
-                                       NULL);
-      gtk_button_set_child (GTK_BUTTON (button), child);
-      gtk_widget_set_valign (child, GTK_ALIGN_CENTER);
-    }
-  else
-    {
-      gtk_image_set_from_icon_name (GTK_IMAGE (priv->child), icon_name);
-    }
+  if (priv->child_type != ICON_LABEL_CHILD || priv->child == NULL)
+    create_default_child (button);
 
-  gtk_accessible_update_relation (GTK_ACCESSIBLE (button),
-                                  GTK_ACCESSIBLE_RELATION_LABELLED_BY, priv->child, NULL,
-                                  -1);
+  gtk_image_set_from_icon_name (GTK_IMAGE (priv->icon), icon_name);
+  gtk_widget_set_visible (priv->icon, icon_name && icon_name[0]);
+
+  gtk_button_update_style_classes (button);
 
-  gtk_button_set_child_type (button, ICON_CHILD);
   g_object_notify_by_pspec (G_OBJECT (button), props[PROP_ICON_NAME]);
 }
 
@@ -1007,8 +1007,8 @@ gtk_button_get_icon_name (GtkButton *button)
 
   g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
 
-  if (priv->child_type == ICON_CHILD)
-    return gtk_image_get_icon_name (GTK_IMAGE (priv->child));
+  if (priv->child_type == ICON_LABEL_CHILD)
+    return gtk_image_get_icon_name (GTK_IMAGE (priv->icon));
 
   return NULL;
 }
@@ -1046,13 +1046,20 @@ gtk_button_set_child (GtkButton *button,
   g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
 
   g_clear_pointer (&priv->child, gtk_widget_unparent);
+  priv->icon = NULL;
+  priv->label = NULL;
 
   priv->child = child;
 
   if (priv->child)
     gtk_widget_set_parent (priv->child, GTK_WIDGET (button));
 
-  gtk_button_set_child_type (button, WIDGET_CHILD);
+  priv->child_type = WIDGET_CHILD;
+
+  gtk_button_update_style_classes (button);
+
+  g_object_notify_by_pspec (G_OBJECT (button), props[PROP_LABEL]);
+  g_object_notify_by_pspec (G_OBJECT (button), props[PROP_ICON_NAME]);
   g_object_notify_by_pspec (G_OBJECT (button), props[PROP_CHILD]);
 }
 
diff --git a/gtk/gtkshortcutssection.c b/gtk/gtkshortcutssection.c
index 1e9038a240..935f76a78a 100644
--- a/gtk/gtkshortcutssection.c
+++ b/gtk/gtkshortcutssection.c
@@ -714,12 +714,8 @@ gtk_shortcuts_section_reflow_groups (GtkShortcutsSection *self)
          w != NULL;
          w = gtk_widget_get_next_sibling (w))
       {
-        GtkWidget *label;
-
         gtk_widget_add_css_class (w, "circular");
-
-        label = gtk_button_get_child (GTK_BUTTON (w));
-        gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
+        gtk_button_set_use_underline (GTK_BUTTON (w), TRUE);
       }
 
     gtk_widget_set_visible (GTK_WIDGET (self->switcher), (n_pages > 1));
diff --git a/gtk/gtkstackswitcher.c b/gtk/gtkstackswitcher.c
index dfb584cece..2153c27c7e 100644
--- a/gtk/gtkstackswitcher.c
+++ b/gtk/gtkstackswitcher.c
@@ -128,47 +128,6 @@ on_button_toggled (GtkWidget        *button,
     }
 }
 
-static void
-rebuild_child (GtkWidget   *self,
-               const char *icon_name,
-               const char *title,
-               gboolean     use_underline)
-{
-  GtkWidget *button_child;
-
-  button_child = NULL;
-
-  if (icon_name != NULL)
-    {
-      button_child = gtk_image_new_from_icon_name (icon_name);
-      if (title != NULL)
-        gtk_widget_set_tooltip_text (GTK_WIDGET (self), title);
-
-      gtk_widget_remove_css_class (self, "text-button");
-      gtk_widget_add_css_class (self, "image-button");
-    }
-  else if (title != NULL)
-    {
-      button_child = gtk_label_new (title);
-      gtk_label_set_use_underline (GTK_LABEL (button_child), use_underline);
-
-      gtk_widget_set_tooltip_text (GTK_WIDGET (self), NULL);
-
-      gtk_widget_remove_css_class (self, "image-button");
-      gtk_widget_add_css_class (self, "text-button");
-    }
-
-  if (button_child)
-    {
-      gtk_widget_set_halign (GTK_WIDGET (button_child), GTK_ALIGN_CENTER);
-      gtk_button_set_child (GTK_BUTTON (self), button_child);
-    }
-
-  gtk_accessible_update_property (GTK_ACCESSIBLE (self),
-                                  GTK_ACCESSIBLE_PROPERTY_LABEL, title,
-                                  -1);
-}
-
 static void
 update_button (GtkStackSwitcher *self,
                GtkStackPage     *page,
@@ -188,7 +147,20 @@ update_button (GtkStackSwitcher *self,
                 "use-underline", &use_underline,
                 NULL);
 
-  rebuild_child (button, icon_name, title, use_underline);
+  gtk_button_set_use_underline (GTK_BUTTON (button), use_underline);
+
+  if (icon_name != NULL && icon_name[0] != '\0')
+    {
+      gtk_button_set_icon_name (GTK_BUTTON (button), icon_name);
+      gtk_button_set_label (GTK_BUTTON (button), "");
+      gtk_widget_set_tooltip_text (button, title ? title : "");
+    }
+  else
+    {
+      gtk_button_set_icon_name (GTK_BUTTON (button), "");
+      gtk_button_set_label (GTK_BUTTON (button), title ? title : "");
+      gtk_widget_set_tooltip_text (button, "");
+    }
 
   gtk_widget_set_visible (button, visible && (title != NULL || icon_name != NULL));
 
diff --git a/gtk/theme/Default/_common.scss b/gtk/theme/Default/_common.scss
index dc9a9f6409..344af26831 100644
--- a/gtk/theme/Default/_common.scss
+++ b/gtk/theme/Default/_common.scss
@@ -557,12 +557,16 @@ button {
     }
 
     &.text-button.image-button {
-      padding-left: 8px;
-      padding-right: 8px;
+      padding-left: 10px;
+      padding-right: 10px;
 
-      label {
-        padding-left: 8px;
-        padding-right: 8px;
+      > box {
+        border-spacing: 4px;
+
+        > label {
+          padding-left: 2px;
+          padding-right: 2px;
+        }
       }
     }
 
@@ -714,19 +718,19 @@ button {
     // child, a label needs just lateral padding while an icon needs vertical
     // padding added too.
 
-    > label {
+    > box > label {
       padding: 0 6px;
       margin: 0 -6px;
     }
 
-    > image {
+    > box > image {
       padding: 3px 6px;
       margin: -3px -6px;
     }
 
     &.needs-attention {
-      > label,
-      > image { @extend %needs_attention; }
+      > box > label,
+      > box > image {@extend %needs_attention; }
     }
   }
 


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