[gtk/wip/exalm/titlebuttons: 4/4] headerbar: Use GtkTitleButtons



commit 350d6522479734767ccab3b4bc05ece289c76f5c
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Fri Apr 24 20:55:43 2020 +0500

    headerbar: Use GtkTitleButtons
    
    Now that the title buttons are encapsulated in a separate widget, use
    it in the header bar.
    
    Hide them when empty, so that they don't add extra spacing.

 gtk/gtkheaderbar.c | 354 +++++++----------------------------------------------
 1 file changed, 45 insertions(+), 309 deletions(-)
---
diff --git a/gtk/gtkheaderbar.c b/gtk/gtkheaderbar.c
index 4afaa7dc19..f10d1d75c9 100644
--- a/gtk/gtkheaderbar.c
+++ b/gtk/gtkheaderbar.c
@@ -21,20 +21,17 @@
 
 #include "gtkheaderbarprivate.h"
 
-#include "gtkactionable.h"
 #include "gtkbox.h"
-#include "gtkbutton.h"
 #include "gtkbuildable.h"
 #include "gtkcenterlayout.h"
 #include "gtkcssnodeprivate.h"
-#include "gtkimage.h"
 #include "gtkintl.h"
 #include "gtklabel.h"
 #include "gtkprivate.h"
 #include "gtksizerequest.h"
+#include "gtktitlebuttons.h"
 #include "gtktypebuiltins.h"
 #include "gtkwidgetprivate.h"
-#include "gtkwindowprivate.h"
 #include "gtknative.h"
 
 #include "a11y/gtkcontaineraccessible.h"
@@ -68,21 +65,18 @@
  * |[<!-- language="plain" -->
  * headerbar
  * ├── box.start
- * │   ╰── box
- * │       ├── [image.titlebutton.icon]
- * │       ├── [button.titlebutton.minimize]
- * │       ├── [button.titlebutton.maximize]
- * │       ╰── [button.titlebutton.close]
+ * │   ╰── titlebuttons.start
  * ├── [Custom Title]
  * ╰── box.end
+ * │   ╰── titlebuttons.end
  * ]|
  *
- * A #GtkHeaderBar's CSS node is called headerbar. It contains two box subnodes at the start
- * and end of the headerbar, as well as a center node that represents the title.
+ * A #GtkHeaderBar's CSS node is called headerbar. It contains two box subnodes
+ * at the start and end of the headerbar, as well as a center node that
+ * represents the title.
  *
- * The titlebuttons get their own box subnode, either in the start box or in the end box.
- * Which of the title buttons exist and where they are placed exactly depends on the
- * desktop environment.
+ * Each of the boxes contains a titlebuttons subnode, see #GtkTitleButtons for
+ * details.
  */
 
 #define MIN_TITLE_CHARS 5
@@ -119,8 +113,8 @@ struct _GtkHeaderBarPrivate
   gchar *decoration_layout;
   gboolean track_default_decoration;
 
-  GtkWidget *titlebar_start_box;
-  GtkWidget *titlebar_end_box;
+  GtkWidget *start_title_buttons;
+  GtkWidget *end_title_buttons;
 
   GdkSurfaceState state;
 };
@@ -214,204 +208,31 @@ create_title_box (const char *title,
   return label_box;
 }
 
