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, &center_min, &center_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, &center_min, &center_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);
+                                       &center_min, &center_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);
+                                       &center_min, &center_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__ */
