[gtk+] Redo header bar decorations once more



commit 7e1a4800fa134365724d3310fc34ec5ccbc776e9
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Dec 21 21:58:24 2013 -0500

    Redo header bar decorations once more
    
    Applications need a way to fix or adapt the decoration layout,
    for situations like split header bars. Setting the layout from
    the theme with a style property did not offer a good way to do
    this, and the ::show-close-button property does not provide
    fine-grained control.
    
    To improve the situation, move the layout string to a property of
    GtkHeaderBar which is backed by a setting. This allows platforms to
    set a default button layout independent of the theme, while applications
    can override the default.
    
    The style GtkWindow style property is now deprecated and ignored.

 docs/reference/gtk/gtk3-sections.txt |    4 +-
 gdk/x11/gdksettings.c                |    1 +
 gtk/gtkheaderbar.c                   |  188 ++++++++++++++++++++++++++++++----
 gtk/gtkheaderbar.h                   |    6 +
 gtk/gtksettings.c                    |   35 +++++++
 tests/testtitlebar.c                 |   22 +---
 6 files changed, 218 insertions(+), 38 deletions(-)
---
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index 1be171d..ae68281 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -7696,8 +7696,8 @@ gtk_header_bar_pack_start
 gtk_header_bar_pack_end
 gtk_header_bar_set_show_close_button
 gtk_header_bar_get_show_close_button
-gtk_header_bar_set_show_fallback_app_menu
-gtk_header_bar_get_show_fallback_app_menu
+gtk_header_bar_set_decoration_layout
+gtk_header_bar_get_decoration_layout
 
 <SUBSECTION Standard>
 GTK_TYPE_HEADER_BAR
