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 "gtkheaderbar.h"
23 #include "gtkheaderbarprivate.h"
24 #include "gtkintl.h"
25 #include "gtkprivate.h"
26 #include "gtktypebuiltins.h"
27 #include "gtkwidgetprivate.h"
28 #include "gtkcssnodeprivate.h"
29 #include "gtkcsscustomgadgetprivate.h"
30 #include "gtkwindowprivate.h"
31 #include "gtkwidgetprivate.h"
32 #include "gtkcontainerprivate.h"
33 #include "a11y/gtkheaderbaraccessible.h"
34
35 #include <string.h>
36
37 /**
38 * SECTION:gtkheaderbar
39 * @Short_description: A box with a centered child
40 * @Title: GtkHeaderBar
41 * @See_also: #GtkBox, #GtkActionBar
42 *
43 * GtkHeaderBar is similar to a horizontal #GtkBox. It allows children to
44 * be placed at the start or the end. In addition, it allows a title and
45 * subtitle to be displayed. The title will be centered with respect to
46 * the width of the box, even if the children at either side take up
47 * different amounts of space. The height of the titlebar will be
48 * set to provide sufficient space for the subtitle, even if none is
49 * currently set. If a subtitle is not needed, the space reservation
50 * can be turned off with gtk_header_bar_set_has_subtitle().
51 *
52 * GtkHeaderBar can add typical window frame controls, such as minimize,
53 * maximize and close buttons, or the window icon.
54 *
55 * For these reasons, GtkHeaderBar is the natural choice for use as the custom
56 * titlebar widget of a #GtkWindow (see gtk_window_set_titlebar()), as it gives
57 * features typical of titlebars while allowing the addition of child widgets.
58 */
59
60 #define DEFAULT_SPACING 6
61 #define MIN_TITLE_CHARS 5
62
63 struct _GtkHeaderBarPrivate
64 {
65 gchar *title;
66 gchar *subtitle;
67 GtkWidget *title_label;
68 GtkWidget *subtitle_label;
69 GtkWidget *label_box;
70 GtkWidget *label_sizing_box;
71 GtkWidget *subtitle_sizing_label;
72 GtkWidget *custom_title;
73 gint spacing;
74 gboolean has_subtitle;
75
76 GList *children;
77
78 gboolean shows_wm_decorations;
79 gchar *decoration_layout;
80 gboolean decoration_layout_set;
81
82 GtkWidget *titlebar_start_box;
83 GtkWidget *titlebar_end_box;
84
85 GtkWidget *titlebar_start_separator;
86 GtkWidget *titlebar_end_separator;
87
88 GtkWidget *titlebar_icon;
89
90 GtkCssGadget *gadget;
91 };
92
93 typedef struct _Child Child;
94 struct _Child
95 {
96 GtkWidget *widget;
97 GtkPackType pack_type;
98 };
99
100 enum {
101 PROP_0,
102 PROP_TITLE,
103 PROP_SUBTITLE,
104 PROP_HAS_SUBTITLE,
105 PROP_CUSTOM_TITLE,
106 PROP_SPACING,
107 PROP_SHOW_CLOSE_BUTTON,
108 PROP_DECORATION_LAYOUT,
109 PROP_DECORATION_LAYOUT_SET,
110 LAST_PROP
111 };
112
113 enum {
114 CHILD_PROP_0,
115 CHILD_PROP_PACK_TYPE,
116 CHILD_PROP_POSITION
117 };
118
119 static GParamSpec *header_bar_props[LAST_PROP] = { NULL, };
120
121 static void gtk_header_bar_buildable_init (GtkBuildableIface *iface);
122
123 G_DEFINE_TYPE_WITH_CODE (GtkHeaderBar, gtk_header_bar, GTK_TYPE_CONTAINER,
124 G_ADD_PRIVATE (GtkHeaderBar)
125 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
126 gtk_header_bar_buildable_init));
127
128 static void
init_sizing_box(GtkHeaderBar * bar)129 init_sizing_box (GtkHeaderBar *bar)
130 {
131 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
132 GtkWidget *w;
133 GtkStyleContext *context;
134
135 /* We use this box to always request size for the two labels (title
136 * and subtitle) as if they were always visible, but then allocate
137 * the real label box with its actual size, to keep it center-aligned
138 * in case we have only the title.
139 */
140 w = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
141 gtk_widget_show (w);
142 priv->label_sizing_box = g_object_ref_sink (w);
143
144 w = gtk_label_new (NULL);
145 gtk_widget_show (w);
146 context = gtk_widget_get_style_context (w);
147 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TITLE);
148 gtk_box_pack_start (GTK_BOX (priv->label_sizing_box), w, FALSE, FALSE, 0);
149 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
150 gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE);
151 gtk_label_set_ellipsize (GTK_LABEL (w), PANGO_ELLIPSIZE_END);
152 gtk_label_set_width_chars (GTK_LABEL (w), MIN_TITLE_CHARS);
153
154 w = gtk_label_new (NULL);
155 context = gtk_widget_get_style_context (w);
156 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SUBTITLE);
157 gtk_box_pack_start (GTK_BOX (priv->label_sizing_box), w, FALSE, FALSE, 0);
158 gtk_label_set_line_wrap (GTK_LABEL (w), FALSE);
159 gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE);
160 gtk_label_set_ellipsize (GTK_LABEL (w), PANGO_ELLIPSIZE_END);
161 gtk_widget_set_visible (w, priv->has_subtitle || (priv->subtitle && priv->subtitle[0]));
162 priv->subtitle_sizing_label = w;
163 }
164
165 static GtkWidget *
create_title_box(const char * title,const char * subtitle,GtkWidget ** ret_title_label,GtkWidget ** ret_subtitle_label)166 create_title_box (const char *title,
167 const char *subtitle,
168 GtkWidget **ret_title_label,
169 GtkWidget **ret_subtitle_label)
170 {
171 GtkWidget *label_box;
172 GtkWidget *title_label;
173 GtkWidget *subtitle_label;
174 GtkStyleContext *context;
175
176 label_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
177 gtk_widget_set_valign (label_box, GTK_ALIGN_CENTER);
178 gtk_widget_show (label_box);
179
180 title_label = gtk_label_new (title);
181 context = gtk_widget_get_style_context (title_label);
182 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TITLE);
183 gtk_label_set_line_wrap (GTK_LABEL (title_label), FALSE);
184 gtk_label_set_single_line_mode (GTK_LABEL (title_label), TRUE);
185 gtk_label_set_ellipsize (GTK_LABEL (title_label), PANGO_ELLIPSIZE_END);
186 gtk_box_pack_start (GTK_BOX (label_box), title_label, FALSE, FALSE, 0);
187 gtk_widget_show (title_label);
188 gtk_label_set_width_chars (GTK_LABEL (title_label), MIN_TITLE_CHARS);
189
190 subtitle_label = gtk_label_new (subtitle);
191 context = gtk_widget_get_style_context (subtitle_label);
192 gtk_style_context_add_class (context, GTK_STYLE_CLASS_SUBTITLE);
193 gtk_label_set_line_wrap (GTK_LABEL (subtitle_label), FALSE);
194 gtk_label_set_single_line_mode (GTK_LABEL (subtitle_label), TRUE);
195 gtk_label_set_ellipsize (GTK_LABEL (subtitle_label), PANGO_ELLIPSIZE_END);
196 gtk_box_pack_start (GTK_BOX (label_box), subtitle_label, FALSE, FALSE, 0);
197 gtk_widget_set_no_show_all (subtitle_label, TRUE);
198 gtk_widget_set_visible (subtitle_label, subtitle && subtitle[0]);
199
200 if (ret_title_label)
201 *ret_title_label = title_label;
202 if (ret_subtitle_label)
203 *ret_subtitle_label = subtitle_label;
204
205 return label_box;
206 }
207
208 gboolean
_gtk_header_bar_update_window_icon(GtkHeaderBar * bar,GtkWindow * window)209 _gtk_header_bar_update_window_icon (GtkHeaderBar *bar,
210 GtkWindow *window)
211 {
212 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
213 GdkPixbuf *pixbuf;
214 gint scale;
215
216 if (priv->titlebar_icon == NULL)
217 return FALSE;
218
219 scale = gtk_widget_get_scale_factor (priv->titlebar_icon);
220 if (GTK_IS_BUTTON (gtk_widget_get_parent (priv->titlebar_icon)))
221 pixbuf = gtk_window_get_icon_for_size (window, scale * 16);
222 else
223 pixbuf = gtk_window_get_icon_for_size (window, scale * 20);
224
225 if (pixbuf)
226 {
227 cairo_surface_t *surface;
228
229 surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, gtk_widget_get_window (priv->titlebar_icon));
230
231 gtk_image_set_from_surface (GTK_IMAGE (priv->titlebar_icon), surface);
232 cairo_surface_destroy (surface);
233 g_object_unref (pixbuf);
234 gtk_widget_show (priv->titlebar_icon);
235
236 return TRUE;
237 }
238
239 return FALSE;
240 }
241
242 static void
_gtk_header_bar_update_separator_visibility(GtkHeaderBar * bar)243 _gtk_header_bar_update_separator_visibility (GtkHeaderBar *bar)
244 {
245 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
246 gboolean have_visible_at_start = FALSE;
247 gboolean have_visible_at_end = FALSE;
248 GList *l;
249
250 for (l = priv->children; l != NULL; l = l->next)
251 {
252 Child *child = l->data;
253
254 if (gtk_widget_get_visible (child->widget))
255 {
256 if (child->pack_type == GTK_PACK_START)
257 have_visible_at_start = TRUE;
258 else
259 have_visible_at_end = TRUE;
260 }
261 }
262
263 if (priv->titlebar_start_separator != NULL)
264 gtk_widget_set_visible (priv->titlebar_start_separator, have_visible_at_start);
265
266 if (priv->titlebar_end_separator != NULL)
267 gtk_widget_set_visible (priv->titlebar_end_separator, have_visible_at_end);
268 }
269
270 void
_gtk_header_bar_update_window_buttons(GtkHeaderBar * bar)271 _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
272 {
273 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
274 GtkWidget *widget = GTK_WIDGET (bar), *toplevel;
275 GtkWindow *window;
276 GtkTextDirection direction;
277 gchar *layout_desc;
278 gchar **tokens, **t;
279 gint i, j;
280 GMenuModel *menu;
281 gboolean shown_by_shell;
282 gboolean is_sovereign_window;
283
284 toplevel = gtk_widget_get_toplevel (widget);
285 if (!gtk_widget_is_toplevel (toplevel))
286 return;
287
288 if (priv->titlebar_start_box)
289 {
290 gtk_widget_unparent (priv->titlebar_start_box);
291 priv->titlebar_start_box = NULL;
292 priv->titlebar_start_separator = NULL;
293 }
294 if (priv->titlebar_end_box)
295 {
296 gtk_widget_unparent (priv->titlebar_end_box);
297 priv->titlebar_end_box = NULL;
298 priv->titlebar_end_separator = NULL;
299 }
300
301 priv->titlebar_icon = NULL;
302
303 if (!priv->shows_wm_decorations)
304 return;
305
306 direction = gtk_widget_get_direction (widget);
307
308 g_object_get (gtk_widget_get_settings (widget),
309 "gtk-shell-shows-app-menu", &shown_by_shell,
310 "gtk-decoration-layout", &layout_desc,
311 NULL);
312
313 if (priv->decoration_layout_set)
314 {
315 g_free (layout_desc);
316 layout_desc = g_strdup (priv->decoration_layout);
317 }
318
319 window = GTK_WINDOW (toplevel);
320
321 if (!shown_by_shell && gtk_window_get_application (window))
322 menu = gtk_application_get_app_menu (gtk_window_get_application (window));
323 else
324 menu = NULL;
325
326 is_sovereign_window = (!gtk_window_get_modal (window) &&
327 gtk_window_get_transient_for (window) == NULL &&
328 gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL);
329
330 tokens = g_strsplit (layout_desc, ":", 2);
331 if (tokens)
332 {
333 for (i = 0; i < 2; i++)
334 {
335 GtkWidget *box;
336 GtkWidget *separator;
337 int n_children = 0;
338
339 if (tokens[i] == NULL)
340 break;
341
342 t = g_strsplit (tokens[i], ",", -1);
343
344 separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
345 gtk_widget_set_no_show_all (separator, TRUE);
346 gtk_style_context_add_class (gtk_widget_get_style_context (separator), "titlebutton");
347
348 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, priv->spacing);
349
350 for (j = 0; t[j]; j++)
351 {
352 GtkWidget *button = NULL;
353 GtkWidget *image = NULL;
354 AtkObject *accessible;
355
356 if (strcmp (t[j], "icon") == 0 &&
357 is_sovereign_window)
358 {
359 button = gtk_image_new ();
360 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
361 priv->titlebar_icon = button;
362 gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
363 gtk_style_context_add_class (gtk_widget_get_style_context (button), "icon");
364 gtk_widget_set_size_request (button, 20, 20);
365 gtk_widget_show (button);
366
367 if (!_gtk_header_bar_update_window_icon (bar, window))
368 {
369 gtk_widget_destroy (button);
370 priv->titlebar_icon = NULL;
371 button = NULL;
372 }
373 }
374 else if (strcmp (t[j], "menu") == 0 &&
375 menu != NULL &&
376 is_sovereign_window)
377 {
378 button = gtk_menu_button_new ();
379 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
380 gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), menu);
381 gtk_menu_button_set_use_popover (GTK_MENU_BUTTON (button), TRUE);
382 gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
383 gtk_style_context_add_class (gtk_widget_get_style_context (button), "appmenu");
384 image = gtk_image_new ();
385 gtk_container_add (GTK_CONTAINER (button), image);
386 gtk_widget_set_can_focus (button, FALSE);
387 gtk_widget_show_all (button);
388
389 accessible = gtk_widget_get_accessible (button);
390 if (GTK_IS_ACCESSIBLE (accessible))
391 atk_object_set_name (accessible, _("Application menu"));
392
393 priv->titlebar_icon = image;
394 if (!_gtk_header_bar_update_window_icon (bar, window))
395 gtk_image_set_from_icon_name (GTK_IMAGE (priv->titlebar_icon),
396 "application-x-executable-symbolic", GTK_ICON_SIZE_MENU);
397 }
398 else if (strcmp (t[j], "minimize") == 0 &&
399 is_sovereign_window)
400 {
401 button = gtk_button_new ();
402 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
403 gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
404 gtk_style_context_add_class (gtk_widget_get_style_context (button), "minimize");
405 image = gtk_image_new_from_icon_name ("window-minimize-symbolic", GTK_ICON_SIZE_MENU);
406 g_object_set (image, "use-fallback", TRUE, NULL);
407 gtk_container_add (GTK_CONTAINER (button), image);
408 gtk_widget_set_can_focus (button, FALSE);
409 gtk_widget_show_all (button);
410 g_signal_connect_swapped (button, "clicked",
411 G_CALLBACK (gtk_window_iconify), window);
412
413 accessible = gtk_widget_get_accessible (button);
414 if (GTK_IS_ACCESSIBLE (accessible))
415 atk_object_set_name (accessible, _("Minimize"));
416 }
417 else if (strcmp (t[j], "maximize") == 0 &&
418 gtk_window_get_resizable (window) &&
419 is_sovereign_window)
420 {
421 const gchar *icon_name;
422 gboolean maximized = gtk_window_is_maximized (window);
423
424 icon_name = maximized ? "window-restore-symbolic" : "window-maximize-symbolic";
425 button = gtk_button_new ();
426 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
427 gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
428 gtk_style_context_add_class (gtk_widget_get_style_context (button), "maximize");
429 image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
430 g_object_set (image, "use-fallback", TRUE, NULL);
431 gtk_container_add (GTK_CONTAINER (button), image);
432 gtk_widget_set_can_focus (button, FALSE);
433 gtk_widget_show_all (button);
434 g_signal_connect_swapped (button, "clicked",
435 G_CALLBACK (_gtk_window_toggle_maximized), window);
436
437 accessible = gtk_widget_get_accessible (button);
438 if (GTK_IS_ACCESSIBLE (accessible))
439 atk_object_set_name (accessible, maximized ? _("Restore") : _("Maximize"));
440 }
441 else if (strcmp (t[j], "close") == 0 &&
442 gtk_window_get_deletable (window))
443 {
444 button = gtk_button_new ();
445 gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
446 image = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_MENU);
447 gtk_style_context_add_class (gtk_widget_get_style_context (button), "titlebutton");
448 gtk_style_context_add_class (gtk_widget_get_style_context (button), "close");
449 g_object_set (image, "use-fallback", TRUE, NULL);
450 gtk_container_add (GTK_CONTAINER (button), image);
451 gtk_widget_set_can_focus (button, FALSE);
452 gtk_widget_show_all (button);
453 g_signal_connect_swapped (button, "clicked",
454 G_CALLBACK (gtk_window_close), window);
455
456 accessible = gtk_widget_get_accessible (button);
457 if (GTK_IS_ACCESSIBLE (accessible))
458 atk_object_set_name (accessible, _("Close"));
459 }
460
461 if (button)
462 {
463 gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
464 n_children ++;
465 }
466 }
467 g_strfreev (t);
468
469 if (n_children == 0)
470 {
471 g_object_ref_sink (box);
472 g_object_unref (box);
473 g_object_ref_sink (separator);
474 g_object_unref (separator);
475 continue;
476 }
477
478 gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, 0);
479 if (i == 1)
480 gtk_box_reorder_child (GTK_BOX (box), separator, 0);
481
482 if ((direction == GTK_TEXT_DIR_LTR && i == 0) ||
483 (direction == GTK_TEXT_DIR_RTL && i == 1))
484 {
485 gtk_style_context_add_class (gtk_widget_get_style_context (box), GTK_STYLE_CLASS_LEFT);
486 gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (bar)),
487 gtk_widget_get_css_node (box),
488 NULL);
489 }
490 else
491 {
492 gtk_style_context_add_class (gtk_widget_get_style_context (box), GTK_STYLE_CLASS_RIGHT);
493 gtk_css_node_insert_before (gtk_widget_get_css_node (GTK_WIDGET (bar)),
494 gtk_widget_get_css_node (box),
495 NULL);
496 }
497
498 gtk_widget_show (box);
499 gtk_widget_set_parent (box, GTK_WIDGET (bar));
500
501 if (i == 0)
502 {
503 priv->titlebar_start_box = box;
504 priv->titlebar_start_separator = separator;
505 }
506 else
507 {
508 priv->titlebar_end_box = box;
509 priv->titlebar_end_separator = separator;
510 }
511 }
512 g_strfreev (tokens);
513 }
514 g_free (layout_desc);
515
516 _gtk_header_bar_update_separator_visibility (bar);
517 }
518
519 gboolean
_gtk_header_bar_shows_app_menu(GtkHeaderBar * bar)520 _gtk_header_bar_shows_app_menu (GtkHeaderBar *bar)
521 {
522 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
523 GtkWindow *window;
524 gchar *layout_desc;
525 gboolean ret;
526
527 window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (bar)));
528 gtk_widget_style_get (GTK_WIDGET (window),
529 "decoration-button-layout", &layout_desc,
530 NULL);
531
532 ret = priv->shows_wm_decorations &&
533 (layout_desc && strstr (layout_desc, "menu"));
534
535 g_free (layout_desc);
536
537 return ret;
538 }
539
540 /* As an intended side effect, this function allows @child
541 * to be the title/label box */
542 static void
gtk_header_bar_reorder_css_node(GtkHeaderBar * bar,GtkPackType pack_type,GtkWidget * widget)543 gtk_header_bar_reorder_css_node (GtkHeaderBar *bar,
544 GtkPackType pack_type,
545 GtkWidget *widget)
546 {
547 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
548 GtkWidget *previous_widget;
549 GList *l;
550
551 if (pack_type == GTK_PACK_START)
552 previous_widget = priv->titlebar_start_box;
553 else
554 previous_widget = priv->titlebar_end_box;
555
556 for (l = priv->children; l; l = l->next)
557 {
558 Child *iter = l->data;
559
560 if (iter->widget == widget)
561 break;
562
563 if (iter->pack_type == pack_type)
564 previous_widget = iter->widget;
565 }
566
567 if ((pack_type == GTK_PACK_START)
568 ^ (gtk_widget_get_direction (GTK_WIDGET (bar)) == GTK_TEXT_DIR_LTR))
569 gtk_css_node_insert_after (gtk_widget_get_css_node (GTK_WIDGET (bar)),
570 gtk_widget_get_css_node (widget),
571 previous_widget ? gtk_widget_get_css_node (previous_widget) : NULL);
572 else
573 gtk_css_node_insert_before (gtk_widget_get_css_node (GTK_WIDGET (bar)),
574 gtk_widget_get_css_node (widget),
575 previous_widget ? gtk_widget_get_css_node (previous_widget) : NULL);
576 }
577
578 static void
construct_label_box(GtkHeaderBar * bar)579 construct_label_box (GtkHeaderBar *bar)
580 {
581 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
582
583 g_assert (priv->label_box == NULL);
584
585 priv->label_box = create_title_box (priv->title,
586 priv->subtitle,
587 &priv->title_label,
588 &priv->subtitle_label);
589 gtk_header_bar_reorder_css_node (bar, GTK_PACK_START, priv->label_box);
590 gtk_widget_set_parent (priv->label_box, GTK_WIDGET (bar));
591 }
592
593 static gint
count_visible_children(GtkHeaderBar * bar)594 count_visible_children (GtkHeaderBar *bar)
595 {
596 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
597 GList *l;
598 Child *child;
599 gint n;
600
601 n = 0;
602 for (l = priv->children; l; l = l->next)
603 {
604 child = l->data;
605 if (gtk_widget_get_visible (child->widget))
606 n++;
607 }
608
609 return n;
610 }
611
612 static gboolean
add_child_size(GtkWidget * child,GtkOrientation orientation,gint * minimum,gint * natural)613 add_child_size (GtkWidget *child,
614 GtkOrientation orientation,
615 gint *minimum,
616 gint *natural)
617 {
618 gint child_minimum, child_natural;
619
620 if (!gtk_widget_get_visible (child))
621 return FALSE;
622
623 if (orientation == GTK_ORIENTATION_HORIZONTAL)
624 gtk_widget_get_preferred_width (child, &child_minimum, &child_natural);
625 else
626 gtk_widget_get_preferred_height (child, &child_minimum, &child_natural);
627
628 if (GTK_ORIENTATION_HORIZONTAL == orientation)
629 {
630 *minimum += child_minimum;
631 *natural += child_natural;
632 }
633 else
634 {
635 *minimum = MAX (*minimum, child_minimum);
636 *natural = MAX (*natural, child_natural);
637 }
638
639 return TRUE;
640 }
641
642 static void
gtk_header_bar_get_size(GtkWidget * widget,GtkOrientation orientation,gint * minimum_size,gint * natural_size)643 gtk_header_bar_get_size (GtkWidget *widget,
644 GtkOrientation orientation,
645 gint *minimum_size,
646 gint *natural_size)
647 {
648 GtkHeaderBar *bar = GTK_HEADER_BAR (widget);
649 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
650 GList *l;
651 gint nvis_children;
652 gint minimum, natural;
653 gint center_min, center_nat;
654
655 minimum = natural = 0;
656 nvis_children = 0;
657
658 for (l = priv->children; l; l = l->next)
659 {
660 Child *child = l->data;
661
662 if (add_child_size (child->widget, orientation, &minimum, &natural))
663 nvis_children += 1;
664 }
665
666 center_min = center_nat = 0;
667 if (priv->label_box != NULL)
668 {
669 if (orientation == GTK_ORIENTATION_HORIZONTAL)
670 add_child_size (priv->label_box, orientation, ¢er_min, ¢er_nat);
671 else
672 add_child_size (priv->label_sizing_box, orientation, ¢er_min, ¢er_nat);
673
674 if (_gtk_widget_get_visible (priv->label_sizing_box))
675 nvis_children += 1;
676 }
677
678 if (priv->custom_title != NULL)
679 {
680 if (add_child_size (priv->custom_title, orientation, ¢er_min, ¢er_nat))
681 nvis_children += 1;
682 }
683
684 if (priv->titlebar_start_box != NULL)
685 {
686 if (add_child_size (priv->titlebar_start_box, orientation, &minimum, &natural))
687 nvis_children += 1;
688 }
689
690 if (priv->titlebar_end_box != NULL)
691 {
692 if (add_child_size (priv->titlebar_end_box, orientation, &minimum, &natural))
693 nvis_children += 1;
694 }
695
696 if (orientation == GTK_ORIENTATION_HORIZONTAL)
697 {
698 minimum += center_min;
699 natural += center_nat;
700 }
701 else
702 {
703 minimum = MAX (minimum, center_min);
704 natural = MAX (natural, center_nat);
705 }
706
707 if (nvis_children > 0 && orientation == GTK_ORIENTATION_HORIZONTAL)
708 {
709 minimum += nvis_children * priv->spacing;
710 natural += nvis_children * priv->spacing;
711 }
712
713 *minimum_size = minimum;
714 *natural_size = natural;
715 }
716
717 static void
gtk_header_bar_compute_size_for_orientation(GtkWidget * widget,gint avail_size,gint * minimum_size,gint * natural_size)718 gtk_header_bar_compute_size_for_orientation (GtkWidget *widget,
719 gint avail_size,
720 gint *minimum_size,
721 gint *natural_size)
722 {
723 GtkHeaderBar *bar = GTK_HEADER_BAR (widget);
724 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
725 GList *children;
726 gint required_size = 0;
727 gint required_natural = 0;
728 gint child_size;
729 gint child_natural;
730 gint nvis_children;
731
732 nvis_children = 0;
733
734 for (children = priv->children; children != NULL; children = children->next)
735 {
736 Child *child = children->data;
737
738 if (gtk_widget_get_visible (child->widget))
739 {
740 gtk_widget_get_preferred_width_for_height (child->widget,
741 avail_size, &child_size, &child_natural);
742
743 required_size += child_size;
744 required_natural += child_natural;
745
746 nvis_children += 1;
747 }
748 }
749
750 if (priv->label_box != NULL)
751 {
752 gtk_widget_get_preferred_width (priv->label_sizing_box,
753 &child_size, &child_natural);
754 required_size += child_size;
755 required_natural += child_natural;
756 }
757
758 if (priv->custom_title != NULL &&
759 gtk_widget_get_visible (priv->custom_title))
760 {
761 gtk_widget_get_preferred_width (priv->custom_title,
762 &child_size, &child_natural);
763 required_size += child_size;
764 required_natural += child_natural;
765 }
766
767 if (priv->titlebar_start_box != NULL)
768 {
769 gtk_widget_get_preferred_width (priv->titlebar_start_box,
770 &child_size, &child_natural);
771 required_size += child_size;
772 required_natural += child_natural;
773 nvis_children += 1;
774 }
775
776 if (priv->titlebar_end_box != NULL)
777 {
778 gtk_widget_get_preferred_width (priv->titlebar_end_box,
779 &child_size, &child_natural);
780 required_size += child_size;
781 required_natural += child_natural;
782 nvis_children += 1;
783 }
784
785 if (nvis_children > 0)
786 {
787 required_size += nvis_children * priv->spacing;
788 required_natural += nvis_children * priv->spacing;
789 }
790
791 *minimum_size = required_size;
792 *natural_size = required_natural;
793 }
794
795 static void
gtk_header_bar_compute_size_for_opposing_orientation(GtkWidget * widget,gint avail_size,gint * minimum_size,gint * natural_size)796 gtk_header_bar_compute_size_for_opposing_orientation (GtkWidget *widget,
797 gint avail_size,
798 gint *minimum_size,
799 gint *natural_size)
800 {
801 GtkHeaderBar *bar = GTK_HEADER_BAR (widget);
802 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
803 Child *child;
804 GList *children;
805 gint nvis_children;
806 gint computed_minimum = 0;
807 gint computed_natural = 0;
808 GtkRequestedSize *sizes;
809 GtkPackType packing;
810 gint size = 0;
811 gint i;
812 gint child_size;
813 gint child_minimum;
814 gint child_natural;
815 gint center_min, center_nat;
816
817 nvis_children = count_visible_children (bar);
818
819 if (nvis_children <= 0)
820 return;
821
822 sizes = g_newa (GtkRequestedSize, nvis_children);
823
824 /* Retrieve desired size for visible children */
825 for (i = 0, children = priv->children; children; children = children->next)
826 {
827 child = children->data;
828
829 if (gtk_widget_get_visible (child->widget))
830 {
831 gtk_widget_get_preferred_width (child->widget,
832 &sizes[i].minimum_size,
833 &sizes[i].natural_size);
834
835 size -= sizes[i].minimum_size;
836 sizes[i].data = child;
837 i += 1;
838 }
839 }
840
841 /* Bring children up to size first */
842 size = gtk_distribute_natural_allocation (MAX (0, avail_size), nvis_children, sizes);
843
844 /* Allocate child positions. */
845 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; ++packing)
846 {
847 for (i = 0, children = priv->children; children; children = children->next)
848 {
849 child = children->data;
850
851 /* If widget is not visible, skip it. */
852 if (!gtk_widget_get_visible (child->widget))
853 continue;
854
855 /* If widget is packed differently skip it, but still increment i,
856 * since widget is visible and will be handled in next loop
857 * iteration.
858 */
859 if (child->pack_type != packing)
860 {
861 i++;
862 continue;
863 }
864
865 child_size = sizes[i].minimum_size;
866
867 gtk_widget_get_preferred_height_for_width (child->widget,
868 child_size, &child_minimum, &child_natural);
869
870 computed_minimum = MAX (computed_minimum, child_minimum);
871 computed_natural = MAX (computed_natural, child_natural);
872 }
873 i += 1;
874 }
875
876 center_min = center_nat = 0;
877 if (priv->label_box != NULL)
878 {
879 gtk_widget_get_preferred_height (priv->label_sizing_box,
880 ¢er_min, ¢er_nat);
881 }
882
883 if (priv->custom_title != NULL &&
884 gtk_widget_get_visible (priv->custom_title))
885 {
886 gtk_widget_get_preferred_height (priv->custom_title,
887 ¢er_min, ¢er_nat);
888 }
889
890 if (priv->titlebar_start_box != NULL)
891 {
892 gtk_widget_get_preferred_height (priv->titlebar_start_box,
893 &child_minimum, &child_natural);
894 computed_minimum = MAX (computed_minimum, child_minimum);
895 computed_natural = MAX (computed_natural, child_natural);
896 }
897
898 if (priv->titlebar_end_box != NULL)
899 {
900 gtk_widget_get_preferred_height (priv->titlebar_end_box,
901 &child_minimum, &child_natural);
902 computed_minimum = MAX (computed_minimum, child_minimum);
903 computed_natural = MAX (computed_natural, child_natural);
904 }
905
906 *minimum_size = computed_minimum;
907 *natural_size = computed_natural;
908 }
909
910 static void
gtk_header_bar_get_content_size(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline,gpointer unused)911 gtk_header_bar_get_content_size (GtkCssGadget *gadget,
912 GtkOrientation orientation,
913 gint for_size,
914 gint *minimum,
915 gint *natural,
916 gint *minimum_baseline,
917 gint *natural_baseline,
918 gpointer unused)
919 {
920 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
921
922 if (for_size < 0)
923 gtk_header_bar_get_size (widget, orientation, minimum, natural);
924 else if (orientation == GTK_ORIENTATION_HORIZONTAL)
925 gtk_header_bar_compute_size_for_orientation (widget, for_size, minimum, natural);
926 else
927 gtk_header_bar_compute_size_for_opposing_orientation (widget, for_size, minimum, natural);
928 }
929
930 static void
gtk_header_bar_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)931 gtk_header_bar_get_preferred_width (GtkWidget *widget,
932 gint *minimum,
933 gint *natural)
934 {
935 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (widget));
936
937 gtk_css_gadget_get_preferred_size (priv->gadget,
938 GTK_ORIENTATION_HORIZONTAL,
939 -1,
940 minimum, natural,
941 NULL, NULL);
942 }
943
944 static void
gtk_header_bar_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)945 gtk_header_bar_get_preferred_height (GtkWidget *widget,
946 gint *minimum,
947 gint *natural)
948 {
949 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (widget));
950
951 gtk_css_gadget_get_preferred_size (priv->gadget,
952 GTK_ORIENTATION_VERTICAL,
953 -1,
954 minimum, natural,
955 NULL, NULL);
956 }
957
958 static void
gtk_header_bar_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum,gint * natural)959 gtk_header_bar_get_preferred_width_for_height (GtkWidget *widget,
960 gint height,
961 gint *minimum,
962 gint *natural)
963 {
964 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (widget));
965
966 gtk_css_gadget_get_preferred_size (priv->gadget,
967 GTK_ORIENTATION_HORIZONTAL,
968 height,
969 minimum, natural,
970 NULL, NULL);
971 }
972
973 static void
gtk_header_bar_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural)974 gtk_header_bar_get_preferred_height_for_width (GtkWidget *widget,
975 gint width,
976 gint *minimum,
977 gint *natural)
978 {
979 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (widget));
980
981 gtk_css_gadget_get_preferred_size (priv->gadget,
982 GTK_ORIENTATION_VERTICAL,
983 width,
984 minimum, natural,
985 NULL, NULL);
986 }
987
988 static void
gtk_header_bar_size_allocate(GtkWidget * widget,GtkAllocation * allocation)989 gtk_header_bar_size_allocate (GtkWidget *widget,
990 GtkAllocation *allocation)
991 {
992 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (widget));
993 GtkAllocation clip;
994
995 gtk_widget_set_allocation (widget, allocation);
996
997 gtk_css_gadget_allocate (priv->gadget, allocation, gtk_widget_get_allocated_baseline (widget), &clip);
998
999 gtk_widget_set_clip (widget, &clip);
1000 }
1001
1002 static void
gtk_header_bar_allocate_contents(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer unused)1003 gtk_header_bar_allocate_contents (GtkCssGadget *gadget,
1004 const GtkAllocation *allocation,
1005 int baseline,
1006 GtkAllocation *out_clip,
1007 gpointer unused)
1008 {
1009 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1010 GtkWidget *title_widget;
1011 GtkHeaderBar *bar = GTK_HEADER_BAR (widget);
1012 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1013 GtkRequestedSize *sizes;
1014 gint width, height;
1015 gint nvis_children;
1016 gint title_minimum_size;
1017 gint title_natural_size;
1018 gboolean title_expands = FALSE;
1019 gint start_width, end_width;
1020 gint uniform_expand_bonus[2] = { 0 };
1021 gint leftover_expand_bonus[2] = { 0 };
1022 gint nexpand_children[2] = { 0 };
1023 gint side[2];
1024 GList *l;
1025 gint i;
1026 Child *child;
1027 GtkPackType packing;
1028 GtkAllocation child_allocation;
1029 gint x;
1030 gint child_size;
1031 GtkTextDirection direction;
1032
1033 direction = gtk_widget_get_direction (widget);
1034 nvis_children = count_visible_children (bar);
1035 sizes = g_newa (GtkRequestedSize, nvis_children);
1036
1037 width = allocation->width - nvis_children * priv->spacing;
1038 height = allocation->height;
1039
1040 i = 0;
1041 for (l = priv->children; l; l = l->next)
1042 {
1043 child = l->data;
1044 if (!gtk_widget_get_visible (child->widget))
1045 continue;
1046
1047 if (gtk_widget_compute_expand (child->widget, GTK_ORIENTATION_HORIZONTAL))
1048 nexpand_children[child->pack_type]++;
1049
1050 gtk_widget_get_preferred_width_for_height (child->widget,
1051 height,
1052 &sizes[i].minimum_size,
1053 &sizes[i].natural_size);
1054 width -= sizes[i].minimum_size;
1055 i++;
1056 }
1057
1058 title_minimum_size = 0;
1059 title_natural_size = 0;
1060
1061 if (priv->custom_title != NULL &&
1062 gtk_widget_get_visible (priv->custom_title))
1063 title_widget = priv->custom_title;
1064 else if (priv->label_box != NULL)
1065 title_widget = priv->label_box;
1066 else
1067 title_widget = NULL;
1068
1069 if (title_widget)
1070 {
1071 gtk_widget_get_preferred_width_for_height (title_widget,
1072 height,
1073 &title_minimum_size,
1074 &title_natural_size);
1075 width -= title_natural_size;
1076
1077 title_expands = gtk_widget_compute_expand (title_widget, GTK_ORIENTATION_HORIZONTAL);
1078 }
1079
1080 start_width = 0;
1081 if (priv->titlebar_start_box != NULL)
1082 {
1083 gint min, nat;
1084 gtk_widget_get_preferred_width_for_height (priv->titlebar_start_box,
1085 height,
1086 &min, &nat);
1087 start_width = nat + priv->spacing;
1088 }
1089 width -= start_width;
1090
1091 end_width = 0;
1092 if (priv->titlebar_end_box != NULL)
1093 {
1094 gint min, nat;
1095 gtk_widget_get_preferred_width_for_height (priv->titlebar_end_box,
1096 height,
1097 &min, &nat);
1098 end_width = nat + priv->spacing;
1099 }
1100 width -= end_width;
1101
1102 width = gtk_distribute_natural_allocation (MAX (0, width), nvis_children, sizes);
1103
1104 /* compute the nominal size of the children filling up each side of
1105 * the title in titlebar
1106 */
1107 side[0] = start_width;
1108 side[1] = end_width;
1109 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; packing++)
1110 {
1111 i = 0;
1112 for (l = priv->children; l != NULL; l = l->next)
1113 {
1114 child = l->data;
1115 if (!gtk_widget_get_visible (child->widget))
1116 continue;
1117
1118 if (child->pack_type == packing)
1119 side[packing] += sizes[i].minimum_size + priv->spacing;
1120
1121 i++;
1122 }
1123 }
1124
1125 /* figure out how much space is left on each side of the title,
1126 * and earkmark that space for the expanded children.
1127 *
1128 * If the title itself is expanded, then it gets half the spoils
1129 * from each side.
1130 */
1131 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; packing++)
1132 {
1133 gint side_free_space;
1134
1135 side_free_space = allocation->width / 2 - title_natural_size / 2 - side[packing];
1136
1137 if (side_free_space > 0 && nexpand_children[packing] > 0)
1138 {
1139 width -= side_free_space;
1140
1141 if (title_expands)
1142 side_free_space -= side_free_space / 2;
1143
1144 side[packing] += side_free_space;
1145 uniform_expand_bonus[packing] = side_free_space / nexpand_children[packing];
1146 leftover_expand_bonus[packing] = side_free_space % nexpand_children[packing];
1147 }
1148 }
1149
1150 /* allocate the children on both sides of the title */
1151 for (packing = GTK_PACK_START; packing <= GTK_PACK_END; packing++)
1152 {
1153 child_allocation.y = allocation->y;
1154 child_allocation.height = height;
1155 if (packing == GTK_PACK_START)
1156 x = allocation->x + start_width;
1157 else
1158 x = allocation->x + allocation->width - end_width;
1159
1160 i = 0;
1161 for (l = priv->children; l != NULL; l = l->next)
1162 {
1163 child = l->data;
1164 if (!gtk_widget_get_visible (child->widget))
1165 continue;
1166
1167 if (child->pack_type != packing)
1168 goto next;
1169
1170 child_size = sizes[i].minimum_size;
1171
1172 /* if this child is expanded, give it extra space from the reserves */
1173 if (gtk_widget_compute_expand (child->widget, GTK_ORIENTATION_HORIZONTAL))
1174 {
1175 gint expand_bonus;
1176
1177 expand_bonus = uniform_expand_bonus[packing];
1178
1179 if (leftover_expand_bonus[packing] > 0)
1180 {
1181 expand_bonus++;
1182 leftover_expand_bonus[packing]--;
1183 }
1184
1185 child_size += expand_bonus;
1186 }
1187
1188 child_allocation.width = child_size;
1189
1190 if (packing == GTK_PACK_START)
1191 {
1192 child_allocation.x = x;
1193 x += child_size;
1194 x += priv->spacing;
1195 }
1196 else
1197 {
1198 x -= child_size;
1199 child_allocation.x = x;
1200 x -= priv->spacing;
1201 }
1202
1203 if (direction == GTK_TEXT_DIR_RTL)
1204 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
1205
1206 gtk_widget_size_allocate (child->widget, &child_allocation);
1207
1208 next:
1209 i++;
1210 }
1211 }
1212
1213 /* We don't enforce css borders on the center widget, to make
1214 * title/subtitle combinations fit without growing the header
1215 */
1216 child_allocation.y = allocation->y;
1217 child_allocation.height = allocation->height;
1218
1219 child_size = MIN (allocation->width - side[0] - side[1], title_natural_size);
1220
1221 child_allocation.x = allocation->x + (allocation->width - child_size) / 2;
1222 child_allocation.width = child_size;
1223
1224 /* if the title widget is expanded, then grow it by all the available
1225 * free space, and recenter it
1226 */
1227 if (title_expands && width > 0)
1228 {
1229 child_allocation.width += width;
1230 child_allocation.x -= width / 2;
1231 }
1232
1233 if (allocation->x + side[0] > child_allocation.x)
1234 child_allocation.x = allocation->x + side[0];
1235 else if (allocation->x + allocation->width - side[1] < child_allocation.x + child_allocation.width)
1236 child_allocation.x = allocation->x + allocation->width - side[1] - child_allocation.width;
1237
1238 if (direction == GTK_TEXT_DIR_RTL)
1239 child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
1240
1241 if (title_widget != NULL)
1242 gtk_widget_size_allocate (title_widget, &child_allocation);
1243
1244 child_allocation.y = allocation->y;
1245 child_allocation.height = height;
1246
1247 if (priv->titlebar_start_box)
1248 {
1249 gboolean left = (direction == GTK_TEXT_DIR_LTR);
1250 if (left)
1251 child_allocation.x = allocation->x;
1252 else
1253 child_allocation.x = allocation->x + allocation->width - start_width + priv->spacing;
1254 child_allocation.width = start_width - priv->spacing;
1255 gtk_widget_size_allocate (priv->titlebar_start_box, &child_allocation);
1256 }
1257
1258 if (priv->titlebar_end_box)
1259 {
1260 gboolean left = (direction != GTK_TEXT_DIR_LTR);
1261 if (left)
1262 child_allocation.x = allocation->x;
1263 else
1264 child_allocation.x = allocation->x + allocation->width - end_width + priv->spacing;
1265 child_allocation.width = end_width - priv->spacing;
1266 gtk_widget_size_allocate (priv->titlebar_end_box, &child_allocation);
1267 }
1268
1269 gtk_container_get_children_clip (GTK_CONTAINER (widget), out_clip);
1270 }
1271
1272 /**
1273 * gtk_header_bar_set_title:
1274 * @bar: a #GtkHeaderBar
1275 * @title: (allow-none): a title, or %NULL
1276 *
1277 * Sets the title of the #GtkHeaderBar. The title should help a user
1278 * identify the current view. A good title should not include the
1279 * application name.
1280 *
1281 * Since: 3.10
1282 */
1283 void
gtk_header_bar_set_title(GtkHeaderBar * bar,const gchar * title)1284 gtk_header_bar_set_title (GtkHeaderBar *bar,
1285 const gchar *title)
1286 {
1287 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1288 gchar *new_title;
1289
1290 g_return_if_fail (GTK_IS_HEADER_BAR (bar));
1291
1292 new_title = g_strdup (title);
1293 g_free (priv->title);
1294 priv->title = new_title;
1295
1296 if (priv->title_label != NULL)
1297 {
1298 gtk_label_set_label (GTK_LABEL (priv->title_label), priv->title);
1299 gtk_widget_queue_resize (GTK_WIDGET (bar));
1300 }
1301
1302 g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_TITLE]);
1303 }
1304
1305 /**
1306 * gtk_header_bar_get_title:
1307 * @bar: a #GtkHeaderBar
1308 *
1309 * Retrieves the title of the header. See gtk_header_bar_set_title().
1310 *
1311 * Returns: (nullable): the title of the header, or %NULL if none has
1312 * been set explicitly. The returned string is owned by the widget
1313 * and must not be modified or freed.
1314 *
1315 * Since: 3.10
1316 */
1317 const gchar *
gtk_header_bar_get_title(GtkHeaderBar * bar)1318 gtk_header_bar_get_title (GtkHeaderBar *bar)
1319 {
1320 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1321
1322 g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL);
1323
1324 return priv->title;
1325 }
1326
1327 /**
1328 * gtk_header_bar_set_subtitle:
1329 * @bar: a #GtkHeaderBar
1330 * @subtitle: (allow-none): a subtitle, or %NULL
1331 *
1332 * Sets the subtitle of the #GtkHeaderBar. The title should give a user
1333 * an additional detail to help him identify the current view.
1334 *
1335 * Note that GtkHeaderBar by default reserves room for the subtitle,
1336 * even if none is currently set. If this is not desired, set the
1337 * #GtkHeaderBar:has-subtitle property to %FALSE.
1338 *
1339 * Since: 3.10
1340 */
1341 void
gtk_header_bar_set_subtitle(GtkHeaderBar * bar,const gchar * subtitle)1342 gtk_header_bar_set_subtitle (GtkHeaderBar *bar,
1343 const gchar *subtitle)
1344 {
1345 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1346 gchar *new_subtitle;
1347
1348 g_return_if_fail (GTK_IS_HEADER_BAR (bar));
1349
1350 new_subtitle = g_strdup (subtitle);
1351 g_free (priv->subtitle);
1352 priv->subtitle = new_subtitle;
1353
1354 if (priv->subtitle_label != NULL)
1355 {
1356 gtk_label_set_label (GTK_LABEL (priv->subtitle_label), priv->subtitle);
1357 gtk_widget_set_visible (priv->subtitle_label, priv->subtitle && priv->subtitle[0]);
1358 gtk_widget_queue_resize (GTK_WIDGET (bar));
1359 }
1360
1361 gtk_widget_set_visible (priv->subtitle_sizing_label, priv->has_subtitle || (priv->subtitle && priv->subtitle[0]));
1362
1363 g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_SUBTITLE]);
1364 }
1365
1366 /**
1367 * gtk_header_bar_get_subtitle:
1368 * @bar: a #GtkHeaderBar
1369 *
1370 * Retrieves the subtitle of the header. See gtk_header_bar_set_subtitle().
1371 *
1372 * Returns: (nullable): the subtitle of the header, or %NULL if none has
1373 * been set explicitly. The returned string is owned by the widget
1374 * and must not be modified or freed.
1375 *
1376 * Since: 3.10
1377 */
1378 const gchar *
gtk_header_bar_get_subtitle(GtkHeaderBar * bar)1379 gtk_header_bar_get_subtitle (GtkHeaderBar *bar)
1380 {
1381 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1382
1383 g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL);
1384
1385 return priv->subtitle;
1386 }
1387
1388 /**
1389 * gtk_header_bar_set_custom_title:
1390 * @bar: a #GtkHeaderBar
1391 * @title_widget: (allow-none): a custom widget to use for a title
1392 *
1393 * Sets a custom title for the #GtkHeaderBar.
1394 *
1395 * The title should help a user identify the current view. This
1396 * supersedes any title set by gtk_header_bar_set_title() or
1397 * gtk_header_bar_set_subtitle(). To achieve the same style as
1398 * the builtin title and subtitle, use the “title” and “subtitle”
1399 * style classes.
1400 *
1401 * You should set the custom title to %NULL, for the header title
1402 * label to be visible again.
1403 *
1404 * Since: 3.10
1405 */
1406 void
gtk_header_bar_set_custom_title(GtkHeaderBar * bar,GtkWidget * title_widget)1407 gtk_header_bar_set_custom_title (GtkHeaderBar *bar,
1408 GtkWidget *title_widget)
1409 {
1410 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1411
1412 g_return_if_fail (GTK_IS_HEADER_BAR (bar));
1413 if (title_widget)
1414 g_return_if_fail (GTK_IS_WIDGET (title_widget));
1415
1416 /* No need to do anything if the custom widget stays the same */
1417 if (priv->custom_title == title_widget)
1418 return;
1419
1420 if (priv->custom_title)
1421 {
1422 GtkWidget *custom = priv->custom_title;
1423
1424 priv->custom_title = NULL;
1425 gtk_widget_unparent (custom);
1426 }
1427
1428 if (title_widget != NULL)
1429 {
1430 priv->custom_title = title_widget;
1431
1432 gtk_header_bar_reorder_css_node (bar, GTK_PACK_START, priv->custom_title);
1433 gtk_widget_set_parent (priv->custom_title, GTK_WIDGET (bar));
1434 gtk_widget_set_valign (priv->custom_title, GTK_ALIGN_CENTER);
1435
1436 if (priv->label_box != NULL)
1437 {
1438 GtkWidget *label_box = priv->label_box;
1439
1440 priv->label_box = NULL;
1441 priv->title_label = NULL;
1442 priv->subtitle_label = NULL;
1443 gtk_widget_unparent (label_box);
1444 }
1445
1446 }
1447 else
1448 {
1449 if (priv->label_box == NULL)
1450 construct_label_box (bar);
1451 }
1452
1453 gtk_widget_queue_resize (GTK_WIDGET (bar));
1454
1455 g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_CUSTOM_TITLE]);
1456 }
1457
1458 /**
1459 * gtk_header_bar_get_custom_title:
1460 * @bar: a #GtkHeaderBar
1461 *
1462 * Retrieves the custom title widget of the header. See
1463 * gtk_header_bar_set_custom_title().
1464 *
1465 * Returns: (nullable) (transfer none): the custom title widget
1466 * of the header, or %NULL if none has been set explicitly.
1467 *
1468 * Since: 3.10
1469 */
1470 GtkWidget *
gtk_header_bar_get_custom_title(GtkHeaderBar * bar)1471 gtk_header_bar_get_custom_title (GtkHeaderBar *bar)
1472 {
1473 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1474
1475 g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL);
1476
1477 return priv->custom_title;
1478 }
1479
1480 static void
gtk_header_bar_destroy(GtkWidget * widget)1481 gtk_header_bar_destroy (GtkWidget *widget)
1482 {
1483 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (widget));
1484
1485 if (priv->label_sizing_box)
1486 {
1487 gtk_widget_destroy (priv->label_sizing_box);
1488 g_clear_object (&priv->label_sizing_box);
1489 }
1490
1491 if (priv->custom_title)
1492 {
1493 gtk_widget_unparent (priv->custom_title);
1494 priv->custom_title = NULL;
1495 }
1496
1497 if (priv->label_box)
1498 {
1499 gtk_widget_unparent (priv->label_box);
1500 priv->label_box = NULL;
1501 }
1502
1503 if (priv->titlebar_start_box)
1504 {
1505 gtk_widget_unparent (priv->titlebar_start_box);
1506 priv->titlebar_start_box = NULL;
1507 priv->titlebar_start_separator = NULL;
1508 }
1509
1510 if (priv->titlebar_end_box)
1511 {
1512 gtk_widget_unparent (priv->titlebar_end_box);
1513 priv->titlebar_end_box = NULL;
1514 priv->titlebar_end_separator = NULL;
1515 }
1516
1517 GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->destroy (widget);
1518 }
1519
1520 static void
gtk_header_bar_finalize(GObject * object)1521 gtk_header_bar_finalize (GObject *object)
1522 {
1523 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (object));
1524
1525 g_free (priv->title);
1526 g_free (priv->subtitle);
1527 g_free (priv->decoration_layout);
1528
1529 g_clear_object (&priv->gadget);
1530
1531 G_OBJECT_CLASS (gtk_header_bar_parent_class)->finalize (object);
1532 }
1533
1534 static void
gtk_header_bar_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1535 gtk_header_bar_get_property (GObject *object,
1536 guint prop_id,
1537 GValue *value,
1538 GParamSpec *pspec)
1539 {
1540 GtkHeaderBar *bar = GTK_HEADER_BAR (object);
1541 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1542
1543 switch (prop_id)
1544 {
1545 case PROP_TITLE:
1546 g_value_set_string (value, priv->title);
1547 break;
1548
1549 case PROP_SUBTITLE:
1550 g_value_set_string (value, priv->subtitle);
1551 break;
1552
1553 case PROP_CUSTOM_TITLE:
1554 g_value_set_object (value, priv->custom_title);
1555 break;
1556
1557 case PROP_SPACING:
1558 g_value_set_int (value, priv->spacing);
1559 break;
1560
1561 case PROP_SHOW_CLOSE_BUTTON:
1562 g_value_set_boolean (value, gtk_header_bar_get_show_close_button (bar));
1563 break;
1564
1565 case PROP_HAS_SUBTITLE:
1566 g_value_set_boolean (value, gtk_header_bar_get_has_subtitle (bar));
1567 break;
1568
1569 case PROP_DECORATION_LAYOUT:
1570 g_value_set_string (value, gtk_header_bar_get_decoration_layout (bar));
1571 break;
1572
1573 case PROP_DECORATION_LAYOUT_SET:
1574 g_value_set_boolean (value, priv->decoration_layout_set);
1575 break;
1576
1577 default:
1578 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1579 break;
1580 }
1581 }
1582
1583 static void
gtk_header_bar_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1584 gtk_header_bar_set_property (GObject *object,
1585 guint prop_id,
1586 const GValue *value,
1587 GParamSpec *pspec)
1588 {
1589 GtkHeaderBar *bar = GTK_HEADER_BAR (object);
1590 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1591
1592 switch (prop_id)
1593 {
1594 case PROP_TITLE:
1595 gtk_header_bar_set_title (bar, g_value_get_string (value));
1596 break;
1597
1598 case PROP_SUBTITLE:
1599 gtk_header_bar_set_subtitle (bar, g_value_get_string (value));
1600 break;
1601
1602 case PROP_CUSTOM_TITLE:
1603 gtk_header_bar_set_custom_title (bar, g_value_get_object (value));
1604 break;
1605
1606 case PROP_SPACING:
1607 if (priv->spacing != g_value_get_int (value))
1608 {
1609 priv->spacing = g_value_get_int (value);
1610 gtk_widget_queue_resize (GTK_WIDGET (bar));
1611 g_object_notify_by_pspec (object, pspec);
1612 }
1613 break;
1614
1615 case PROP_SHOW_CLOSE_BUTTON:
1616 gtk_header_bar_set_show_close_button (bar, g_value_get_boolean (value));
1617 break;
1618
1619 case PROP_HAS_SUBTITLE:
1620 gtk_header_bar_set_has_subtitle (bar, g_value_get_boolean (value));
1621 break;
1622
1623 case PROP_DECORATION_LAYOUT:
1624 gtk_header_bar_set_decoration_layout (bar, g_value_get_string (value));
1625 break;
1626
1627 case PROP_DECORATION_LAYOUT_SET:
1628 priv->decoration_layout_set = g_value_get_boolean (value);
1629 break;
1630
1631 default:
1632 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1633 break;
1634 }
1635 }
1636
1637 static void
notify_child_cb(GObject * child,GParamSpec * pspec,GtkHeaderBar * bar)1638 notify_child_cb (GObject *child,
1639 GParamSpec *pspec,
1640 GtkHeaderBar *bar)
1641 {
1642 _gtk_header_bar_update_separator_visibility (bar);
1643 }
1644
1645 static void
gtk_header_bar_pack(GtkHeaderBar * bar,GtkWidget * widget,GtkPackType pack_type)1646 gtk_header_bar_pack (GtkHeaderBar *bar,
1647 GtkWidget *widget,
1648 GtkPackType pack_type)
1649 {
1650 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1651 Child *child;
1652
1653 g_return_if_fail (gtk_widget_get_parent (widget) == NULL);
1654
1655 child = g_new (Child, 1);
1656 child->widget = widget;
1657 child->pack_type = pack_type;
1658
1659 priv->children = g_list_append (priv->children, child);
1660
1661 gtk_widget_freeze_child_notify (widget);
1662 gtk_header_bar_reorder_css_node (bar, GTK_PACK_START, widget);
1663 gtk_widget_set_parent (widget, GTK_WIDGET (bar));
1664 g_signal_connect (widget, "notify::visible", G_CALLBACK (notify_child_cb), bar);
1665 gtk_widget_child_notify (widget, "pack-type");
1666 gtk_widget_child_notify (widget, "position");
1667 gtk_widget_thaw_child_notify (widget);
1668
1669 _gtk_header_bar_update_separator_visibility (bar);
1670 }
1671
1672 static void
gtk_header_bar_add(GtkContainer * container,GtkWidget * child)1673 gtk_header_bar_add (GtkContainer *container,
1674 GtkWidget *child)
1675 {
1676 gtk_header_bar_pack (GTK_HEADER_BAR (container), child, GTK_PACK_START);
1677 }
1678
1679 static GList *
find_child_link(GtkHeaderBar * bar,GtkWidget * widget,gint * position)1680 find_child_link (GtkHeaderBar *bar,
1681 GtkWidget *widget,
1682 gint *position)
1683 {
1684 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1685 GList *l;
1686 Child *child;
1687 gint i;
1688
1689 for (l = priv->children, i = 0; l; l = l->next, i++)
1690 {
1691 child = l->data;
1692 if (child->widget == widget)
1693 {
1694 if (position)
1695 *position = i;
1696 return l;
1697 }
1698 }
1699
1700 return NULL;
1701 }
1702
1703 static void
gtk_header_bar_remove(GtkContainer * container,GtkWidget * widget)1704 gtk_header_bar_remove (GtkContainer *container,
1705 GtkWidget *widget)
1706 {
1707 GtkHeaderBar *bar = GTK_HEADER_BAR (container);
1708 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1709 GList *l;
1710 Child *child;
1711
1712 l = find_child_link (bar, widget, NULL);
1713 if (l)
1714 {
1715 child = l->data;
1716 g_signal_handlers_disconnect_by_func (widget, notify_child_cb, bar);
1717 gtk_widget_unparent (child->widget);
1718 priv->children = g_list_delete_link (priv->children, l);
1719 g_free (child);
1720 gtk_widget_queue_resize (GTK_WIDGET (container));
1721 _gtk_header_bar_update_separator_visibility (bar);
1722 }
1723 }
1724
1725 static void
gtk_header_bar_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)1726 gtk_header_bar_forall (GtkContainer *container,
1727 gboolean include_internals,
1728 GtkCallback callback,
1729 gpointer callback_data)
1730 {
1731 GtkHeaderBar *bar = GTK_HEADER_BAR (container);
1732 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1733 Child *child;
1734 GList *children;
1735
1736 if (include_internals && priv->titlebar_start_box != NULL)
1737 (* callback) (priv->titlebar_start_box, callback_data);
1738
1739 children = priv->children;
1740 while (children)
1741 {
1742 child = children->data;
1743 children = children->next;
1744 if (child->pack_type == GTK_PACK_START)
1745 (* callback) (child->widget, callback_data);
1746 }
1747
1748 if (priv->custom_title != NULL)
1749 (* callback) (priv->custom_title, callback_data);
1750
1751 if (include_internals && priv->label_box != NULL)
1752 (* callback) (priv->label_box, callback_data);
1753
1754 children = priv->children;
1755 while (children)
1756 {
1757 child = children->data;
1758 children = children->next;
1759 if (child->pack_type == GTK_PACK_END)
1760 (* callback) (child->widget, callback_data);
1761 }
1762
1763 if (include_internals && priv->titlebar_end_box != NULL)
1764 (* callback) (priv->titlebar_end_box, callback_data);
1765 }
1766
1767 static void
gtk_header_bar_reorder_child(GtkHeaderBar * bar,GtkWidget * widget,gint position)1768 gtk_header_bar_reorder_child (GtkHeaderBar *bar,
1769 GtkWidget *widget,
1770 gint position)
1771 {
1772 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1773 GList *l;
1774 gint old_position;
1775 Child *child;
1776
1777 l = find_child_link (bar, widget, &old_position);
1778
1779 if (l == NULL)
1780 return;
1781
1782 if (old_position == position)
1783 return;
1784
1785 child = l->data;
1786 priv->children = g_list_delete_link (priv->children, l);
1787
1788 if (position < 0)
1789 l = NULL;
1790 else
1791 l = g_list_nth (priv->children, position);
1792
1793 priv->children = g_list_insert_before (priv->children, l, child);
1794 gtk_header_bar_reorder_css_node (bar, child->pack_type, widget);
1795 gtk_widget_child_notify (widget, "position");
1796 gtk_widget_queue_resize (widget);
1797 }
1798
1799 static GType
gtk_header_bar_child_type(GtkContainer * container)1800 gtk_header_bar_child_type (GtkContainer *container)
1801 {
1802 return GTK_TYPE_WIDGET;
1803 }
1804
1805 static void
gtk_header_bar_get_child_property(GtkContainer * container,GtkWidget * widget,guint property_id,GValue * value,GParamSpec * pspec)1806 gtk_header_bar_get_child_property (GtkContainer *container,
1807 GtkWidget *widget,
1808 guint property_id,
1809 GValue *value,
1810 GParamSpec *pspec)
1811 {
1812 GtkHeaderBar *bar = GTK_HEADER_BAR (container);
1813 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
1814 GList *l;
1815 Child *child;
1816
1817 l = find_child_link (bar, widget, NULL);
1818 if (l == NULL)
1819 {
1820 g_param_value_set_default (pspec, value);
1821 return;
1822 }
1823
1824 child = l->data;
1825
1826 switch (property_id)
1827 {
1828 case CHILD_PROP_PACK_TYPE:
1829 g_value_set_enum (value, child->pack_type);
1830 break;
1831
1832 case CHILD_PROP_POSITION:
1833 g_value_set_int (value, g_list_position (priv->children, l));
1834 break;
1835
1836 default:
1837 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1838 break;
1839 }
1840 }
1841
1842 static void
gtk_header_bar_set_child_property(GtkContainer * container,GtkWidget * widget,guint property_id,const GValue * value,GParamSpec * pspec)1843 gtk_header_bar_set_child_property (GtkContainer *container,
1844 GtkWidget *widget,
1845 guint property_id,
1846 const GValue *value,
1847 GParamSpec *pspec)
1848 {
1849 GtkHeaderBar *bar = GTK_HEADER_BAR (container);
1850 GList *l;
1851 Child *child;
1852
1853 l = find_child_link (bar, widget, NULL);
1854 if (l == NULL)
1855 return;
1856
1857 child = l->data;
1858
1859 switch (property_id)
1860 {
1861 case CHILD_PROP_PACK_TYPE:
1862 child->pack_type = g_value_get_enum (value);
1863 _gtk_header_bar_update_separator_visibility (bar);
1864 gtk_widget_queue_resize (widget);
1865 break;
1866
1867 case CHILD_PROP_POSITION:
1868 gtk_header_bar_reorder_child (bar, widget, g_value_get_int (value));
1869 break;
1870
1871 default:
1872 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1873 break;
1874 }
1875 }
1876
1877 static gint
gtk_header_bar_draw(GtkWidget * widget,cairo_t * cr)1878 gtk_header_bar_draw (GtkWidget *widget,
1879 cairo_t *cr)
1880 {
1881 GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (GTK_HEADER_BAR (widget));
1882
1883 gtk_css_gadget_draw (priv->gadget, cr);
1884
1885 return FALSE;
1886 }
1887
1888 static gboolean
gtk_header_bar_render_contents(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer unused)1889 gtk_header_bar_render_contents (GtkCssGadget *gadget,
1890 cairo_t *cr,
1891 int x,
1892 int y,
1893 int width,
1894 int height,
1895 gpointer unused)
1896 {
1897 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1898
1899 GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->draw (widget, cr);
1900
1901 return FALSE;
1902 }
1903
1904 static void
gtk_header_bar_realize(GtkWidget * widget)1905 gtk_header_bar_realize (GtkWidget *widget)
1906 {
1907 GtkSettings *settings;
1908
1909 GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->realize (widget);
1910
1911 settings = gtk_widget_get_settings (widget);
1912 g_signal_connect_swapped (settings, "notify::gtk-shell-shows-app-menu",
1913 G_CALLBACK (_gtk_header_bar_update_window_buttons), widget);
1914 g_signal_connect_swapped (settings, "notify::gtk-decoration-layout",
1915 G_CALLBACK (_gtk_header_bar_update_window_buttons), widget);
1916 _gtk_header_bar_update_window_buttons (GTK_HEADER_BAR (widget));
1917 }
1918
1919 static void
gtk_header_bar_unrealize(GtkWidget * widget)1920 gtk_header_bar_unrealize (GtkWidget *widget)
1921 {
1922 GtkSettings *settings;
1923
1924 settings = gtk_widget_get_settings (widget);
1925
1926 g_signal_handlers_disconnect_by_func (settings, _gtk_header_bar_update_window_buttons, widget);
1927
1928 GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->unrealize (widget);
1929 }
1930
1931 static gboolean
window_state_changed(GtkWidget * window,GdkEventWindowState * event,gpointer data)1932 window_state_changed (GtkWidget *window,
1933 GdkEventWindowState *event,
1934 gpointer data)
1935 {
1936 GtkHeaderBar *bar = GTK_HEADER_BAR (data);
1937
1938 if (event->changed_mask & (GDK_WINDOW_STATE_FULLSCREEN |
1939 GDK_WINDOW_STATE_MAXIMIZED |
1940 GDK_WINDOW_STATE_TILED |
1941 GDK_WINDOW_STATE_TOP_TILED |
1942 GDK_WINDOW_STATE_RIGHT_TILED |
1943 GDK_WINDOW_STATE_BOTTOM_TILED |
1944 GDK_WINDOW_STATE_LEFT_TILED))
1945 _gtk_header_bar_update_window_buttons (bar);
1946
1947 return FALSE;
1948 }
1949
1950 static void
gtk_header_bar_hierarchy_changed(GtkWidget * widget,GtkWidget * previous_toplevel)1951 gtk_header_bar_hierarchy_changed (GtkWidget *widget,
1952 GtkWidget *previous_toplevel)
1953 {
1954 GtkWidget *toplevel;
1955 GtkHeaderBar *bar = GTK_HEADER_BAR (widget);
1956
1957 toplevel = gtk_widget_get_toplevel (widget);
1958
1959 if (previous_toplevel)
1960 g_signal_handlers_disconnect_by_func (previous_toplevel,
1961 window_state_changed, widget);
1962
1963 if (toplevel)
1964 g_signal_connect_after (toplevel, "window-state-event",
1965 G_CALLBACK (window_state_changed), widget);
1966
1967 _gtk_header_bar_update_window_buttons (bar);
1968 }
1969
1970 static void
gtk_header_bar_direction_changed(GtkWidget * widget,GtkTextDirection previous_direction)1971 gtk_header_bar_direction_changed (GtkWidget *widget,
1972 GtkTextDirection previous_direction)
1973 {
1974 GTK_WIDGET_CLASS (gtk_header_bar_parent_class)->direction_changed (widget, previous_direction);
1975
1976 gtk_css_node_reverse_children (gtk_widget_get_css_node (widget));
1977 }
1978
1979 static void
gtk_header_bar_class_init(GtkHeaderBarClass * class)1980 gtk_header_bar_class_init (GtkHeaderBarClass *class)
1981 {
1982 GObjectClass *object_class = G_OBJECT_CLASS (class);
1983 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1984 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
1985
1986 object_class->finalize = gtk_header_bar_finalize;
1987 object_class->get_property = gtk_header_bar_get_property;
1988 object_class->set_property = gtk_header_bar_set_property;
1989
1990 widget_class->destroy = gtk_header_bar_destroy;
1991 widget_class->size_allocate = gtk_header_bar_size_allocate;
1992 widget_class->get_preferred_width = gtk_header_bar_get_preferred_width;
1993 widget_class->get_preferred_height = gtk_header_bar_get_preferred_height;
1994 widget_class->get_preferred_height_for_width = gtk_header_bar_get_preferred_height_for_width;
1995 widget_class->get_preferred_width_for_height = gtk_header_bar_get_preferred_width_for_height;
1996 widget_class->draw = gtk_header_bar_draw;
1997 widget_class->realize = gtk_header_bar_realize;
1998 widget_class->unrealize = gtk_header_bar_unrealize;
1999 widget_class->hierarchy_changed = gtk_header_bar_hierarchy_changed;
2000 widget_class->direction_changed = gtk_header_bar_direction_changed;
2001
2002 container_class->add = gtk_header_bar_add;
2003 container_class->remove = gtk_header_bar_remove;
2004 container_class->forall = gtk_header_bar_forall;
2005 container_class->child_type = gtk_header_bar_child_type;
2006 container_class->set_child_property = gtk_header_bar_set_child_property;
2007 container_class->get_child_property = gtk_header_bar_get_child_property;
2008 gtk_container_class_handle_border_width (container_class);
2009
2010 gtk_container_class_install_child_property (container_class,
2011 CHILD_PROP_PACK_TYPE,
2012 g_param_spec_enum ("pack-type",
2013 P_("Pack type"),
2014 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
2015 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
2016 GTK_PARAM_READWRITE));
2017 gtk_container_class_install_child_property (container_class,
2018 CHILD_PROP_POSITION,
2019 g_param_spec_int ("position",
2020 P_("Position"),
2021 P_("The index of the child in the parent"),
2022 -1, G_MAXINT, 0,
2023 GTK_PARAM_READWRITE));
2024
2025 header_bar_props[PROP_TITLE] =
2026 g_param_spec_string ("title",
2027 P_("Title"),
2028 P_("The title to display"),
2029 NULL,
2030 G_PARAM_READWRITE);
2031
2032 header_bar_props[PROP_SUBTITLE] =
2033 g_param_spec_string ("subtitle",
2034 P_("Subtitle"),
2035 P_("The subtitle to display"),
2036 NULL,
2037 G_PARAM_READWRITE);
2038
2039 header_bar_props[PROP_CUSTOM_TITLE] =
2040 g_param_spec_object ("custom-title",
2041 P_("Custom Title"),
2042 P_("Custom title widget to display"),
2043 GTK_TYPE_WIDGET,
2044 G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
2045
2046 header_bar_props[PROP_SPACING] =
2047 g_param_spec_int ("spacing",
2048 P_("Spacing"),
2049 P_("The amount of space between children"),
2050 0, G_MAXINT,
2051 DEFAULT_SPACING,
2052 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
2053
2054 /**
2055 * GtkHeaderBar:show-close-button:
2056 *
2057 * Whether to show window decorations.
2058 *
2059 * Which buttons are actually shown and where is determined
2060 * by the #GtkHeaderBar:decoration-layout property, and by
2061 * the state of the window (e.g. a close button will not be
2062 * shown if the window can't be closed).
2063 */
2064 header_bar_props[PROP_SHOW_CLOSE_BUTTON] =
2065 g_param_spec_boolean ("show-close-button",
2066 P_("Show decorations"),
2067 P_("Whether to show window decorations"),
2068 FALSE,
2069 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
2070
2071 /**
2072 * GtkHeaderBar:decoration-layout:
2073 *
2074 * The decoration layout for buttons. If this property is
2075 * not set, the #GtkSettings:gtk-decoration-layout setting
2076 * is used.
2077 *
2078 * See gtk_header_bar_set_decoration_layout() for information
2079 * about the format of this string.
2080 *
2081 * Since: 3.12
2082 */
2083 header_bar_props[PROP_DECORATION_LAYOUT] =
2084 g_param_spec_string ("decoration-layout",
2085 P_("Decoration Layout"),
2086 P_("The layout for window decorations"),
2087 NULL,
2088 GTK_PARAM_READWRITE);
2089
2090 /**
2091 * GtkHeaderBar:decoration-layout-set:
2092 *
2093 * Set to %TRUE if #GtkHeaderBar:decoration-layout is set.
2094 *
2095 * Since: 3.12
2096 */
2097 header_bar_props[PROP_DECORATION_LAYOUT_SET] =
2098 g_param_spec_boolean ("decoration-layout-set",
2099 P_("Decoration Layout Set"),
2100 P_("Whether the decoration-layout property has been set"),
2101 FALSE,
2102 GTK_PARAM_READWRITE);
2103
2104 /**
2105 * GtkHeaderBar:has-subtitle:
2106 *
2107 * If %TRUE, reserve space for a subtitle, even if none
2108 * is currently set.
2109 *
2110 * Since: 3.12
2111 */
2112 header_bar_props[PROP_HAS_SUBTITLE] =
2113 g_param_spec_boolean ("has-subtitle",
2114 P_("Has Subtitle"),
2115 P_("Whether to reserve space for a subtitle"),
2116 TRUE,
2117 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
2118
2119 g_object_class_install_properties (object_class, LAST_PROP, header_bar_props);
2120
2121 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_HEADER_BAR_ACCESSIBLE);
2122 gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_PANEL);
2123 gtk_widget_class_set_css_name (widget_class, "headerbar");
2124 }
2125
2126 static void
gtk_header_bar_init(GtkHeaderBar * bar)2127 gtk_header_bar_init (GtkHeaderBar *bar)
2128 {
2129 GtkHeaderBarPrivate *priv;
2130 GtkCssNode *widget_node;
2131
2132 priv = gtk_header_bar_get_instance_private (bar);
2133
2134 gtk_widget_set_has_window (GTK_WIDGET (bar), FALSE);
2135
2136 priv->title = NULL;
2137 priv->subtitle = NULL;
2138 priv->custom_title = NULL;
2139 priv->children = NULL;
2140 priv->spacing = DEFAULT_SPACING;
2141 priv->has_subtitle = TRUE;
2142 priv->decoration_layout = NULL;
2143 priv->decoration_layout_set = FALSE;
2144
2145 init_sizing_box (bar);
2146 construct_label_box (bar);
2147
2148 widget_node = gtk_widget_get_css_node (GTK_WIDGET (bar));
2149 priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
2150 GTK_WIDGET (bar),
2151 gtk_header_bar_get_content_size,
2152 gtk_header_bar_allocate_contents,
2153 gtk_header_bar_render_contents,
2154 NULL,
2155 NULL);
2156
2157 }
2158
2159 static void
gtk_header_bar_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)2160 gtk_header_bar_buildable_add_child (GtkBuildable *buildable,
2161 GtkBuilder *builder,
2162 GObject *child,
2163 const gchar *type)
2164 {
2165 if (type && strcmp (type, "title") == 0)
2166 gtk_header_bar_set_custom_title (GTK_HEADER_BAR (buildable), GTK_WIDGET (child));
2167 else if (!type)
2168 gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
2169 else
2170 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_HEADER_BAR (buildable), type);
2171 }
2172
2173 static void
gtk_header_bar_buildable_init(GtkBuildableIface * iface)2174 gtk_header_bar_buildable_init (GtkBuildableIface *iface)
2175 {
2176 iface->add_child = gtk_header_bar_buildable_add_child;
2177 }
2178
2179 /**
2180 * gtk_header_bar_pack_start:
2181 * @bar: A #GtkHeaderBar
2182 * @child: the #GtkWidget to be added to @bar
2183 *
2184 * Adds @child to @bar, packed with reference to the
2185 * start of the @bar.
2186 *
2187 * Since: 3.10
2188 */
2189 void
gtk_header_bar_pack_start(GtkHeaderBar * bar,GtkWidget * child)2190 gtk_header_bar_pack_start (GtkHeaderBar *bar,
2191 GtkWidget *child)
2192 {
2193 gtk_header_bar_pack (bar, child, GTK_PACK_START);
2194 }
2195
2196 /**
2197 * gtk_header_bar_pack_end:
2198 * @bar: A #GtkHeaderBar
2199 * @child: the #GtkWidget to be added to @bar
2200 *
2201 * Adds @child to @bar, packed with reference to the
2202 * end of the @bar.
2203 *
2204 * Since: 3.10
2205 */
2206 void
gtk_header_bar_pack_end(GtkHeaderBar * bar,GtkWidget * child)2207 gtk_header_bar_pack_end (GtkHeaderBar *bar,
2208 GtkWidget *child)
2209 {
2210 gtk_header_bar_pack (bar, child, GTK_PACK_END);
2211 }
2212
2213 /**
2214 * gtk_header_bar_new:
2215 *
2216 * Creates a new #GtkHeaderBar widget.
2217 *
2218 * Returns: a new #GtkHeaderBar
2219 *
2220 * Since: 3.10
2221 */
2222 GtkWidget *
gtk_header_bar_new(void)2223 gtk_header_bar_new (void)
2224 {
2225 return GTK_WIDGET (g_object_new (GTK_TYPE_HEADER_BAR, NULL));
2226 }
2227
2228 /**
2229 * gtk_header_bar_get_show_close_button:
2230 * @bar: a #GtkHeaderBar
2231 *
2232 * Returns whether this header bar shows the standard window
2233 * decorations.
2234 *
2235 * Returns: %TRUE if the decorations are shown
2236 *
2237 * Since: 3.10
2238 */
2239 gboolean
gtk_header_bar_get_show_close_button(GtkHeaderBar * bar)2240 gtk_header_bar_get_show_close_button (GtkHeaderBar *bar)
2241 {
2242 GtkHeaderBarPrivate *priv;
2243
2244 g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), FALSE);
2245
2246 priv = gtk_header_bar_get_instance_private (bar);
2247
2248 return priv->shows_wm_decorations;
2249 }
2250
2251 /**
2252 * gtk_header_bar_set_show_close_button:
2253 * @bar: a #GtkHeaderBar
2254 * @setting: %TRUE to show standard window decorations
2255 *
2256 * Sets whether this header bar shows the standard window decorations,
2257 * including close, maximize, and minimize.
2258 *
2259 * Since: 3.10
2260 */
2261 void
gtk_header_bar_set_show_close_button(GtkHeaderBar * bar,gboolean setting)2262 gtk_header_bar_set_show_close_button (GtkHeaderBar *bar,
2263 gboolean setting)
2264 {
2265 GtkHeaderBarPrivate *priv;
2266
2267 g_return_if_fail (GTK_IS_HEADER_BAR (bar));
2268
2269 priv = gtk_header_bar_get_instance_private (bar);
2270
2271 setting = setting != FALSE;
2272
2273 if (priv->shows_wm_decorations == setting)
2274 return;
2275
2276 priv->shows_wm_decorations = setting;
2277 _gtk_header_bar_update_window_buttons (bar);
2278 g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_SHOW_CLOSE_BUTTON]);
2279 }
2280
2281 /**
2282 * gtk_header_bar_set_has_subtitle:
2283 * @bar: a #GtkHeaderBar
2284 * @setting: %TRUE to reserve space for a subtitle
2285 *
2286 * Sets whether the header bar should reserve space
2287 * for a subtitle, even if none is currently set.
2288 *
2289 * Since: 3.12
2290 */
2291 void
gtk_header_bar_set_has_subtitle(GtkHeaderBar * bar,gboolean setting)2292 gtk_header_bar_set_has_subtitle (GtkHeaderBar *bar,
2293 gboolean setting)
2294 {
2295 GtkHeaderBarPrivate *priv;
2296
2297 g_return_if_fail (GTK_IS_HEADER_BAR (bar));
2298
2299 priv = gtk_header_bar_get_instance_private (bar);
2300
2301 setting = setting != FALSE;
2302
2303 if (priv->has_subtitle == setting)
2304 return;
2305
2306 priv->has_subtitle = setting;
2307 gtk_widget_set_visible (priv->subtitle_sizing_label, setting || (priv->subtitle && priv->subtitle[0]));
2308
2309 gtk_widget_queue_resize (GTK_WIDGET (bar));
2310
2311 g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_HAS_SUBTITLE]);
2312 }
2313
2314 /**
2315 * gtk_header_bar_get_has_subtitle:
2316 * @bar: a #GtkHeaderBar
2317 *
2318 * Retrieves whether the header bar reserves space for
2319 * a subtitle, regardless if one is currently set or not.
2320 *
2321 * Returns: %TRUE if the header bar reserves space
2322 * for a subtitle
2323 *
2324 * Since: 3.12
2325 */
2326 gboolean
gtk_header_bar_get_has_subtitle(GtkHeaderBar * bar)2327 gtk_header_bar_get_has_subtitle (GtkHeaderBar *bar)
2328 {
2329 GtkHeaderBarPrivate *priv;
2330
2331 g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), FALSE);
2332
2333 priv = gtk_header_bar_get_instance_private (bar);
2334
2335 return priv->has_subtitle;
2336 }
2337
2338 /**
2339 * gtk_header_bar_set_decoration_layout:
2340 * @bar: a #GtkHeaderBar
2341 * @layout: (allow-none): a decoration layout, or %NULL to
2342 * unset the layout
2343 *
2344 * Sets the decoration layout for this header bar, overriding
2345 * the #GtkSettings:gtk-decoration-layout setting.
2346 *
2347 * There can be valid reasons for overriding the setting, such
2348 * as a header bar design that does not allow for buttons to take
2349 * room on the right, or only offers room for a single close button.
2350 * Split header bars are another example for overriding the
2351 * setting.
2352 *
2353 * The format of the string is button names, separated by commas.
2354 * A colon separates the buttons that should appear on the left
2355 * from those on the right. Recognized button names are minimize,
2356 * maximize, close, icon (the window icon) and menu (a menu button
2357 * for the fallback app menu).
2358 *
2359 * For example, “menu:minimize,maximize,close” specifies a menu
2360 * on the left, and minimize, maximize and close buttons on the right.
2361 *
2362 * Since: 3.12
2363 */
2364 void
gtk_header_bar_set_decoration_layout(GtkHeaderBar * bar,const gchar * layout)2365 gtk_header_bar_set_decoration_layout (GtkHeaderBar *bar,
2366 const gchar *layout)
2367 {
2368 GtkHeaderBarPrivate *priv;
2369
2370 g_return_if_fail (GTK_IS_HEADER_BAR (bar));
2371
2372 priv = gtk_header_bar_get_instance_private (bar);
2373
2374 g_free (priv->decoration_layout);
2375 priv->decoration_layout = g_strdup (layout);
2376 priv->decoration_layout_set = (layout != NULL);
2377
2378 _gtk_header_bar_update_window_buttons (bar);
2379
2380 g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_DECORATION_LAYOUT]);
2381 g_object_notify_by_pspec (G_OBJECT (bar), header_bar_props[PROP_DECORATION_LAYOUT_SET]);
2382 }
2383
2384 /**
2385 * gtk_header_bar_get_decoration_layout:
2386 * @bar: a #GtkHeaderBar
2387 *
2388 * Gets the decoration layout set with
2389 * gtk_header_bar_set_decoration_layout().
2390 *
2391 * Returns: the decoration layout
2392 *
2393 * Since: 3.12
2394 */
2395 const gchar *
gtk_header_bar_get_decoration_layout(GtkHeaderBar * bar)2396 gtk_header_bar_get_decoration_layout (GtkHeaderBar *bar)
2397 {
2398 GtkHeaderBarPrivate *priv;
2399
2400 g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL);
2401
2402 priv = gtk_header_bar_get_instance_private (bar);
2403
2404 return priv->decoration_layout;
2405 }
2406