1 /*
2  * Copyright (c) 2013 Red Hat, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
12  * License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  */
19 
20 #include "config.h"
21 
22 #include "gtkheaderbarprivate.h"
23 
24 #include "gtkbinlayout.h"
25 #include "gtkbox.h"
26 #include "gtkbuildable.h"
27 #include "gtkcenterbox.h"
28 #include "gtkintl.h"
29 #include "gtklabel.h"
30 #include "gtknative.h"
31 #include "gtkprivate.h"
32 #include "gtksizerequest.h"
33 #include "gtktypebuiltins.h"
34 #include "gtkwidgetprivate.h"
35 #include "gtkwindowcontrols.h"
36 #include "gtkwindowhandle.h"
37 
38 #include <string.h>
39 
40 /**
41  * GtkHeaderBar:
42  *
43  * `GtkHeaderBar` is a widget for creating custom title bars for windows.
44  *
45  * ![An example GtkHeaderBar](headerbar.png)
46  *
47  * `GtkHeaderBar` is similar to a horizontal `GtkCenterBox`. It allows
48  * children to be placed at the start or the end. In addition, it allows
49  * the window title to be displayed. The title will be centered with respect
50  * to the width of the box, even if the children at either side take up
51  * different amounts of space.
52  *
53  * `GtkHeaderBar` can add typical window frame controls, such as minimize,
54  * maximize and close buttons, or the window icon.
55  *
56  * For these reasons, `GtkHeaderBar` is the natural choice for use as the
57  * custom titlebar widget of a `GtkWindow (see [method@Gtk.Window.set_titlebar]),
58  * as it gives features typical of titlebars while allowing the addition of
59  * child widgets.
60  *
61  * ## GtkHeaderBar as GtkBuildable
62  *
63  * The `GtkHeaderBar` implementation of the `GtkBuildable` interface supports
64  * adding children at the start or end sides by specifying “start” or “end” as
65  * the “type” attribute of a <child> element, or setting the title widget by
66  * specifying “title” value.
67  *
68  * By default the `GtkHeaderBar` uses a `GtkLabel` displaying the title of the
69  * window it is contained in as the title widget, equivalent to the following
70  * UI definition:
71  *
72  * ```xml
73  * <object class="GtkHeaderBar">
74  *   <property name="title-widget">
75  *     <object class="GtkLabel">
76  *       <property name="label" translatable="yes">Label</property>
77  *       <property name="single-line-mode">True</property>
78  *       <property name="ellipsize">end</property>
79  *       <property name="width-chars">5</property>
80  *       <style>
81  *         <class name="title"/>
82  *       </style>
83  *     </object>
84  *   </property>
85  * </object>
86  * ```
87  *
88  * # CSS nodes
89  *
90  * ```
91  * headerbar
92  * ╰── windowhandle
93  *     ╰── box
94  *         ├── box.start
95  *         │   ├── windowcontrols.start
96  *         │   ╰── [other children]
97  *         ├── [Title Widget]
98  *         ╰── box.end
99  *             ├── [other children]
100  *             ╰── windowcontrols.end
101  * ```
102  *
103  * A `GtkHeaderBar`'s CSS node is called `headerbar`. It contains a `windowhandle`
104  * subnode, which contains a `box` subnode, which contains two `box` subnodes at
105  * the start and end of the header bar, as well as a center node that represents
106  * the title.
107  *
108  * Each of the boxes contains a `windowcontrols` subnode, see
109  * [class@Gtk.WindowControls] for details, as well as other children.
110  *
111  * # Accessibility
112  *
113  * `GtkHeaderBar` uses the %GTK_ACCESSIBLE_ROLE_GROUP role.
114  */
115 
116 #define MIN_TITLE_CHARS 5
117 
118 struct _GtkHeaderBar
119 {
120   GtkWidget container;
121 
122   GtkWidget *handle;
123   GtkWidget *center_box;
124   GtkWidget *start_box;
125   GtkWidget *end_box;
126 
127   GtkWidget *title_label;
128   GtkWidget *title_widget;
129 
130   GtkWidget *start_window_controls;
131   GtkWidget *end_window_controls;
132 
133   char *decoration_layout;
134 
135   guint show_title_buttons : 1;
136   guint track_default_decoration : 1;
137 };
138 
139 typedef struct _GtkHeaderBarClass GtkHeaderBarClass;
140 
141 struct _GtkHeaderBarClass
142 {
143   GtkWidgetClass parent_class;
144 };
145 
146 enum {
147   PROP_0,
148   PROP_TITLE_WIDGET,
149   PROP_SHOW_TITLE_BUTTONS,
150   PROP_DECORATION_LAYOUT,
151   LAST_PROP
152 };
153 
154 static GParamSpec *header_bar_props[LAST_PROP] = { NULL, };
155 
156 static void gtk_header_bar_buildable_init (GtkBuildableIface *iface);
157 
158 G_DEFINE_TYPE_WITH_CODE (GtkHeaderBar, gtk_header_bar, GTK_TYPE_WIDGET,
159                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
160                                                 gtk_header_bar_buildable_init));
161 
162 static void
create_window_controls(GtkHeaderBar * bar)163 create_window_controls (GtkHeaderBar *bar)
164 {
165   GtkWidget *controls;
166 
167   controls = gtk_window_controls_new (GTK_PACK_START);
168   g_object_bind_property (bar, "decoration-layout",
169                           controls, "decoration-layout",
170                           G_BINDING_SYNC_CREATE);
171   g_object_bind_property (controls, "empty",
172                           controls, "visible",
173                           G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
174   gtk_box_prepend (GTK_BOX (bar->start_box), controls);
175   bar->start_window_controls = controls;
176 
177   controls = gtk_window_controls_new (GTK_PACK_END);
178   g_object_bind_property (bar, "decoration-layout",
179                           controls, "decoration-layout",
180                           G_BINDING_SYNC_CREATE);
181   g_object_bind_property (controls, "empty",
182                           controls, "visible",
183                           G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
184   gtk_box_append (GTK_BOX (bar->end_box), controls);
185   bar->end_window_controls = controls;
186 }
187 
188 static void
update_default_decoration(GtkHeaderBar * bar)189 update_default_decoration (GtkHeaderBar *bar)
190 {
191   gboolean have_children = FALSE;
192 
193   /* Check whether we have any child widgets that we didn't add ourselves */
194   if (gtk_center_box_get_center_widget (GTK_CENTER_BOX (bar->center_box)) != NULL)
195     {
196       have_children = TRUE;
197     }
198   else
199     {
200       GtkWidget *w;
201 
202       for (w = _gtk_widget_get_first_child (bar->start_box);
203            w != NULL;
204            w = _gtk_widget_get_next_sibling (w))
205         {
206           if (w != bar->start_window_controls)
207             {
208               have_children = TRUE;
209               break;
210             }
211         }
212 
213       if (!have_children)
214         for (w = _gtk_widget_get_first_child (bar->end_box);
215              w != NULL;
216              w = _gtk_widget_get_next_sibling (w))
217           {
218             if (w != bar->end_window_controls)
219               {
220                 have_children = TRUE;
221                 break;
222               }
223           }
224     }
225 
226   if (have_children || bar->title_widget != NULL)
227     gtk_widget_remove_css_class (GTK_WIDGET (bar), "default-decoration");
228   else
229     gtk_widget_add_css_class (GTK_WIDGET (bar), "default-decoration");
230 }
231 
232 void
_gtk_header_bar_track_default_decoration(GtkHeaderBar * bar)233 _gtk_header_bar_track_default_decoration (GtkHeaderBar *bar)
234 {
235   bar->track_default_decoration = TRUE;
236 
237   update_default_decoration (bar);
238 }
239 
240 static void
update_title(GtkHeaderBar * bar)241 update_title (GtkHeaderBar *bar)
242 {
243   GtkRoot *root;
244   const char *title = NULL;
245 
246   if (!bar->title_label)
247     return;
248 
249   root = gtk_widget_get_root (GTK_WIDGET (bar));
250 
251   if (GTK_IS_WINDOW (root))
252     title = gtk_window_get_title (GTK_WINDOW (root));
253 
254   if (!title)
255     title = g_get_application_name ();
256 
257   if (!title)
258     title = g_get_prgname ();
259 
260   gtk_label_set_text (GTK_LABEL (bar->title_label), title);
261 }
262 
263 static void
construct_title_label(GtkHeaderBar * bar)264 construct_title_label (GtkHeaderBar *bar)
265 {
266   GtkWidget *label;
267 
268   g_assert (bar->title_label == NULL);
269 
270   label = gtk_label_new (NULL);
271   gtk_widget_add_css_class (label, "title");
272   gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
273   gtk_label_set_wrap (GTK_LABEL (label), FALSE);
274   gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE);
275   gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
276   gtk_label_set_width_chars (GTK_LABEL (label), MIN_TITLE_CHARS);
277   gtk_center_box_set_center_widget (GTK_CENTER_BOX (bar->center_box), label);
278 
279   bar->title_label = label;
280 
281   update_title (bar);
282 }
283 
284 /**
285  * gtk_header_bar_set_title_widget:
286  * @bar: a `GtkHeaderBar`
287  * @title_widget: (nullable): a widget to use for a title
288  *
289  * Sets the title for the `GtkHeaderBar`.
290  *
291  * When set to %NULL, the headerbar will display the title of
292  * the window it is contained in.
293  *
294  * The title should help a user identify the current view.
295  * To achieve the same style as the builtin title, use the
296  * “title” style class.
297  *
298  * You should set the title widget to %NULL, for the window
299  * title label to be visible again.
300  */
301 void
gtk_header_bar_set_title_widget(GtkHeaderBar * bar,GtkWidget * title_widget)302 gtk_header_bar_set_title_widget (GtkHeaderBar *bar,
303                                  GtkWidget    *title_widget)
304 {
305   g_return_if_fail (GTK_IS_HEADER_BAR (bar));
306   if (title_widget)
307     g_return_if_fail (GTK_IS_WIDGET (title_widget));
308 
309   /* No need to do anything if the title widget stays the same */
310   if (bar->title_widget == title_widget)
311     return;
312 
313   gtk_center_box_set_center_widget (GTK_CENTER_BOX (bar->center_box), NULL);
314   bar->title_widget = NULL;
315 
316   if (title_widget != NULL)
317     {
318       bar->title_widget = title_widget;
319 
320       gtk_center_box_set_center_widget (GTK_CENTER_BOX (bar->center_box), title_widget);
321 
322       bar->title_label = NULL;
323     }
324   else
325     {
326       if (bar->title_label == NULL)
327         construct_title_label (bar);
328     }
329 
330   g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_TITLE_WIDGET]);
331 }
332 
333 /**
334  * gtk_header_bar_get_title_widget:
335  * @bar: a `GtkHeaderBar`
336  *
337  * Retrieves the title widget of the header.
338  *
339  * See [method@Gtk.HeaderBar.set_title_widget].
340  *
341  * Returns: (nullable) (transfer none): the title widget of the header
342  */
343 GtkWidget *
gtk_header_bar_get_title_widget(GtkHeaderBar * bar)344 gtk_header_bar_get_title_widget (GtkHeaderBar *bar)
345 {
346   g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL);
347 
348   return bar->title_widget;
349 }
350 
351 static void
gtk_header_bar_root(GtkWidget * widget)352 gtk_header_bar_root (GtkWidget *widget)
353 {
354   GtkWidget *root;
355 
356   GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->root (widget);
357 
358   root = GTK_WIDGET (gtk_widget_get_root (widget));
359 
360   if (GTK_IS_WINDOW (root))
361     g_signal_connect_swapped (root, "notify::title",
362                               G_CALLBACK (update_title), widget);
363 
364   update_title (GTK_HEADER_BAR (widget));
365 }
366 
367 static void
gtk_header_bar_unroot(GtkWidget * widget)368 gtk_header_bar_unroot (GtkWidget *widget)
369 {
370   g_signal_handlers_disconnect_by_func (gtk_widget_get_root (widget),
371                                         update_title, widget);
372 
373   GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->unroot (widget);
374 }
375 
376 static void
gtk_header_bar_dispose(GObject * object)377 gtk_header_bar_dispose (GObject *object)
378 {
379   GtkHeaderBar *bar = GTK_HEADER_BAR (object);
380 
381   bar->title_widget = NULL;
382   bar->title_label = NULL;
383   bar->start_box = NULL;
384   bar->end_box = NULL;
385 
386   g_clear_pointer (&bar->handle, gtk_widget_unparent);
387 
388   G_OBJECT_CLASS (gtk_header_bar_parent_class)->dispose (object);
389 }
390 
391 static void
gtk_header_bar_finalize(GObject * object)392 gtk_header_bar_finalize (GObject *object)
393 {
394   GtkHeaderBar *bar = GTK_HEADER_BAR (object);
395 
396   g_free (bar->decoration_layout);
397 
398   G_OBJECT_CLASS (gtk_header_bar_parent_class)->finalize (object);
399 }
400 
401 static void
gtk_header_bar_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)402 gtk_header_bar_get_property (GObject    *object,
403                              guint       prop_id,
404                              GValue     *value,
405                              GParamSpec *pspec)
406 {
407   GtkHeaderBar *bar = GTK_HEADER_BAR (object);
408 
409   switch (prop_id)
410     {
411     case PROP_TITLE_WIDGET:
412       g_value_set_object (value, bar->title_widget);
413       break;
414 
415     case PROP_SHOW_TITLE_BUTTONS:
416       g_value_set_boolean (value, gtk_header_bar_get_show_title_buttons (bar));
417       break;
418 
419     case PROP_DECORATION_LAYOUT:
420       g_value_set_string (value, gtk_header_bar_get_decoration_layout (bar));
421       break;
422 
423     default:
424       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
425       break;
426     }
427 }
428 
429 static void
gtk_header_bar_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)430 gtk_header_bar_set_property (GObject      *object,
431                              guint         prop_id,
432                              const GValue *value,
433                              GParamSpec   *pspec)
434 {
435   GtkHeaderBar *bar = GTK_HEADER_BAR (object);
436 
437   switch (prop_id)
438     {
439     case PROP_TITLE_WIDGET:
440       gtk_header_bar_set_title_widget (bar, g_value_get_object (value));
441       break;
442 
443     case PROP_SHOW_TITLE_BUTTONS:
444       gtk_header_bar_set_show_title_buttons (bar, g_value_get_boolean (value));
445       break;
446 
447     case PROP_DECORATION_LAYOUT:
448       gtk_header_bar_set_decoration_layout (bar, g_value_get_string (value));
449       break;
450 
451     default:
452       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
453       break;
454     }
455 }
456 
457 static void
gtk_header_bar_pack(GtkHeaderBar * bar,GtkWidget * widget,GtkPackType pack_type)458 gtk_header_bar_pack (GtkHeaderBar *bar,
459                      GtkWidget    *widget,
460                      GtkPackType   pack_type)
461 {
462   g_return_if_fail (gtk_widget_get_parent (widget) == NULL);
463 
464   if (pack_type == GTK_PACK_START)
465     {
466       gtk_box_append (GTK_BOX (bar->start_box), widget);
467     }
468   else if (pack_type == GTK_PACK_END)
469     {
470       gtk_box_append (GTK_BOX (bar->end_box), widget);
471       gtk_box_reorder_child_after (GTK_BOX (bar->end_box), widget, NULL);
472     }
473 
474   if (bar->track_default_decoration)
475     update_default_decoration (bar);
476 }
477 
478 /**
479  * gtk_header_bar_remove:
480  * @bar: a `GtkHeaderBar`
481  * @child: the child to remove
482  *
483  * Removes a child from the `GtkHeaderBar`.
484  *
485  * The child must have been added with
486  * [method@Gtk.HeaderBar.pack_start],
487  * [method@Gtk.HeaderBar.pack_end] or
488  * [method@Gtk.HeaderBar.set_title_widget].
489  */
490 void
gtk_header_bar_remove(GtkHeaderBar * bar,GtkWidget * child)491 gtk_header_bar_remove (GtkHeaderBar *bar,
492                        GtkWidget    *child)
493 {
494   GtkWidget *parent;
495   gboolean removed = FALSE;
496 
497   parent = gtk_widget_get_parent (child);
498 
499   if (parent == bar->start_box)
500     {
501       gtk_box_remove (GTK_BOX (bar->start_box), child);
502       removed = TRUE;
503     }
504   else if (parent == bar->end_box)
505     {
506       gtk_box_remove (GTK_BOX (bar->end_box), child);
507       removed = TRUE;
508     }
509   else if (parent == bar->center_box)
510     {
511       gtk_center_box_set_center_widget (GTK_CENTER_BOX (bar->center_box), NULL);
512       removed = TRUE;
513     }
514 
515   if (removed && bar->track_default_decoration)
516     update_default_decoration (bar);
517 }
518 
519 static GtkSizeRequestMode
gtk_header_bar_get_request_mode(GtkWidget * widget)520 gtk_header_bar_get_request_mode (GtkWidget *widget)
521 {
522   GtkWidget *w;
523   int wfh = 0, hfw = 0;
524 
525   for (w = gtk_widget_get_first_child (widget);
526        w != NULL;
527        w = gtk_widget_get_next_sibling (w))
528     {
529       GtkSizeRequestMode mode = gtk_widget_get_request_mode (w);
530 
531       switch (mode)
532         {
533         case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH:
534           hfw ++;
535           break;
536         case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT:
537           wfh ++;
538           break;
539         case GTK_SIZE_REQUEST_CONSTANT_SIZE:
540         default:
541           break;
542         }
543     }
544 
545   if (hfw == 0 && wfh == 0)
546     return GTK_SIZE_REQUEST_CONSTANT_SIZE;
547   else
548     return wfh > hfw ?
549         GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
550         GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
551 }
552 
553 static void
gtk_header_bar_class_init(GtkHeaderBarClass * class)554 gtk_header_bar_class_init (GtkHeaderBarClass *class)
555 {
556   GObjectClass *object_class = G_OBJECT_CLASS (class);
557   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
558 
559   object_class->dispose = gtk_header_bar_dispose;
560   object_class->finalize = gtk_header_bar_finalize;
561   object_class->get_property = gtk_header_bar_get_property;
562   object_class->set_property = gtk_header_bar_set_property;
563 
564   widget_class->root = gtk_header_bar_root;
565   widget_class->unroot = gtk_header_bar_unroot;
566   widget_class->get_request_mode = gtk_header_bar_get_request_mode;
567 
568   header_bar_props[PROP_TITLE_WIDGET] =
569       g_param_spec_object ("title-widget",
570                            P_("Title Widget"),
571                            P_("Title widget to display"),
572                            GTK_TYPE_WIDGET,
573                            G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
574 
575   /**
576    * GtkHeaderBar:show-title-buttons: (attributes org.gtk.Property.get=gtk_header_bar_get_show_title_buttons org.gtk.Property.set=gtk_header_bar_set_show_title_buttons)
577    *
578    * Whether to show title buttons like close, minimize, maximize.
579    *
580    * Which buttons are actually shown and where is determined
581    * by the [property@Gtk.HeaderBar:decoration-layout] property,
582    * and by the state of the window (e.g. a close button will not
583    * be shown if the window can't be closed).
584    */
585   header_bar_props[PROP_SHOW_TITLE_BUTTONS] =
586       g_param_spec_boolean ("show-title-buttons",
587                             P_("Show title buttons"),
588                             P_("Whether to show title buttons"),
589                             TRUE,
590                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
591 
592   /**
593    * GtkHeaderBar:decoration-layout: (attributes org.gtk.Property.get=gtk_header_bar_get_decoration_layout org.gtk.Property.set=gtk_header_bar_set_decoration_layout)
594    *
595    * The decoration layout for buttons.
596    *
597    * If this property is not set, the
598    * [property@Gtk.Settings:gtk-decoration-layout] setting is used.
599    */
600   header_bar_props[PROP_DECORATION_LAYOUT] =
601       g_param_spec_string ("decoration-layout",
602                            P_("Decoration Layout"),
603                            P_("The layout for window decorations"),
604                            NULL,
605                            GTK_PARAM_READWRITE);
606 
607   g_object_class_install_properties (object_class, LAST_PROP, header_bar_props);
608 
609   gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
610   gtk_widget_class_set_css_name (widget_class, I_("headerbar"));
611   gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP);
612 }
613 
614 static void
gtk_header_bar_init(GtkHeaderBar * bar)615 gtk_header_bar_init (GtkHeaderBar *bar)
616 {
617   bar->title_widget = NULL;
618   bar->decoration_layout = NULL;
619   bar->show_title_buttons = TRUE;
620 
621   bar->handle = gtk_window_handle_new ();
622   gtk_widget_set_parent (bar->handle, GTK_WIDGET (bar));
623 
624   bar->center_box = gtk_center_box_new ();
625   gtk_window_handle_set_child (GTK_WINDOW_HANDLE (bar->handle), bar->center_box);
626 
627   bar->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
628   gtk_widget_add_css_class (bar->start_box, "start");
629   gtk_center_box_set_start_widget (GTK_CENTER_BOX (bar->center_box), bar->start_box);
630 
631   bar->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
632   gtk_widget_add_css_class (bar->end_box, "end");
633   gtk_center_box_set_end_widget (GTK_CENTER_BOX (bar->center_box), bar->end_box);
634 
635   construct_title_label (bar);
636   create_window_controls (bar);
637 }
638 
639 static GtkBuildableIface *parent_buildable_iface;
640 
641 static void
gtk_header_bar_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const char * type)642 gtk_header_bar_buildable_add_child (GtkBuildable *buildable,
643                                     GtkBuilder   *builder,
644                                     GObject      *child,
645                                     const char   *type)
646 {
647   if (g_strcmp0 (type, "title") == 0)
648     gtk_header_bar_set_title_widget (GTK_HEADER_BAR (buildable), GTK_WIDGET (child));
649   else if (g_strcmp0 (type, "start") == 0)
650     gtk_header_bar_pack_start (GTK_HEADER_BAR (buildable), GTK_WIDGET (child));
651   else if (g_strcmp0 (type, "end") == 0)
652     gtk_header_bar_pack_end (GTK_HEADER_BAR (buildable), GTK_WIDGET (child));
653   else if (type == NULL && GTK_IS_WIDGET (child))
654     gtk_header_bar_pack_start (GTK_HEADER_BAR (buildable), GTK_WIDGET (child));
655   else
656     parent_buildable_iface->add_child (buildable, builder, child, type);
657 }
658 
659 static void
gtk_header_bar_buildable_init(GtkBuildableIface * iface)660 gtk_header_bar_buildable_init (GtkBuildableIface *iface)
661 {
662   parent_buildable_iface = g_type_interface_peek_parent (iface);
663 
664   iface->add_child = gtk_header_bar_buildable_add_child;
665 }
666 
667 /**
668  * gtk_header_bar_pack_start:
669  * @bar: A `GtkHeaderBar`
670  * @child: the `GtkWidget` to be added to @bar
671  *
672  * Adds @child to @bar, packed with reference to the
673  * start of the @bar.
674  */
675 void
gtk_header_bar_pack_start(GtkHeaderBar * bar,GtkWidget * child)676 gtk_header_bar_pack_start (GtkHeaderBar *bar,
677                            GtkWidget    *child)
678 {
679   gtk_header_bar_pack (bar, child, GTK_PACK_START);
680 }
681 
682 /**
683  * gtk_header_bar_pack_end:
684  * @bar: A `GtkHeaderBar`
685  * @child: the `GtkWidget` to be added to @bar
686  *
687  * Adds @child to @bar, packed with reference to the
688  * end of the @bar.
689  */
690 void
gtk_header_bar_pack_end(GtkHeaderBar * bar,GtkWidget * child)691 gtk_header_bar_pack_end (GtkHeaderBar *bar,
692                          GtkWidget    *child)
693 {
694   gtk_header_bar_pack (bar, child, GTK_PACK_END);
695 }
696 
697 /**
698  * gtk_header_bar_new:
699  *
700  * Creates a new `GtkHeaderBar` widget.
701  *
702  * Returns: a new `GtkHeaderBar`
703  */
704 GtkWidget *
gtk_header_bar_new(void)705 gtk_header_bar_new (void)
706 {
707   return GTK_WIDGET (g_object_new (GTK_TYPE_HEADER_BAR, NULL));
708 }
709 
710 /**
711  * gtk_header_bar_get_show_title_buttons: (attributes org.gtk.Method.get_property=show-title-buttons)
712  * @bar: a `GtkHeaderBar`
713  *
714  * Returns whether this header bar shows the standard window
715  * title buttons.
716  *
717  * Returns: %TRUE if title buttons are shown
718  */
719 gboolean
gtk_header_bar_get_show_title_buttons(GtkHeaderBar * bar)720 gtk_header_bar_get_show_title_buttons (GtkHeaderBar *bar)
721 {
722   g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), FALSE);
723 
724   return bar->show_title_buttons;
725 }
726 
727 /**
728  * gtk_header_bar_set_show_title_buttons: (attributes org.gtk.Method.set_property=show-title-buttons)
729  * @bar: a `GtkHeaderBar`
730  * @setting: %TRUE to show standard title buttons
731  *
732  * Sets whether this header bar shows the standard window
733  * title buttons.
734  */
735 void
gtk_header_bar_set_show_title_buttons(GtkHeaderBar * bar,gboolean setting)736 gtk_header_bar_set_show_title_buttons (GtkHeaderBar *bar,
737                                        gboolean      setting)
738 {
739   g_return_if_fail (GTK_IS_HEADER_BAR (bar));
740 
741   setting = setting != FALSE;
742 
743   if (bar->show_title_buttons == setting)
744     return;
745 
746   bar->show_title_buttons = setting;
747 
748   if (setting)
749     create_window_controls (bar);
750   else
751     {
752       if (bar->start_box && bar->start_window_controls)
753         {
754           gtk_box_remove (GTK_BOX (bar->start_box), bar->start_window_controls);
755           bar->start_window_controls = NULL;
756         }
757 
758       if (bar->end_box && bar->end_window_controls)
759         {
760           gtk_box_remove (GTK_BOX (bar->end_box), bar->end_window_controls);
761           bar->end_window_controls = NULL;
762         }
763     }
764 
765   g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_SHOW_TITLE_BUTTONS]);
766 }
767 
768 /**
769  * gtk_header_bar_set_decoration_layout: (attributes org.gtk.Method.set_property=decoration-layout)
770  * @bar: a `GtkHeaderBar`
771  * @layout: (nullable): a decoration layout, or %NULL to unset the layout
772  *
773  * Sets the decoration layout for this header bar.
774  *
775  * This property overrides the
776  * [property@Gtk.Settings:gtk-decoration-layout] setting.
777  *
778  * There can be valid reasons for overriding the setting, such
779  * as a header bar design that does not allow for buttons to take
780  * room on the right, or only offers room for a single close button.
781  * Split header bars are another example for overriding the setting.
782  *
783  * The format of the string is button names, separated by commas.
784  * A colon separates the buttons that should appear on the left
785  * from those on the right. Recognized button names are minimize,
786  * maximize, close and icon (the window icon).
787  *
788  * For example, “icon:minimize,maximize,close” specifies a icon
789  * on the left, and minimize, maximize and close buttons on the right.
790  */
791 void
gtk_header_bar_set_decoration_layout(GtkHeaderBar * bar,const char * layout)792 gtk_header_bar_set_decoration_layout (GtkHeaderBar *bar,
793                                       const char   *layout)
794 {
795   g_return_if_fail (GTK_IS_HEADER_BAR (bar));
796 
797   g_free (bar->decoration_layout);
798   bar->decoration_layout = g_strdup (layout);
799 
800   g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_DECORATION_LAYOUT]);
801 }
802 
803 /**
804  * gtk_header_bar_get_decoration_layout: (attributes org.gtk.Method.get_property=decoration-layout)
805  * @bar: a `GtkHeaderBar`
806  *
807  * Gets the decoration layout of the `GtkHeaderBar`.
808  *
809  * Returns: (nullable): the decoration layout
810  */
811 const char *
gtk_header_bar_get_decoration_layout(GtkHeaderBar * bar)812 gtk_header_bar_get_decoration_layout (GtkHeaderBar *bar)
813 {
814   g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL);
815 
816   return bar->decoration_layout;
817 }
818