diff --git a/gdk/x11/gdksettings.c b/gdk/x11/gdksettings.c
index f9bc28e..ac92cca 100644
--- a/gdk/x11/gdksettings.c
+++ b/gdk/x11/gdksettings.c
@@ -58,6 +58,7 @@ static const struct {
   {"Gtk/ShellShowsAppMenu",   "gtk-shell-shows-app-menu"},
   {"Gtk/ShellShowsMenubar",   "gtk-shell-shows-menubar"},
   {"Gtk/ShellShowsDesktop",   "gtk-shell-shows-desktop"},
+  {"Gtk/DecorationLayout",    "gtk-decoration-layout"},
   {"Gtk/EnablePrimaryPaste",  "gtk-enable-primary-paste"},
   {"Gtk/RecentFilesMaxAge",   "gtk-recent-files-max-age"},
   {"Gtk/RecentFilesEnabled",  "gtk-recent-files-enabled"},
diff --git a/gtk/gtkheaderbar.c b/gtk/gtkheaderbar.c
index 1d8dac6..d1f3fa4 100644
--- a/gtk/gtkheaderbar.c
+++ b/gtk/gtkheaderbar.c
@@ -37,11 +37,17 @@
  * @Title: GtkHeaderBar
  * @See_also: #GtkBox
  *
- * GtkHeaderBar is similar to a horizontal #GtkBox, it allows
- * to place children at the start or the end. In addition,
- * it allows a title to be displayed. The title will be
- * centered with respect to the width of the box, even if the children
- * at either side take up different amounts of space.
+ * GtkHeaderBar is similar to a horizontal #GtkBox, it allows to place
+ * children at the start or the end. In addition, it allows a title and
+ * subtitle to be displayed. The title will be centered with respect to
+ * the width of the box, even if the children at either side take up
+ * different amounts of space. The height of the titlebar will be
+ * set to provide sufficient space for the subtitle, even if none is
+ * currently set. If a subtitle is not needed, the space reservation
+ * can be turned off with gtk_header_bar_set_has_subtitle().
+ *
+ * GtkHeaderBar can add typical window frame controls, such as minimize,
+ * maximize and close buttons, or the window icon.
  */
 
 #define DEFAULT_SPACING 6
@@ -62,6 +68,8 @@ struct _GtkHeaderBarPrivate
   GList *children;
 
   gboolean shows_wm_decorations;
+  gchar *decoration_layout;
+  gboolean decoration_layout_set;
 
   GtkWidget *titlebar_start_box;
   GtkWidget *titlebar_end_box;
@@ -88,6 +96,8 @@ enum {
   PROP_CUSTOM_TITLE,
   PROP_SPACING,
   PROP_SHOW_CLOSE_BUTTON,
+  PROP_DECORATION_LAYOUT,
+  PROP_DECORATION_LAYOUT_SET
 };
 
 enum {
@@ -265,6 +275,7 @@ void
 _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
 {
   GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
+  GtkWidget *widget = GTK_WIDGET (bar);
   GtkWindow *window;
   GtkTextDirection direction;
   gchar *layout_desc;
@@ -272,14 +283,11 @@ _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
   gint i, j;
   GMenuModel *menu;
   gboolean shown_by_shell;
+  GdkWindowTypeHint type_hint;
 
-  if (!gtk_widget_get_realized (GTK_WIDGET (bar)))
+  if (!gtk_widget_get_realized (widget))
     return;
 
-  window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (bar)));
-
-  direction = gtk_widget_get_direction (GTK_WIDGET (window));
-
   if (priv->titlebar_icon)
     {
       gtk_widget_destroy (priv->titlebar_icon);
@@ -319,18 +327,28 @@ _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
   if (!priv->shows_wm_decorations)
     return;
 
-  gtk_widget_style_get (GTK_WIDGET (window),
-                        "decoration-button-layout", &layout_desc,
-                        NULL);
+  direction = gtk_widget_get_direction (widget);
+
+  g_object_get (gtk_widget_get_settings (widget),
+                "gtk-shell-shows-app-menu", &shown_by_shell,
+                "gtk-decoration-layout", &layout_desc,
+                NULL);
 
-  g_object_get (gtk_widget_get_settings (GTK_WIDGET (window)),
-                "gtk-shell-shows-app-menu", &shown_by_shell, NULL);
+  if (priv->decoration_layout_set)
+    {
+      g_free (layout_desc);
+      layout_desc = g_strdup (priv->decoration_layout);
+    }
+
+  window = GTK_WINDOW (gtk_widget_get_toplevel (widget));
 
   if (!shown_by_shell && gtk_window_get_application (window))
     menu = gtk_application_get_app_menu (gtk_window_get_application (window));
   else
     menu = NULL;
 
+  type_hint = gtk_window_get_type_hint (window);
+
   tokens = g_strsplit (layout_desc, ":", 2);
   if (tokens)
     {
@@ -357,7 +375,7 @@ _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
               AtkObject *accessible;
 
               if (strcmp (t[j], "icon") == 0 &&
-                  gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL)
+                  type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
                 {
                   button = gtk_image_new ();
                   gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
@@ -374,7 +392,7 @@ _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
                 }
               else if (strcmp (t[j], "menu") == 0 &&
                        menu != NULL &&
-                       gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL)
+                       type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
                 {
                   button = gtk_menu_button_new ();
                   gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
@@ -393,7 +411,7 @@ _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
                     gtk_image_set_from_icon_name (GTK_IMAGE (priv->titlebar_icon), "process-stop-symbolic", 
GTK_ICON_SIZE_MENU);
                 }
               else if (strcmp (t[j], "minimize") == 0 &&
-                       gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL)
+                       type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
                 {
                   button = gtk_button_new ();
                   gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
@@ -412,7 +430,7 @@ _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
                 }
               else if (strcmp (t[j], "maximize") == 0 &&
                        gtk_window_get_resizable (window) &&
-                       gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL)
+                       type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
                 {
                   const gchar *icon_name;
                   gboolean maximized = _gtk_window_get_maximized (window);
@@ -541,6 +559,8 @@ gtk_header_bar_init (GtkHeaderBar *bar)
   priv->children = NULL;
   priv->spacing = DEFAULT_SPACING;
   priv->has_subtitle = TRUE;
+  priv->decoration_layout = NULL;
+  priv->decoration_layout_set = FALSE;
 
   init_sizing_box (bar);
   construct_label_box (bar);
@@ -1341,6 +1361,7 @@ gtk_header_bar_finalize (GObject *object)
 
   g_free (priv->title);
   g_free (priv->subtitle);
+  g_free (priv->decoration_layout);
 
   G_OBJECT_CLASS (gtk_header_bar_parent_class)->finalize (object);
 }
@@ -1380,6 +1401,14 @@ gtk_header_bar_get_property (GObject    *object,
       g_value_set_boolean (value, gtk_header_bar_get_has_subtitle (bar));
       break;
 
+    case PROP_DECORATION_LAYOUT:
+      g_value_set_string (value, gtk_header_bar_get_decoration_layout (bar));
+      break;
+
+    case PROP_DECORATION_LAYOUT_SET:
+      g_value_set_boolean (value, priv->decoration_layout_set);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1422,6 +1451,14 @@ gtk_header_bar_set_property (GObject      *object,
       gtk_header_bar_set_has_subtitle (bar, g_value_get_boolean (value));
       break;
 
+    case PROP_DECORATION_LAYOUT:
+      gtk_header_bar_set_decoration_layout (bar, g_value_get_string (value));
+      break;
+
+    case PROP_DECORATION_LAYOUT_SET:
+      priv->decoration_layout_set = g_value_get_boolean (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1779,6 +1816,16 @@ gtk_header_bar_class_init (GtkHeaderBarClass *class)
                                                      DEFAULT_SPACING,
                                                      GTK_PARAM_READWRITE));
 
+  /**
+   * GtkHeaderBar:show-close-button:
+   *
+   * Whether to show window decorations.
+   *
+   * Which buttons are actually shown and where is determined
+   * by the #GtkHeaderBar:decoration-layout property, and by
+   * the state of the window (e.g. a close button will not be
+   * shown if the window can't be closed).
+   */
   g_object_class_install_property (object_class,
                                    PROP_SHOW_CLOSE_BUTTON,
                                    g_param_spec_boolean ("show-close-button",
@@ -1788,6 +1835,41 @@ gtk_header_bar_class_init (GtkHeaderBarClass *class)
                                                          GTK_PARAM_READWRITE));
 
   /**
+   * GtkHeaderBar:decoration-layout:
+   * 
+   * The decoration layout for buttons. If this property is
+   * not set, the #GtkSettings:gtk-decoration-layout setting
+   * is used.
+   *
+   * See gtk_header_bar_set_decoration_layout() for information
+   * about the format of this string.
+   *
+   * Since: 3.12
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_DECORATION_LAYOUT,
+                                   g_param_spec_string ("decoration-layout",
+                                                        P_("Decoration Layout"),
+                                                        P_("The layout for window decorations"),
+                                                        NULL,
+                                                        GTK_PARAM_READWRITE));
+
+  /**
+   * GtkHeaderBar:decoration-layout-set:
+   *
+   * Set to %TRUE if #GtkHeaderBar:decoration-layout is set.
+   *
+   * Since: 3.12
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_DECORATION_LAYOUT_SET,
+                                   g_param_spec_boolean ("decoration-layout-set",
+                                                         P_("Decoration Layout Set"),
+                                                         P_("Whether the decoration-layout property has been 
set"),
+                                                         FALSE,
+                                                         GTK_PARAM_READWRITE));
+
+  /**
    * GtkHeaderBar:has-subtitle: 
    * 
    * If %TRUE, reserve space for a subtitle, even if none
@@ -1981,3 +2063,71 @@ gtk_header_bar_get_has_subtitle (GtkHeaderBar *bar)
 
   return priv->has_subtitle;
 }
+
+/**
+ * gtk_header_bar_set_decoration_layout:
+ * @bar: a #GtkHeaderBar
+ * @layout: (allow-none): a decoration layout, or %NULL to
+ *     unset the layout
+ *
+ * Sets the decoration layout for this header bar, overriding
+ * the #GtkSettings:gtk-decoration-layout setting. 
+ *
+ * There can be valid reasons for overriding the setting, such
+ * as a header bar design that does not allow for buttons to take
+ * room on the right, or only offers room for a single close button.
+ * Split header bars are another example for overriding the
+ * setting.
+ *
+ * The format of the string is button names, separated by commas.
+ * A colon separates the buttons that should appear on the left
+ * from those on the right. Recognized button names are minimize,
+ * maximize, close, icon (the window icon) and menu (a menu button
+ * for the fallback app menu).
+ *
+ * For example, "menu:minimize,maximize,close" specifies a menu
+ * on the left, and minimize, maximize and close buttons on the right.
+ *
+ * Since: 3.12
+ */
+void
+gtk_header_bar_set_decoration_layout (GtkHeaderBar *bar,
+                                      const gchar  *layout)
+{
+  GtkHeaderBarPrivate *priv;
+
+  g_return_if_fail (GTK_IS_HEADER_BAR (bar));
+
+  priv = gtk_header_bar_get_instance_private (bar);
+
+  priv->decoration_layout = g_strdup (layout);
+  priv->decoration_layout_set = (layout != NULL);
+
+  _gtk_header_bar_update_window_buttons (bar);
+
+  g_object_notify (G_OBJECT (bar), "decoration-layout");
+  g_object_notify (G_OBJECT (bar), "decoration-layout-set");
+}
+
+/**
+ * gtk_header_bar_get_decoration_layout:
+ * @bar: a #GtkHeaderBar
+ *
+ * Gets the decoration layout set with
+ * gtk_header_bar_set_decoration_layout().
+ *
+ * Returns: the decoration layout
+ *
+ * Since: 3.12 
+ */
+const gchar *
+gtk_header_bar_get_decoration_layout (GtkHeaderBar *bar)
+{
+  GtkHeaderBarPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL);
+
+  priv = gtk_header_bar_get_instance_private (bar);
+
+  return priv->decoration_layout;
+}
diff --git a/gtk/gtkheaderbar.h b/gtk/gtkheaderbar.h
index 1df62b7..73346e4 100644
--- a/gtk/gtkheaderbar.h
+++ b/gtk/gtkheaderbar.h
@@ -96,6 +96,12 @@ void         gtk_header_bar_set_has_subtitle (GtkHeaderBar *bar,
 GDK_AVAILABLE_IN_3_12
 gboolean     gtk_header_bar_get_has_subtitle (GtkHeaderBar *bar);
 
+GDK_AVAILABLE_IN_3_12
+void         gtk_header_bar_set_decoration_layout (GtkHeaderBar *bar,
+                                                   const gchar  *layout);
+GDK_AVAILABLE_IN_3_12
+const gchar *gtk_header_bar_get_decoration_layout (GtkHeaderBar *bar);
+
 G_END_DECLS
 
 #endif /* __GTK_HEADER_BAR_H__ */
diff --git a/gtk/gtksettings.c b/gtk/gtksettings.c
index 01f2ef8..3c74274 100644
--- a/gtk/gtksettings.c
+++ b/gtk/gtksettings.c
@@ -214,6 +214,7 @@ enum {
   PROP_SHELL_SHOWS_APP_MENU,
   PROP_SHELL_SHOWS_MENUBAR,
   PROP_SHELL_SHOWS_DESKTOP,
+  PROP_DECORATION_LAYOUT,
   PROP_ENABLE_PRIMARY_PASTE,
   PROP_RECENT_FILES_ENABLED
 };
@@ -1541,6 +1542,40 @@ gtk_settings_class_init (GtkSettingsClass *class)
   g_assert (result == PROP_SHELL_SHOWS_DESKTOP);
 
   /**
+   * GtkSettings:gtk-decoration-layout:
+   *
+   * This setting determines which buttons should be put in the
+   * titlebar of client-side decorated windows, and whether they
+   * should be placed at the left of right.
+   *
+   * The format of the string is button names, separated by commas.
+   * A colon separates the buttons that should appear on the left
+   * from those on the right. Recognized button names are minimize,
+   * maximize, close, icon (the window icon) and menu (a menu button
+   * for the fallback app menu).
+   *
+   * For example, "menu:minimize,maximize,close" specifies a menu
+   * on the left, and minimize, maximize and close buttons on the right.
+   *
+   * Note that buttons will only be shown when they are meaningful.
+   * E.g. a menu button only appears when the desktop shell does not
+   * show the app menu, and a close button only appears on a window
+   * that can be closed.
+   *
+   * Also note that the setting can be overridden with the
+   * #GtkHeaderBar:decoration-layout property.
+   *
+   * Since: 3.12
+   */
+  result = settings_install_property_parser (class,
+                                             g_param_spec_string ("gtk-decoration-layout",
+                                                                  P_("Decoration Layout"),
+                                                                   P_("The layout for window decorations"),
+                                                                   "menu:close", GTK_PARAM_READWRITE),
+                                             NULL);
+  g_assert (result == PROP_DECORATION_LAYOUT);
+
+  /**
    * GtkSettings:gtk-enable-primary-paste:
    *
    * Whether a middle click on a mouse should paste the
diff --git a/tests/testtitlebar.c b/tests/testtitlebar.c
index f5d032d..d11bb4b 100644
--- a/tests/testtitlebar.c
+++ b/tests/testtitlebar.c
@@ -3,19 +3,13 @@
 static void
 on_text_changed (GtkEntry       *entry,
                  GParamSpec     *pspec,
-                 GtkCssProvider *provider)
+                 GtkHeaderBar   *bar)
 {
   const gchar *layout;
-  gchar *css;
 
   layout = gtk_entry_get_text (entry);
 
-  css = g_strdup_printf ("GtkWindow {\n"
-                         "  -GtkWindow-decoration-button-layout: '%s';\n"
-                         "}", layout);
-
-  gtk_css_provider_load_from_data (provider, css, -1, NULL);
-  g_free (css);
+  gtk_header_bar_set_decoration_layout (bar, layout);
 }
 
 static void
@@ -30,7 +24,6 @@ activate (GApplication *gapp)
   GtkWidget *check;
   GtkBuilder *builder;
   GMenuModel *menu;
-  GtkCssProvider *provider;
   gchar *layout;
 
   g_action_map_add_action (G_ACTION_MAP (gapp), G_ACTION (g_simple_action_new ("test", NULL)));
@@ -58,11 +51,6 @@ activate (GApplication *gapp)
   gtk_header_bar_pack_end (GTK_HEADER_BAR (header), gtk_button_new_with_label ("End"));
   gtk_window_set_titlebar (GTK_WINDOW (window), header);
 
-  provider = gtk_css_provider_new ();
-
-  gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
-                                             GTK_STYLE_PROVIDER (provider), 600);
-
   grid = gtk_grid_new ();
   g_object_set (grid,
                 "halign", GTK_ALIGN_CENTER,
@@ -93,16 +81,16 @@ activate (GApplication *gapp)
   gtk_widget_set_halign (label, GTK_ALIGN_END);
   entry = gtk_entry_new ();
 
-  gtk_widget_style_get (window, "decoration-button-layout", &layout, NULL);
+  g_object_get (gtk_widget_get_settings (window), "gtk-decoration-layout", &layout, NULL);
   gtk_entry_set_text (GTK_ENTRY (entry), layout);
   g_free (layout);
 
   g_signal_connect (entry, "notify::text",
-                    G_CALLBACK (on_text_changed), provider);
+                    G_CALLBACK (on_text_changed), header);
   gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
   gtk_grid_attach (GTK_GRID (grid), entry, 1, 2, 1, 1);
 
-  label = gtk_label_new ("Close Button");
+  label = gtk_label_new ("Decorations");
   gtk_widget_set_halign (label, GTK_ALIGN_END);
   check = gtk_check_button_new ();
   g_object_bind_property (header, "show-close-button",


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