File gtk3-bnc872820-headerbar-decoration-layout.patch of Package gtk3.4637
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 412b27c..9f4b95a 100644
--- a/gtk/gtkheaderbar.c
+++ b/gtk/gtkheaderbar.c
@@ -26,6 +26,7 @@
#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
#include "gtkcontainerprivate.h"
+#include "gtkwindowprivate.h"
#include "a11y/gtkcontaineraccessible.h"
#include <string.h>
@@ -34,16 +35,23 @@
* SECTION:gtkheaderbar
* @Short_description: A box with a centered child
* @Title: GtkHeaderBar
- * @See_also: #GtkBox
+ * @See_also: #GtkBox, #GtkActionBar
*
- * 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 children to
+ * be placed 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
+#define MIN_TITLE_CHARS 20
struct _GtkHeaderBarPrivate
{
@@ -53,12 +61,25 @@ struct _GtkHeaderBarPrivate
GtkWidget *subtitle_label;
GtkWidget *label_box;
GtkWidget *label_sizing_box;
+ GtkWidget *subtitle_sizing_label;
GtkWidget *custom_title;
- GtkWidget *close_button;
- GtkWidget *separator;
gint spacing;
+ gboolean has_subtitle;
GList *children;
+
+ gboolean shows_wm_decorations;
+ gchar *decoration_layout;
+ gboolean decoration_layout_set;
+
+ GtkWidget *titlebar_start_box;
+ GtkWidget *titlebar_end_box;
+
+ GtkWidget *titlebar_icon;
+ GtkWidget *titlebar_menu_button;
+ GtkWidget *titlebar_min_button;
+ GtkWidget *titlebar_max_button;
+ GtkWidget *titlebar_close_button;
};
typedef struct _Child Child;
@@ -72,9 +93,12 @@ enum {
PROP_0,
PROP_TITLE,
PROP_SUBTITLE,
+ PROP_HAS_SUBTITLE,
PROP_CUSTOM_TITLE,
PROP_SPACING,
- PROP_SHOW_CLOSE_BUTTON
+ PROP_SHOW_CLOSE_BUTTON,
+ PROP_DECORATION_LAYOUT,
+ PROP_DECORATION_LAYOUT_SET
};
enum {
@@ -122,14 +146,17 @@ init_sizing_box (GtkHeaderBar *bar)
* in case we have only the title.
*/
priv->label_sizing_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_widget_show (priv->label_sizing_box);
w = gtk_label_new (NULL);
+ gtk_widget_show (w);
context = gtk_widget_get_style_context (w);
gtk_style_context_add_class (context, "title");
gtk_box_pack_start (GTK_BOX (priv->label_sizing_box), w, FALSE, FALSE, 0);
gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE);
gtk_label_set_ellipsize (GTK_LABEL (w), PANGO_ELLIPSIZE_END);
+ gtk_label_set_width_chars (GTK_LABEL (w), MIN_TITLE_CHARS);
w = gtk_label_new (NULL);
context = gtk_widget_get_style_context (w);
@@ -139,15 +166,15 @@ init_sizing_box (GtkHeaderBar *bar)
gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE);
gtk_label_set_ellipsize (GTK_LABEL (w), PANGO_ELLIPSIZE_END);
-
- gtk_widget_show_all (priv->label_sizing_box);
+ gtk_widget_set_visible (w, priv->has_subtitle || (priv->subtitle && priv->subtitle[0]));
+ priv->subtitle_sizing_label = w;
}
-GtkWidget *
-_gtk_header_bar_create_title_box (const char *title,
- const char *subtitle,
- GtkWidget **ret_title_label,
- GtkWidget **ret_subtitle_label)
+static GtkWidget *
+create_title_box (const char *title,
+ const char *subtitle,
+ GtkWidget **ret_title_label,
+ GtkWidget **ret_subtitle_label)
{
GtkWidget *label_box;
GtkWidget *title_label;
@@ -166,6 +193,7 @@ _gtk_header_bar_create_title_box (const char *title,
gtk_label_set_ellipsize (GTK_LABEL (title_label), PANGO_ELLIPSIZE_END);
gtk_box_pack_start (GTK_BOX (label_box), title_label, FALSE, FALSE, 0);
gtk_widget_show (title_label);
+ gtk_label_set_width_chars (GTK_LABEL (title_label), MIN_TITLE_CHARS);
subtitle_label = gtk_label_new (subtitle);
context = gtk_widget_get_style_context (subtitle_label);
@@ -185,64 +213,288 @@ _gtk_header_bar_create_title_box (const char *title,
return label_box;
}
-static void
-close_button_clicked (GtkButton *button, gpointer data)
+gboolean
+_gtk_header_bar_update_window_icon (GtkHeaderBar *bar,
+ GtkWindow *window)
{
- GtkWidget *toplevel;
+ GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
+ GdkPixbuf *pixbuf;
+
+ if (priv->titlebar_icon == NULL)
+ return FALSE;
+
+ if (GTK_IS_BUTTON (gtk_widget_get_parent (priv->titlebar_icon)))
+ pixbuf = gtk_window_get_icon_for_size (window, 16);
+ else
+ pixbuf = gtk_window_get_icon_for_size (window, 20);
+
+ if (pixbuf)
+ {
+ gtk_image_set_from_pixbuf (GTK_IMAGE (priv->titlebar_icon), pixbuf);
+ g_object_unref (pixbuf);
+ gtk_widget_show (priv->titlebar_icon);
+
+ return TRUE;
+ }
- toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
- gtk_window_close (GTK_WINDOW (toplevel));
+ return FALSE;
}
-static void
-add_close_button (GtkHeaderBar *bar)
+void
+_gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
{
- GtkHeaderBarPrivate *priv;
- GtkWidget *button;
- GIcon *icon;
- GtkWidget *image;
- GtkWidget *separator;
- GtkStyleContext *context;
- AtkObject *accessible;
+ GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
+ GtkWidget *widget = GTK_WIDGET (bar);
+ GtkWindow *window;
+ GtkTextDirection direction;
+ gchar *layout_desc;
+ gchar **tokens, **t;
+ gint i, j;
+ GMenuModel *menu;
+ gboolean shown_by_shell;
+ GdkWindowTypeHint type_hint;
- priv = gtk_header_bar_get_instance_private (bar);
+ if (!gtk_widget_get_realized (widget))
+ return;
- button = gtk_button_new ();
- gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
- context = gtk_widget_get_style_context (button);
- gtk_style_context_add_class (context, "image-button");
- gtk_style_context_add_class (context, "titlebutton");
- icon = g_themed_icon_new ("window-close-symbolic");
- image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_MENU);
- g_object_unref (icon);
- gtk_container_add (GTK_CONTAINER (button), image);
- gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
- g_signal_connect (button, "clicked",
- G_CALLBACK (close_button_clicked), NULL);
- accessible = gtk_widget_get_accessible (button);
- if (GTK_IS_ACCESSIBLE (accessible))
- atk_object_set_name (accessible, _("Close"));
- gtk_widget_show_all (button);
- gtk_widget_set_parent (button, GTK_WIDGET (bar));
-
- separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
- gtk_widget_show (separator);
- gtk_widget_set_parent (separator, GTK_WIDGET (bar));
-
- priv->separator = separator;
- priv->close_button = button;
+ if (priv->titlebar_icon)
+ {
+ gtk_widget_destroy (priv->titlebar_icon);
+ priv->titlebar_icon = NULL;
+ }
+ if (priv->titlebar_menu_button)
+ {
+ gtk_widget_destroy (priv->titlebar_menu_button);
+ priv->titlebar_menu_button = NULL;
+ }
+ if (priv->titlebar_min_button)
+ {
+ gtk_widget_destroy (priv->titlebar_min_button);
+ priv->titlebar_min_button = NULL;
+ }
+ if (priv->titlebar_max_button)
+ {
+ gtk_widget_destroy (priv->titlebar_max_button);
+ priv->titlebar_max_button = NULL;
+ }
+ if (priv->titlebar_close_button)
+ {
+ gtk_widget_destroy (priv->titlebar_close_button);
+ priv->titlebar_close_button = NULL;
+ }
+ if (priv->titlebar_start_box)
+ {
+ gtk_widget_destroy (priv->titlebar_start_box);
+ priv->titlebar_start_box = NULL;
+ }
+ if (priv->titlebar_end_box)
+ {
+ gtk_widget_destroy (priv->titlebar_end_box);
+ priv->titlebar_end_box = NULL;
+ }
+
+ if (!priv->shows_wm_decorations)
+ return;
+
+ 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);
+
+ 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)
+ {
+ for (i = 0; i < 2; i++)
+ {
+ GtkWidget *box;
+ GtkWidget *separator;
+ int n_children = 0;
+
+ if (tokens[i] == NULL)
+ break;
+
+ t = g_strsplit (tokens[i], ",", -1);
+
+ separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
+ gtk_widget_show (separator);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, priv->spacing);
+
+ for (j = 0; t[j]; j++)
+ {
+ GtkWidget *button = NULL;
+ GtkWidget *image = NULL;
+ AtkObject *accessible;
+
+ if (strcmp (t[j], "icon") == 0 &&
+ type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
+ {
+ button = gtk_image_new ();
+ gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
+ priv->titlebar_icon = button;
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
+ gtk_widget_set_size_request (button, 20, 20);
+ gtk_widget_show (button);
+ if (!_gtk_header_bar_update_window_icon (bar, window))
+ {
+ gtk_widget_destroy (button);
+ priv->titlebar_icon = NULL;
+ button = NULL;
+ }
+ }
+ else if (strcmp (t[j], "menu") == 0 &&
+ menu != NULL &&
+ type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
+ {
+ button = gtk_menu_button_new ();
+ gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
+ gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), menu);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
+ image = gtk_image_new ();
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_set_can_focus (button, FALSE);
+ gtk_widget_show_all (button);
+ accessible = gtk_widget_get_accessible (button);
+ if (GTK_IS_ACCESSIBLE (accessible))
+ atk_object_set_name (accessible, _("Application menu"));
+ priv->titlebar_icon = image;
+ priv->titlebar_menu_button = button;
+ if (!_gtk_header_bar_update_window_icon (bar, window))
+ 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 &&
+ type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
+ {
+ button = gtk_button_new ();
+ gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
+ image = gtk_image_new_from_icon_name ("window-minimize-symbolic", GTK_ICON_SIZE_MENU);
+ g_object_set (image, "use-fallback", TRUE, NULL);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_set_can_focus (button, FALSE);
+ gtk_widget_show_all (button);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gtk_window_iconify), window);
+ accessible = gtk_widget_get_accessible (button);
+ if (GTK_IS_ACCESSIBLE (accessible))
+ atk_object_set_name (accessible, _("Minimize"));
+ priv->titlebar_min_button = button;
+ }
+ else if (strcmp (t[j], "maximize") == 0 &&
+ gtk_window_get_resizable (window) &&
+ type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
+ {
+ 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_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
+ image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+ g_object_set (image, "use-fallback", TRUE, NULL);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_set_can_focus (button, FALSE);
+ gtk_widget_show_all (button);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (_gtk_window_toggle_maximized), window);
+ accessible = gtk_widget_get_accessible (button);
+ if (GTK_IS_ACCESSIBLE (accessible))
+ atk_object_set_name (accessible, maximized ? _("Restore") : _("Maximize"));
+ priv->titlebar_max_button = button;
+ }
+ 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_ICON_SIZE_MENU);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
+ g_object_set (image, "use-fallback", TRUE, NULL);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_set_can_focus (button, FALSE);
+ gtk_widget_show_all (button);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gtk_window_close), window);
+ accessible = gtk_widget_get_accessible (button);
+ if (GTK_IS_ACCESSIBLE (accessible))
+ atk_object_set_name (accessible, _("Close"));
+ priv->titlebar_close_button = button;
+ }
+
+ if (button)
+ {
+ gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+ n_children ++;
+ }
+ }
+ g_strfreev (t);
+
+ if (n_children == 0)
+ {
+ gtk_widget_destroy (box);
+ continue;
+ }
+
+ gtk_widget_show (box);
+ gtk_widget_set_parent (box, GTK_WIDGET (bar));
+
+ gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, 0);
+ if (i ==1)
+ gtk_box_reorder_child (GTK_BOX (box), separator, 0);
+
+ if ((direction == GTK_TEXT_DIR_LTR && i == 0) ||
+ (direction == GTK_TEXT_DIR_RTL && i == 1))
+ gtk_style_context_add_class (gtk_widget_get_style_context (box), "left");
+ else
+ gtk_style_context_add_class (gtk_widget_get_style_context (box), "right");
+
+ if (i == 0)
+ priv->titlebar_start_box = box;
+ else
+ priv->titlebar_end_box = box;
+ }
+ g_strfreev (tokens);
+ }
+ g_free (layout_desc);
}
-static void
-remove_close_button (GtkHeaderBar *bar)
+gboolean
+_gtk_header_bar_shows_app_menu (GtkHeaderBar *bar)
{
GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
+ GtkWindow *window;
+ gchar *layout_desc;
+ gboolean ret;
+
+ window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (bar)));
+ gtk_widget_style_get (GTK_WIDGET (window),
+ "decoration-button-layout", &layout_desc,
+ NULL);
- gtk_widget_unparent (priv->separator);
- gtk_widget_unparent (priv->close_button);
+ ret = priv->shows_wm_decorations &&
+ (layout_desc && strstr (layout_desc, "menu"));
+
+ g_free (layout_desc);
- priv->separator = NULL;
- priv->close_button = NULL;
+ return ret;
}
static void
@@ -252,10 +504,10 @@ construct_label_box (GtkHeaderBar *bar)
g_assert (priv->label_box == NULL);
- priv->label_box = _gtk_header_bar_create_title_box (priv->title,
- priv->subtitle,
- &priv->title_label,
- &priv->subtitle_label);
+ priv->label_box = create_title_box (priv->title,
+ priv->subtitle,
+ &priv->title_label,
+ &priv->subtitle_label);
gtk_widget_set_parent (priv->label_box, GTK_WIDGET (bar));
}
@@ -273,10 +525,11 @@ gtk_header_bar_init (GtkHeaderBar *bar)
priv->title = NULL;
priv->subtitle = NULL;
priv->custom_title = NULL;
- priv->close_button = NULL;
- priv->separator = NULL;
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);
@@ -347,6 +600,7 @@ gtk_header_bar_get_size (GtkWidget *widget,
gint nvis_children;
gint minimum, natural;
GtkBorder css_borders;
+ gint center_min, center_nat;
minimum = natural = 0;
nvis_children = 0;
@@ -359,24 +613,28 @@ gtk_header_bar_get_size (GtkWidget *widget,
nvis_children += 1;
}
+ center_min = center_nat = 0;
if (priv->label_box != NULL)
{
- if (add_child_size (priv->label_sizing_box, orientation, &minimum, &natural))
+ if (add_child_size (priv->label_sizing_box, orientation, ¢er_min, ¢er_nat))
nvis_children += 1;
}
if (priv->custom_title != NULL)
{
- if (add_child_size (priv->custom_title, orientation, &minimum, &natural))
+ if (add_child_size (priv->custom_title, orientation, ¢er_min, ¢er_nat))
nvis_children += 1;
}
- if (priv->close_button != NULL)
+ if (priv->titlebar_start_box != NULL)
{
- if (add_child_size (priv->close_button, orientation, &minimum, &natural))
+ if (add_child_size (priv->titlebar_start_box, orientation, &minimum, &natural))
nvis_children += 1;
+ }
- if (add_child_size (priv->separator, orientation, &minimum, &natural))
+ if (priv->titlebar_end_box != NULL)
+ {
+ if (add_child_size (priv->titlebar_end_box, orientation, &minimum, &natural))
nvis_children += 1;
}
@@ -390,13 +648,16 @@ gtk_header_bar_get_size (GtkWidget *widget,
if (GTK_ORIENTATION_HORIZONTAL == orientation)
{
- minimum += css_borders.left + css_borders.right;
- natural += css_borders.left + css_borders.right;
+ minimum += center_min + css_borders.left + css_borders.right;
+ natural += center_nat + css_borders.left + css_borders.right;
}
else
{
- minimum += css_borders.top + css_borders.bottom;
- natural += css_borders.top + css_borders.bottom;
+ /* We don't enforce css borders on the center widget, to make
+ * title/subtitle combinations fit without growing the header
+ */
+ minimum = MAX (center_min, minimum + css_borders.top + css_borders.bottom);
+ natural = MAX (center_nat, natural + css_borders.top + css_borders.bottom);
}
if (minimum_size)
@@ -440,8 +701,7 @@ gtk_header_bar_compute_size_for_orientation (GtkWidget *widget,
}
}
- if (priv->label_box != NULL &&
- gtk_widget_get_visible (priv->label_box))
+ if (priv->label_box != NULL)
{
gtk_widget_get_preferred_width (priv->label_sizing_box,
&child_size, &child_natural);
@@ -458,17 +718,22 @@ gtk_header_bar_compute_size_for_orientation (GtkWidget *widget,
required_natural += child_natural;
}
- if (priv->close_button != NULL)
+ if (priv->titlebar_start_box != NULL)
{
- gtk_widget_get_preferred_width (priv->close_button,
+ gtk_widget_get_preferred_width (priv->titlebar_start_box,
&child_size, &child_natural);
required_size += child_size;
required_natural += child_natural;
+ nvis_children += 1;
+ }
- gtk_widget_get_preferred_width (priv->separator,
+ if (priv->titlebar_end_box != NULL)
+ {
+ gtk_widget_get_preferred_width (priv->titlebar_end_box,
&child_size, &child_natural);
required_size += child_size;
required_natural += child_natural;
+ nvis_children += 1;
}
if (nvis_children > 0)
@@ -510,6 +775,7 @@ gtk_header_bar_compute_size_for_opposing_orientation (GtkWidget *widget,
gint child_minimum;
gint child_natural;
GtkBorder css_borders;
+ gint center_min, center_nat;
nvis_children = count_visible_children (bar);
@@ -570,32 +836,31 @@ gtk_header_bar_compute_size_for_opposing_orientation (GtkWidget *widget,
i += 1;
}
- if (priv->label_box != NULL &&
- gtk_widget_get_visible (priv->label_box))
+ center_min = center_nat = 0;
+ if (priv->label_box != NULL)
{
gtk_widget_get_preferred_height (priv->label_sizing_box,
- &child_minimum, &child_natural);
- computed_minimum = MAX (computed_minimum, child_minimum);
- computed_natural = MAX (computed_natural, child_natural);
+ ¢er_min, ¢er_nat);
}
if (priv->custom_title != NULL &&
gtk_widget_get_visible (priv->custom_title))
{
gtk_widget_get_preferred_height (priv->custom_title,
- &child_minimum, &child_natural);
- computed_minimum = MAX (computed_minimum, child_minimum);
- computed_natural = MAX (computed_natural, child_natural);
+ ¢er_min, ¢er_nat);
}
- if (priv->close_button != NULL)
+ if (priv->titlebar_start_box != NULL)
{
- gtk_widget_get_preferred_height (priv->close_button,
+ gtk_widget_get_preferred_height (priv->titlebar_start_box,
&child_minimum, &child_natural);
computed_minimum = MAX (computed_minimum, child_minimum);
computed_natural = MAX (computed_natural, child_natural);
+ }
- gtk_widget_get_preferred_height (priv->separator,
+ if (priv->titlebar_end_box != NULL)
+ {
+ gtk_widget_get_preferred_height (priv->titlebar_end_box,
&child_minimum, &child_natural);
computed_minimum = MAX (computed_minimum, child_minimum);
computed_natural = MAX (computed_natural, child_natural);
@@ -603,8 +868,11 @@ gtk_header_bar_compute_size_for_opposing_orientation (GtkWidget *widget,
get_css_padding_and_border (widget, &css_borders);
- computed_minimum += css_borders.top + css_borders.bottom;
- computed_natural += css_borders.top + css_borders.bottom;
+ /* We don't enforce css borders on the center widget, to make
+ * title/subtitle combinations fit without growing the header
+ */
+ computed_minimum = MAX (center_min, computed_minimum + css_borders.top + css_borders.bottom);
+ computed_natural = MAX (center_nat, computed_natural + css_borders.top + css_borders.bottom);
if (minimum_size)
*minimum_size = computed_minimum;
@@ -647,30 +915,6 @@ gtk_header_bar_get_preferred_height_for_width (GtkWidget *widget,
gtk_header_bar_compute_size_for_opposing_orientation (widget, width, minimum_height, natural_height);
}
-static gboolean
-close_button_at_end (GtkWidget *widget)
-{
- GtkWidget *toplevel;
- gchar *layout_desc;
- gboolean at_end;
- gchar *p;
-
- toplevel = gtk_widget_get_toplevel (widget);
- if (!GTK_IS_WINDOW (toplevel))
- return TRUE;
- gtk_widget_style_get (toplevel, "decoration-button-layout", &layout_desc, NULL);
-
- p = strchr (layout_desc, ':');
- if (p && strstr (p, "close"))
- at_end = TRUE;
- else
- at_end = FALSE;
-
- g_free (layout_desc);
-
- return at_end;
-}
-
static void
gtk_header_bar_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
@@ -682,9 +926,7 @@ gtk_header_bar_size_allocate (GtkWidget *widget,
gint nvis_children;
gint title_minimum_size;
gint title_natural_size;
- gint close_button_width;
- gint separator_width;
- gint close_width;
+ gint start_width, end_width;
gint side[2];
GList *l;
gint i;
@@ -695,9 +937,6 @@ gtk_header_bar_size_allocate (GtkWidget *widget,
gint child_size;
GtkTextDirection direction;
GtkBorder css_borders;
- gboolean at_end;
-
- at_end = close_button_at_end (widget);
gtk_widget_set_allocation (widget, allocation);
@@ -724,14 +963,19 @@ gtk_header_bar_size_allocate (GtkWidget *widget,
i++;
}
- if (priv->custom_title)
+ title_minimum_size = 0;
+ title_natural_size = 0;
+
+ if (priv->custom_title &&
+ gtk_widget_get_visible (priv->custom_title))
{
gtk_widget_get_preferred_width_for_height (priv->custom_title,
height,
&title_minimum_size,
&title_natural_size);
}
- else
+
+ if (priv->label_box != NULL)
{
gtk_widget_get_preferred_width_for_height (priv->label_box,
height,
@@ -740,22 +984,27 @@ gtk_header_bar_size_allocate (GtkWidget *widget,
}
width -= title_natural_size;
- close_button_width = separator_width = close_width = 0;
- if (priv->close_button != NULL)
+ start_width = 0;
+ if (priv->titlebar_start_box != NULL)
{
gint min, nat;
- gtk_widget_get_preferred_width_for_height (priv->close_button,
+ gtk_widget_get_preferred_width_for_height (priv->titlebar_start_box,
height,
&min, &nat);
- close_button_width = nat;
+ start_width = nat + priv->spacing;
+ }
+ width -= start_width;
- gtk_widget_get_preferred_width_for_height (priv->separator,
+ end_width = 0;
+ if (priv->titlebar_end_box != NULL)
+ {
+ gint min, nat;
+ gtk_widget_get_preferred_width_for_height (priv->titlebar_end_box,
height,
&min, &nat);
- separator_width = nat;
- close_width = close_button_width + separator_width + 2 * priv->spacing;
+ end_width = nat + priv->spacing;
}
- width -= close_width;
+ width -= end_width;
width = gtk_distribute_natural_allocation (MAX (0, width), nvis_children, sizes);
@@ -765,22 +1014,12 @@ gtk_header_bar_size_allocate (GtkWidget *widget,
child_allocation.y = allocation->y + css_borders.top;
child_allocation.height = height;
if (packing == GTK_PACK_START)
- x = allocation->x + css_borders.left + (at_end ? 0 : close_width);
+ x = allocation->x + css_borders.left + start_width;
else
- x = allocation->x + allocation->width - (at_end ? close_width : 0) - css_borders.right;
+ x = allocation->x + allocation->width - end_width - css_borders.right;
- if (packing == GTK_PACK_START)
- {
- l = priv->children;
- i = 0;
- }
- else
- {
- l = g_list_last (priv->children);
- i = nvis_children - 1;
- }
-
- for (; l != NULL; (packing == GTK_PACK_START) ? (l = l->next) : (l = l->prev))
+ i = 0;
+ for (l = priv->children; l != NULL; l = l->next)
{
child = l->data;
if (!gtk_widget_get_visible (child->widget))
@@ -814,20 +1053,18 @@ gtk_header_bar_size_allocate (GtkWidget *widget,
gtk_widget_size_allocate (child->widget, &child_allocation);
next:
- if (packing == GTK_PACK_START)
- i++;
- else
- i--;
+ i++;
}
}
- if (at_end)
- side[GTK_PACK_END] += close_width;
- else
- side[GTK_PACK_START] += close_width;
+ side[GTK_PACK_START] += start_width;
+ side[GTK_PACK_END] += end_width;
- child_allocation.y = allocation->y + css_borders.top;
- child_allocation.height = height;
+ /* We don't enforce css borders on the center widget, to make
+ * title/subtitle combinations fit without growing the header
+ */
+ child_allocation.y = allocation->y;
+ child_allocation.height = allocation->height;
width = MAX (side[0], side[1]);
@@ -849,40 +1086,43 @@ gtk_header_bar_size_allocate (GtkWidget *widget,
if (direction == GTK_TEXT_DIR_RTL)
child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
- if (priv->custom_title)
+ if (priv->custom_title != NULL &&
+ gtk_widget_get_visible (priv->custom_title))
gtk_widget_size_allocate (priv->custom_title, &child_allocation);
- else
- gtk_widget_size_allocate (priv->label_box, &child_allocation);
- if (priv->close_button)
- {
- gboolean left;
+ if (priv->label_box != NULL)
+ gtk_widget_size_allocate (priv->label_box, &child_allocation);
- if (direction == GTK_TEXT_DIR_RTL)
- left = at_end;
- else
- left = !at_end;
+ child_allocation.y = allocation->y + css_borders.top;
+ child_allocation.height = height;
+ if (priv->titlebar_start_box)
+ {
+ gboolean left = (direction == GTK_TEXT_DIR_LTR);
if (left)
child_allocation.x = allocation->x + css_borders.left;
else
- child_allocation.x = allocation->x + allocation->width - css_borders.right - close_button_width;
- child_allocation.width = close_button_width;
- gtk_widget_size_allocate (priv->close_button, &child_allocation);
+ child_allocation.x = allocation->x + allocation->width - css_borders.right - start_width + priv->spacing;
+ child_allocation.width = start_width - priv->spacing;
+ gtk_widget_size_allocate (priv->titlebar_start_box, &child_allocation);
+ }
+ if (priv->titlebar_end_box)
+ {
+ gboolean left = (direction != GTK_TEXT_DIR_LTR);
if (left)
- child_allocation.x = allocation->x + css_borders.left + close_button_width + priv->spacing;
+ child_allocation.x = allocation->x + css_borders.left;
else
- child_allocation.x = allocation->x + allocation->width - css_borders.right - close_button_width - priv->spacing - separator_width;
- child_allocation.width = separator_width;
- gtk_widget_size_allocate (priv->separator, &child_allocation);
+ child_allocation.x = allocation->x + allocation->width - css_borders.right - end_width + priv->spacing;
+ child_allocation.width = end_width - priv->spacing;
+ gtk_widget_size_allocate (priv->titlebar_end_box, &child_allocation);
}
}
/**
* gtk_header_bar_set_title:
* @bar: a #GtkHeaderBar
- * @title: (allow-none): a title
+ * @title: (allow-none): a title, or %NULL
*
* Sets the title of the #GtkHeaderBar. The title should help a user
* identify the current view. A good title should not include the
@@ -918,8 +1158,8 @@ gtk_header_bar_set_title (GtkHeaderBar *bar,
*
* Retrieves the title of the header. See gtk_header_bar_set_title().
*
- * Return value: the title of the header, or %NULL if none has
- * been set explicitely. The returned string is owned by the widget
+ * Returns: the title of the header, or %NULL if none has
+ * been set explicitly. The returned string is owned by the widget
* and must not be modified or freed.
*
* Since: 3.10
@@ -937,16 +1177,14 @@ gtk_header_bar_get_title (GtkHeaderBar *bar)
/**
* gtk_header_bar_set_subtitle:
* @bar: a #GtkHeaderBar
- * @subtitle: (allow-none): a subtitle
+ * @subtitle: (allow-none): a subtitle, or %NULL
*
* Sets the subtitle of the #GtkHeaderBar. The title should give a user
* an additional detail to help him identify the current view.
*
- * Note that GtkHeaderBar always reserves room for the subtitle, even
- * if none is currently set. If this is not desired, use
- * gtk_header_bar_set_custom_title() to place your own label in the
- * title position. To achieve the same style, use the "title" style
- * class.
+ * Note that GtkHeaderBar by default reserves room for the subtitle,
+ * even if none is currently set. If this is not desired, set the
+ * #GtkHeaderBar:has-subtitle property to %FALSE.
*
* Since: 3.10
*/
@@ -966,10 +1204,12 @@ gtk_header_bar_set_subtitle (GtkHeaderBar *bar,
if (priv->subtitle_label != NULL)
{
gtk_label_set_label (GTK_LABEL (priv->subtitle_label), priv->subtitle);
- gtk_widget_set_visible (priv->subtitle_label, priv->subtitle != NULL);
+ gtk_widget_set_visible (priv->subtitle_label, priv->subtitle && priv->subtitle[0]);
gtk_widget_queue_resize (GTK_WIDGET (bar));
}
+ gtk_widget_set_visible (priv->subtitle_sizing_label, priv->has_subtitle || (priv->subtitle && priv->subtitle[0]));
+
g_object_notify (G_OBJECT (bar), "subtitle");
}
@@ -979,8 +1219,8 @@ gtk_header_bar_set_subtitle (GtkHeaderBar *bar,
*
* Retrieves the subtitle of the header. See gtk_header_bar_set_subtitle().
*
- * Return value: the subtitle of the header, or %NULL if none has
- * been set explicitely. The returned string is owned by the widget
+ * Returns: the subtitle of the header, or %NULL if none has
+ * been set explicitly. The returned string is owned by the widget
* and must not be modified or freed.
*
* Since: 3.10
@@ -1000,11 +1240,16 @@ gtk_header_bar_get_subtitle (GtkHeaderBar *bar)
* @bar: a #GtkHeaderBar
* @title_widget: (allow-none): a custom widget to use for a title
*
- * Sets a custom title for the #GtkHeaderBar. The title should help a
- * user identify the current view. This supercedes any title set by
- * gtk_header_bar_set_title() or gtk_header_bar_set_subtitle().
- * You should set the custom title to %NULL, for the header title label
- * to be visible again.
+ * Sets a custom title for the #GtkHeaderBar.
+ *
+ * The title should help a user identify the current view. This
+ * supersedes any title set by gtk_header_bar_set_title() or
+ * gtk_header_bar_set_subtitle(). To achieve the same style as
+ * the builtin title and subtitle, use the “title” and “subtitle”
+ * style classes.
+ *
+ * You should set the custom title to %NULL, for the header title
+ * label to be visible again.
*
* Since: 3.10
*/
@@ -1036,7 +1281,6 @@ gtk_header_bar_set_custom_title (GtkHeaderBar *bar,
gtk_widget_set_parent (priv->custom_title, GTK_WIDGET (bar));
gtk_widget_set_valign (priv->custom_title, GTK_ALIGN_CENTER);
- gtk_widget_show (title_widget);
if (priv->label_box != NULL)
{
@@ -1067,8 +1311,8 @@ gtk_header_bar_set_custom_title (GtkHeaderBar *bar,
* Retrieves the custom title widget of the header. See
* gtk_header_bar_set_custom_title().
*
- * Return value: (transfer none): the custom title widget
- * of the header, or %NULL if none has been set explicitely.
+ * Returns: (transfer none): the custom title widget
+ * of the header, or %NULL if none has been set explicitly.
*
* Since: 3.10
*/
@@ -1089,6 +1333,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);
}
@@ -1124,6 +1369,18 @@ gtk_header_bar_get_property (GObject *object,
g_value_set_boolean (value, gtk_header_bar_get_show_close_button (bar));
break;
+ case PROP_HAS_SUBTITLE:
+ 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;
@@ -1162,6 +1419,18 @@ gtk_header_bar_set_property (GObject *object,
gtk_header_bar_set_show_close_button (bar, g_value_get_boolean (value));
break;
+ case PROP_HAS_SUBTITLE:
+ 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;
@@ -1262,11 +1531,11 @@ gtk_header_bar_forall (GtkContainer *container,
if (include_internals && priv->label_box != NULL)
(* callback) (priv->label_box, callback_data);
- if (include_internals && priv->close_button != NULL)
- (* callback) (priv->close_button, callback_data);
+ if (include_internals && priv->titlebar_start_box != NULL)
+ (* callback) (priv->titlebar_start_box, callback_data);
- if (include_internals && priv->separator != NULL)
- (* callback) (priv->separator, callback_data);
+ if (include_internals && priv->titlebar_end_box != NULL)
+ (* callback) (priv->titlebar_end_box, callback_data);
children = priv->children;
while (children)
@@ -1416,6 +1685,33 @@ gtk_header_bar_draw (GtkWidget *widget,
}
static void
+gtk_header_bar_realize (GtkWidget *widget)
+{
+ GtkSettings *settings;
+
+ GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->realize (widget);
+
+ settings = gtk_widget_get_settings (widget);
+ g_signal_connect_swapped (settings, "notify::gtk-shell-shows-app-menu",
+ G_CALLBACK (_gtk_header_bar_update_window_buttons), widget);
+ g_signal_connect_swapped (settings, "notify::gtk-decoration-layout",
+ G_CALLBACK (_gtk_header_bar_update_window_buttons), widget);
+ _gtk_header_bar_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, _gtk_header_bar_update_window_buttons, widget);
+
+ GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->unrealize (widget);
+}
+
+static void
gtk_header_bar_class_init (GtkHeaderBarClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
@@ -1432,6 +1728,8 @@ gtk_header_bar_class_init (GtkHeaderBarClass *class)
widget_class->get_preferred_height_for_width = gtk_header_bar_get_preferred_height_for_width;
widget_class->get_preferred_width_for_height = gtk_header_bar_get_preferred_width_for_height;
widget_class->draw = gtk_header_bar_draw;
+ widget_class->realize = gtk_header_bar_realize;
+ widget_class->unrealize = gtk_header_bar_unrealize;
container_class->add = gtk_header_bar_add;
container_class->remove = gtk_header_bar_remove;
@@ -1468,7 +1766,7 @@ gtk_header_bar_class_init (GtkHeaderBarClass *class)
g_object_class_install_property (object_class,
PROP_SUBTITLE,
g_param_spec_string ("subtitle",
- P_("Subitle"),
+ P_("Subtitle"),
P_("The subtitle to display"),
NULL,
G_PARAM_READWRITE));
@@ -1492,14 +1790,75 @@ 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",
- P_("Show Close button"),
- P_("Whether to show a window close button"),
+ P_("Show decorations"),
+ P_("Whether to show window decorations"),
+ FALSE,
+ 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
+ * is currently set.
+ *
+ * Since: 3.12
+ */
+ g_object_class_install_property (object_class,
+ PROP_HAS_SUBTITLE,
+ g_param_spec_boolean ("has-subtitle",
+ P_("Has Subtitle"),
+ P_("Whether to reserve space for a subtitle"),
+ TRUE,
+ GTK_PARAM_READWRITE));
+
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_PANEL);
}
@@ -1592,7 +1951,7 @@ gtk_header_bar_get_show_close_button (GtkHeaderBar *bar)
priv = gtk_header_bar_get_instance_private (bar);
- return priv->close_button != NULL;
+ return priv->shows_wm_decorations;
}
/**
@@ -1617,15 +1976,135 @@ gtk_header_bar_set_show_close_button (GtkHeaderBar *bar,
setting = setting != FALSE;
- if ((priv->close_button != NULL) == setting)
+ if (priv->shows_wm_decorations == setting)
return;
- if (setting)
- add_close_button (bar);
- else
- remove_close_button (bar);
+ priv->shows_wm_decorations = setting;
+ _gtk_header_bar_update_window_buttons (bar);
+ g_object_notify (G_OBJECT (bar), "show-close-button");
+}
+
+/**
+ * gtk_header_bar_set_has_subtitle:
+ * @bar: a #GtkHeaderBar
+ * @setting: %TRUE to reserve space for a subtitle
+ *
+ * Sets whether the header bar should reserve space
+ * for a subtitle, even if none is currently set.
+ *
+ * Since: 3.12
+ */
+void
+gtk_header_bar_set_has_subtitle (GtkHeaderBar *bar,
+ gboolean setting)
+{
+ GtkHeaderBarPrivate *priv;
+
+ g_return_if_fail (GTK_IS_HEADER_BAR (bar));
+
+ priv = gtk_header_bar_get_instance_private (bar);
+
+ setting = setting != FALSE;
+
+ if (priv->has_subtitle == setting)
+ return;
+
+ priv->has_subtitle = setting;
+ gtk_widget_set_visible (priv->subtitle_sizing_label, setting || (priv->subtitle && priv->subtitle[0]));
gtk_widget_queue_resize (GTK_WIDGET (bar));
- g_object_notify (G_OBJECT (bar), "show-close-button");
+ g_object_notify (G_OBJECT (bar), "has-subtitle");
+}
+
+/**
+ * gtk_header_bar_get_has_subtitle:
+ * @bar: a #GtkHeaderBar
+ *
+ * Retrieves whether the header bar reserves space for
+ * a subtitle, regardless if one is currently set or not.
+ *
+ * Returns: %TRUE if the header bar reserves space
+ * for a subtitle
+ *
+ * Since: 3.12
+ */
+gboolean
+gtk_header_bar_get_has_subtitle (GtkHeaderBar *bar)
+{
+ GtkHeaderBarPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), FALSE);
+
+ priv = gtk_header_bar_get_instance_private (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 0e4f5e2..d495525 100644
--- a/gtk/gtkheaderbar.h
+++ b/gtk/gtkheaderbar.h
@@ -90,6 +90,14 @@ GDK_AVAILABLE_IN_3_10
void gtk_header_bar_set_show_close_button (GtkHeaderBar *bar,
gboolean setting);
+void gtk_header_bar_set_has_subtitle (GtkHeaderBar *bar,
+ gboolean setting);
+gboolean gtk_header_bar_get_has_subtitle (GtkHeaderBar *bar);
+
+void gtk_header_bar_set_decoration_layout (GtkHeaderBar *bar,
+ const gchar *layout);
+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 8e95bb3..6140b5f 100644
--- a/gtk/gtksettings.c
+++ b/gtk/gtksettings.c
@@ -210,6 +210,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
};
@@ -1539,6 +1540,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/gtk/gtkwindow.c b/gtk/gtkwindow.c
index e9d13ed..8891526 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -1217,6 +1217,12 @@ gtk_window_get_maximized (GtkWindow *window)
return maximized;
}
+gboolean
+_gtk_window_is_maximized (GtkWindow *window)
+{
+ return gtk_window_get_maximized (window);
+}
+
static void
gtk_window_titlebar_min_clicked (GtkWidget *widget, gpointer data)
{
@@ -1236,6 +1242,12 @@ gtk_window_titlebar_max_clicked (GtkWidget *widget, gpointer data)
gtk_window_maximize (window);
}
+void
+_gtk_window_toggle_maximized (GtkWindow *window)
+{
+ gtk_window_titlebar_max_clicked (NULL, window);
+}
+
static gboolean
send_delete_event (gpointer data)
{
@@ -3919,6 +3931,73 @@ gtk_window_realize_icon (GtkWindow *window)
}
}
+static GdkPixbuf *
+icon_from_list (GList *list,
+ gint size)
+{
+ GdkPixbuf *best;
+ GdkPixbuf *pixbuf;
+ GList *l;
+
+ best = NULL;
+ for (l = list; l; l = l->next)
+ {
+ pixbuf = list->data;
+ if (gdk_pixbuf_get_width (pixbuf) <= size)
+ {
+ best = g_object_ref (pixbuf);
+ break;
+ }
+ }
+
+ if (best == NULL)
+ best = gdk_pixbuf_scale_simple (GDK_PIXBUF (list->data), size, size, GDK_INTERP_BILINEAR);
+
+ return best;
+}
+
+static GdkPixbuf *
+icon_from_name (const gchar *name,
+ gint size)
+{
+ return gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ name, size,
+ GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
+}
+
+GdkPixbuf *
+gtk_window_get_icon_for_size (GtkWindow *window,
+ gint size)
+{
+ GtkWindowPrivate *priv = window->priv;
+ GtkWindowIconInfo *info;
+ const gchar *name;
+
+ info = ensure_icon_info (window);
+
+ if (info->icon_list != NULL)
+ return icon_from_list (info->icon_list, size);
+
+ name = gtk_window_get_icon_name (window);
+ if (name != NULL)
+ return icon_from_name (name, size);
+
+ if (priv->transient_parent != NULL)
+ {
+ info = ensure_icon_info (priv->transient_parent);
+ if (info->icon_list)
+ return icon_from_list (info->icon_list, size);
+ }
+
+ if (default_icon_list != NULL)
+ return icon_from_list (default_icon_list, size);
+
+ if (default_icon_name != NULL)
+ return icon_from_name (default_icon_name, size);
+
+ return NULL;
+}
+
static void
gtk_window_unrealize_icon (GtkWindow *window)
{
diff --git a/gtk/gtkwindowprivate.h b/gtk/gtkwindowprivate.h
index 507d796..b7fc16a 100644
--- a/gtk/gtkwindowprivate.h
+++ b/gtk/gtkwindowprivate.h
@@ -65,6 +65,9 @@ void _gtk_window_set_allocation (GtkWindow *window,
const GtkAllocation *allocation,
GtkAllocation *allocation_out);
+gboolean _gtk_window_is_maximized (GtkWindow *window);
+void _gtk_window_toggle_maximized (GtkWindow *window);
+
typedef void (*GtkWindowKeysForeachFunc) (GtkWindow *window,
guint keyval,
GdkModifierType modifiers,
@@ -82,6 +85,9 @@ gboolean _gtk_window_query_nonaccels (GtkWindow *window,
void _gtk_window_schedule_mnemonics_visible (GtkWindow *window);
+GdkPixbuf *gtk_window_get_icon_for_size (GtkWindow *window,
+ gint size);
+
G_END_DECLS
#endif /* __GTK_WINDOW_PRIVATE_H__ */