-static gboolean
-update_window_icon (GtkHeaderBar *bar,
-                    GtkWindow    *window,
-                    GtkWidget    *icon)
-{
-  GdkPaintable *paintable;
-  gint scale;
-
-  scale = gtk_widget_get_scale_factor (icon);
-  paintable = gtk_window_get_icon_for_size (window, 20 * scale);
-
-  if (paintable)
-    {
-      gtk_image_set_from_paintable (GTK_IMAGE (icon), paintable);
-      g_object_unref (paintable);
-      gtk_widget_show (icon);
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
 static void
-update_window_buttons (GtkHeaderBar *bar)
+create_title_buttons (GtkHeaderBar *bar)
 {
   GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
-  GtkWidget *widget = GTK_WIDGET (bar);
-  GtkWidget *toplevel;
-  GtkWindow *window;
-  gchar *layout_desc;
-  gchar **tokens, **t;
-  gint i, j;
-  gboolean is_sovereign_window;
-
-  if (!gtk_widget_get_realized (widget))
-    return;
-
-  toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
-  if (!GTK_IS_WINDOW (toplevel))
-    return;
-
-  if (priv->titlebar_start_box)
-    {
-      gtk_widget_unparent (priv->titlebar_start_box);
-      priv->titlebar_start_box = NULL;
-    }
-  if (priv->titlebar_end_box)
-    {
-      gtk_widget_unparent (priv->titlebar_end_box);
-      priv->titlebar_end_box = NULL;
-    }
-
-  if (!priv->show_title_buttons)
-    return;
-
-  if (priv->decoration_layout)
-    layout_desc = g_strdup (priv->decoration_layout);
-  else
-    g_object_get (gtk_widget_get_settings (widget),
-                  "gtk-decoration-layout", &layout_desc,
-                  NULL);
-
-  window = GTK_WINDOW (toplevel);
-
-  is_sovereign_window = !gtk_window_get_modal (window) &&
-                         gtk_window_get_transient_for (window) == NULL;
-
-  tokens = g_strsplit (layout_desc, ":", 2);
-  if (tokens)
-    {
-      for (i = 0; i < 2; i++)
-        {
-          GtkWidget *box;
-          int n_children = 0;
-
-          if (tokens[i] == NULL)
-            break;
-
-          t = g_strsplit (tokens[i], ",", -1);
-
-          box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-
-          for (j = 0; t[j]; j++)
-            {
-              GtkWidget *button = NULL;
-              GtkWidget *image = NULL;
-              AtkObject *accessible;
-
-              if (strcmp (t[j], "icon") == 0 &&
-                  is_sovereign_window)
-                {
-                  button = gtk_image_new ();
-                  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
-                  gtk_widget_add_css_class (button, "titlebutton");
-                  gtk_widget_add_css_class (button, "icon");
-
-                  if (!update_window_icon (bar, window, button))
-                    {
-                      g_object_ref_sink (button);
-                      g_object_unref (button);
-                      button = NULL;
-                    }
-                }
-              else if (strcmp (t[j], "minimize") == 0 &&
-                       is_sovereign_window)
-                {
-                  button = gtk_button_new ();
-                  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
-                  gtk_widget_add_css_class (button, "titlebutton");
-                  gtk_widget_add_css_class (button, "minimize");
-                  image = gtk_image_new_from_icon_name ("window-minimize-symbolic");
-                  g_object_set (image, "use-fallback", TRUE, NULL);
-                  gtk_container_add (GTK_CONTAINER (button), image);
-                  gtk_widget_set_can_focus (button, FALSE);
-                  gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
-                                                  "window.minimize");
-
-                  accessible = gtk_widget_get_accessible (button);
-                  if (GTK_IS_ACCESSIBLE (accessible))
-                    atk_object_set_name (accessible, _("Minimize"));
-                }
-              else if (strcmp (t[j], "maximize") == 0 &&
-                       gtk_window_get_resizable (window) &&
-                       is_sovereign_window)
-                {
-                  const gchar *icon_name;
-                  gboolean maximized = gtk_window_is_maximized (window);
-
-                  icon_name = maximized ? "window-restore-symbolic" : "window-maximize-symbolic";
-                  button = gtk_button_new ();
-                  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
-                  gtk_widget_add_css_class (button, "titlebutton");
-                  gtk_widget_add_css_class (button, "maximize");
-                  image = gtk_image_new_from_icon_name (icon_name);
-                  g_object_set (image, "use-fallback", TRUE, NULL);
-                  gtk_container_add (GTK_CONTAINER (button), image);
-                  gtk_widget_set_can_focus (button, FALSE);
-                  gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
-                                                  "window.toggle-maximized");
-
-                  accessible = gtk_widget_get_accessible (button);
-                  if (GTK_IS_ACCESSIBLE (accessible))
-                    atk_object_set_name (accessible, maximized ? _("Restore") : _("Maximize"));
-                }
-              else if (strcmp (t[j], "close") == 0 &&
-                       gtk_window_get_deletable (window))
-                {
-                  button = gtk_button_new ();
-                  gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
-                  image = gtk_image_new_from_icon_name ("window-close-symbolic");
-                  gtk_widget_add_css_class (button, "titlebutton");
-                  gtk_widget_add_css_class (button, "close");
-                  g_object_set (image, "use-fallback", TRUE, NULL);
-                  gtk_container_add (GTK_CONTAINER (button), image);
-                  gtk_widget_set_can_focus (button, FALSE);
-                  gtk_actionable_set_action_name (GTK_ACTIONABLE (button),
-                                                  "window.close");
-
-                  accessible = gtk_widget_get_accessible (button);
-                  if (GTK_IS_ACCESSIBLE (accessible))
-                    atk_object_set_name (accessible, _("Close"));
-                }
-
-              if (button)
-                {
-                  gtk_container_add (GTK_CONTAINER (box), button);
-                  n_children ++;
-                }
-            }
-          g_strfreev (t);
-
-          if (n_children == 0)
-            {
-              g_object_ref_sink (box);
-              g_object_unref (box);
-              continue;
-            }
-
-          if (i == 0)
-            gtk_widget_add_css_class (box, GTK_STYLE_CLASS_LEFT);
-          else
-            gtk_widget_add_css_class (box, GTK_STYLE_CLASS_RIGHT);
-
-          if (i == 0)
-            {
-              priv->titlebar_start_box = box;
-              gtk_container_add (GTK_CONTAINER (priv->start_box), box);
-            }
-          else
-            {
-              priv->titlebar_end_box = box;
-              gtk_container_add (GTK_CONTAINER (priv->end_box), box);
-            }
-        }
-      g_strfreev (tokens);
-    }
-  g_free (layout_desc);
+  GtkWidget *buttons;
+
+  buttons = gtk_title_buttons_new (GTK_PACK_START);
+  g_object_bind_property (bar, "decoration-layout",
+                          buttons, "decoration-layout",
+                          G_BINDING_SYNC_CREATE);
+  g_object_bind_property (buttons, "empty",
+                          buttons, "visible",
+                          G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+  gtk_container_add (GTK_CONTAINER (priv->start_box), buttons);
+  priv->start_title_buttons = buttons;
+
+  buttons = gtk_title_buttons_new (GTK_PACK_END);
+  g_object_bind_property (bar,  "decoration-layout",
+                          buttons, "decoration-layout",
+                          G_BINDING_SYNC_CREATE);
+  g_object_bind_property (buttons, "empty",
+                          buttons, "visible",
+                          G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+  gtk_container_add (GTK_CONTAINER (priv->end_box), buttons);
+  priv->end_title_buttons = buttons;
 }
 
 static void
@@ -434,7 +255,7 @@ update_default_decoration (GtkHeaderBar *bar)
            w != NULL;
            w = _gtk_widget_get_next_sibling (w))
         {
-          if (w != priv->titlebar_start_box)
+          if (w != priv->start_title_buttons)
             {
               have_children = TRUE;
               break;
@@ -446,7 +267,7 @@ update_default_decoration (GtkHeaderBar *bar)
              w != NULL;
              w = _gtk_widget_get_next_sibling (w))
           {
-            if (w != priv->titlebar_end_box)
+            if (w != priv->end_title_buttons)
               {
                 have_children = TRUE;
                 break;
@@ -872,7 +693,7 @@ gtk_header_bar_forall (GtkContainer *container,
         {
           GtkWidget *next = _gtk_widget_get_next_sibling (w);
 
-          if (w != priv->titlebar_start_box)
+          if (w != priv->start_title_buttons)
             (* callback) (w, callback_data);
 
           w = next;
@@ -889,7 +710,7 @@ gtk_header_bar_forall (GtkContainer *container,
         {
           GtkWidget *next = _gtk_widget_get_next_sibling (w);
 
-          if (w != priv->titlebar_end_box)
+          if (w != priv->end_title_buttons)
             (* callback) (w, callback_data);
 
           w = next;
@@ -903,93 +724,6 @@ gtk_header_bar_child_type (GtkContainer *container)
   return GTK_TYPE_WIDGET;
 }
 
-static void surface_state_changed (GtkWidget *widget);
-
-static void window_notify_cb (GtkHeaderBar *header_bar,
-                              GParamSpec   *pspec,
-                              GtkWindow    *window);
-
-static void
-gtk_header_bar_realize (GtkWidget *widget)
-{
-  GtkSettings *settings;
-  GtkWidget *root;
-
-  GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->realize (widget);
-
-  settings = gtk_widget_get_settings (widget);
-  g_signal_connect_swapped (settings, "notify::gtk-decoration-layout",
-                            G_CALLBACK (update_window_buttons), widget);
-  g_signal_connect_swapped (gtk_native_get_surface (gtk_widget_get_native (widget)), "notify::state",
-                            G_CALLBACK (surface_state_changed), widget);
-
-  root = GTK_WIDGET (gtk_widget_get_root (widget));
-
-  if (GTK_IS_WINDOW (root))
-    g_signal_connect_swapped (root, "notify",
-                              G_CALLBACK (window_notify_cb), widget);
-
-  update_window_buttons (GTK_HEADER_BAR (widget));
-}
-
-static void
-gtk_header_bar_unrealize (GtkWidget *widget)
-{
-  GtkSettings *settings;
-
-  settings = gtk_widget_get_settings (widget);
-
-  g_signal_handlers_disconnect_by_func (settings, update_window_buttons, widget);
-  g_signal_handlers_disconnect_by_func (gtk_native_get_surface (gtk_widget_get_native (widget)), 
surface_state_changed, widget);
-  g_signal_handlers_disconnect_by_func (gtk_widget_get_root (widget), window_notify_cb, widget);
-
-  GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->unrealize (widget);
-}
-
-static void
-surface_state_changed (GtkWidget *widget)
-{
-  GtkHeaderBar *bar = GTK_HEADER_BAR (widget);
-  GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
-  GdkSurfaceState changed, new_state;
-
-  new_state = gdk_toplevel_get_state (GDK_TOPLEVEL (gtk_native_get_surface (gtk_widget_get_native 
(widget))));
-  changed = new_state ^ priv->state;
-  priv->state = new_state;
-
-  if (changed & (GDK_SURFACE_STATE_FULLSCREEN |
-                 GDK_SURFACE_STATE_MAXIMIZED |
-                 GDK_SURFACE_STATE_TILED |
-                 GDK_SURFACE_STATE_TOP_TILED |
-                 GDK_SURFACE_STATE_RIGHT_TILED |
-                 GDK_SURFACE_STATE_BOTTOM_TILED |
-                 GDK_SURFACE_STATE_LEFT_TILED))
-    update_window_buttons (bar);
-}
-
-static void
-window_notify_cb (GtkHeaderBar *header_bar,
-                  GParamSpec   *pspec,
-                  GtkWindow    *window)
-{
-  if (pspec->name == I_("deletable") ||
-      pspec->name == I_("icon-name") ||
-      pspec->name == I_("modal") ||
-      pspec->name == I_("resizable") ||
-      pspec->name == I_("transient-for"))
-    update_window_buttons (header_bar);
-}
-
-static void
-gtk_header_bar_root (GtkWidget *widget)
-{
-  GtkHeaderBar *bar = GTK_HEADER_BAR (widget);
-
-  GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->root (widget);
-
-  update_window_buttons (bar);
-}
-
 static void
 gtk_header_bar_class_init (GtkHeaderBarClass *class)
 {
@@ -1002,10 +736,6 @@ gtk_header_bar_class_init (GtkHeaderBarClass *class)
   object_class->get_property = gtk_header_bar_get_property;
   object_class->set_property = gtk_header_bar_set_property;
 
-  widget_class->realize = gtk_header_bar_realize;
-  widget_class->unrealize = gtk_header_bar_unrealize;
-  widget_class->root = gtk_header_bar_root;
-
   container_class->add = gtk_header_bar_add;
   container_class->remove = gtk_header_bar_remove;
   container_class->forall = gtk_header_bar_forall;
@@ -1227,7 +957,15 @@ gtk_header_bar_set_show_title_buttons (GtkHeaderBar *bar,
     return;
 
   priv->show_title_buttons = setting;
-  update_window_buttons (bar);
+
+  if (setting)
+    create_title_buttons (bar);
+  else
+    {
+      g_clear_pointer (&priv->start_title_buttons, gtk_widget_unparent);
+      g_clear_pointer (&priv->end_title_buttons, gtk_widget_unparent);
+    }
+
   g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_SHOW_TITLE_BUTTONS]);
 }
 
@@ -1320,8 +1058,6 @@ gtk_header_bar_set_decoration_layout (GtkHeaderBar *bar,
   g_free (priv->decoration_layout);
   priv->decoration_layout = g_strdup (layout);
 
-  update_window_buttons (bar);
-
   g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_DECORATION_LAYOUT]);
 }
 


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