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 |
119 | { |
120 | GtkWidget ; |
121 | |
122 | GtkWidget *handle; |
123 | GtkWidget *; |
124 | GtkWidget *; |
125 | GtkWidget *; |
126 | |
127 | GtkWidget *; |
128 | GtkWidget *; |
129 | |
130 | GtkWidget *; |
131 | GtkWidget *; |
132 | |
133 | char *; |
134 | |
135 | guint : 1; |
136 | guint : 1; |
137 | }; |
138 | |
139 | typedef struct _GtkHeaderBarClass ; |
140 | |
141 | struct |
142 | { |
143 | GtkWidgetClass ; |
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 *[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 |
163 | create_window_controls (GtkHeaderBar *bar) |
164 | { |
165 | GtkWidget *controls; |
166 | |
167 | controls = gtk_window_controls_new (side: GTK_PACK_START); |
168 | g_object_bind_property (source: bar, source_property: "decoration-layout" , |
169 | target: controls, target_property: "decoration-layout" , |
170 | flags: G_BINDING_SYNC_CREATE); |
171 | g_object_bind_property (source: controls, source_property: "empty" , |
172 | target: controls, target_property: "visible" , |
173 | flags: G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); |
174 | gtk_box_prepend (GTK_BOX (bar->start_box), child: controls); |
175 | bar->start_window_controls = controls; |
176 | |
177 | controls = gtk_window_controls_new (side: GTK_PACK_END); |
178 | g_object_bind_property (source: bar, source_property: "decoration-layout" , |
179 | target: controls, target_property: "decoration-layout" , |
180 | flags: G_BINDING_SYNC_CREATE); |
181 | g_object_bind_property (source: controls, source_property: "empty" , |
182 | target: controls, target_property: "visible" , |
183 | flags: G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); |
184 | gtk_box_append (GTK_BOX (bar->end_box), child: controls); |
185 | bar->end_window_controls = controls; |
186 | } |
187 | |
188 | static void |
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 (widget: bar->start_box); |
203 | w != NULL; |
204 | w = _gtk_widget_get_next_sibling (widget: 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 (widget: bar->end_box); |
215 | w != NULL; |
216 | w = _gtk_widget_get_next_sibling (widget: 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), css_class: "default-decoration" ); |
228 | else |
229 | gtk_widget_add_css_class (GTK_WIDGET (bar), css_class: "default-decoration" ); |
230 | } |
231 | |
232 | void |
233 | (GtkHeaderBar *bar) |
234 | { |
235 | bar->track_default_decoration = TRUE; |
236 | |
237 | update_default_decoration (bar); |
238 | } |
239 | |
240 | static void |
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), str: title); |
261 | } |
262 | |
263 | static void |
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 (widget: label, css_class: "title" ); |
272 | gtk_widget_set_valign (widget: label, align: 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), mode: 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), child: 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 |
302 | (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), child: 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), pspec: 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 * |
344 | (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 |
352 | (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 |
368 | (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 |
377 | (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 |
392 | (GObject *object) |
393 | { |
394 | GtkHeaderBar *bar = GTK_HEADER_BAR (object); |
395 | |
396 | g_free (mem: bar->decoration_layout); |
397 | |
398 | G_OBJECT_CLASS (gtk_header_bar_parent_class)->finalize (object); |
399 | } |
400 | |
401 | static void |
402 | (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, v_object: bar->title_widget); |
413 | break; |
414 | |
415 | case PROP_SHOW_TITLE_BUTTONS: |
416 | g_value_set_boolean (value, v_boolean: gtk_header_bar_get_show_title_buttons (bar)); |
417 | break; |
418 | |
419 | case PROP_DECORATION_LAYOUT: |
420 | g_value_set_string (value, v_string: 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 |
430 | (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, title_widget: g_value_get_object (value)); |
441 | break; |
442 | |
443 | case PROP_SHOW_TITLE_BUTTONS: |
444 | gtk_header_bar_set_show_title_buttons (bar, setting: g_value_get_boolean (value)); |
445 | break; |
446 | |
447 | case PROP_DECORATION_LAYOUT: |
448 | gtk_header_bar_set_decoration_layout (bar, layout: 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 |
458 | (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), child: widget); |
467 | } |
468 | else if (pack_type == GTK_PACK_END) |
469 | { |
470 | gtk_box_prepend (GTK_BOX (bar->end_box), child: widget); |
471 | } |
472 | |
473 | if (bar->track_default_decoration) |
474 | update_default_decoration (bar); |
475 | } |
476 | |
477 | /** |
478 | * gtk_header_bar_remove: |
479 | * @bar: a `GtkHeaderBar` |
480 | * @child: the child to remove |
481 | * |
482 | * Removes a child from the `GtkHeaderBar`. |
483 | * |
484 | * The child must have been added with |
485 | * [method@Gtk.HeaderBar.pack_start], |
486 | * [method@Gtk.HeaderBar.pack_end] or |
487 | * [method@Gtk.HeaderBar.set_title_widget]. |
488 | */ |
489 | void |
490 | (GtkHeaderBar *bar, |
491 | GtkWidget *child) |
492 | { |
493 | GtkWidget *parent; |
494 | gboolean removed = FALSE; |
495 | |
496 | parent = gtk_widget_get_parent (widget: child); |
497 | |
498 | if (parent == bar->start_box) |
499 | { |
500 | gtk_box_remove (GTK_BOX (bar->start_box), child); |
501 | removed = TRUE; |
502 | } |
503 | else if (parent == bar->end_box) |
504 | { |
505 | gtk_box_remove (GTK_BOX (bar->end_box), child); |
506 | removed = TRUE; |
507 | } |
508 | else if (parent == bar->center_box) |
509 | { |
510 | gtk_center_box_set_center_widget (GTK_CENTER_BOX (bar->center_box), NULL); |
511 | removed = TRUE; |
512 | } |
513 | |
514 | if (removed && bar->track_default_decoration) |
515 | update_default_decoration (bar); |
516 | } |
517 | |
518 | static GtkSizeRequestMode |
519 | (GtkWidget *widget) |
520 | { |
521 | GtkWidget *w; |
522 | int wfh = 0, hfw = 0; |
523 | |
524 | for (w = gtk_widget_get_first_child (widget); |
525 | w != NULL; |
526 | w = gtk_widget_get_next_sibling (widget: w)) |
527 | { |
528 | GtkSizeRequestMode mode = gtk_widget_get_request_mode (widget: w); |
529 | |
530 | switch (mode) |
531 | { |
532 | case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH: |
533 | hfw ++; |
534 | break; |
535 | case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT: |
536 | wfh ++; |
537 | break; |
538 | case GTK_SIZE_REQUEST_CONSTANT_SIZE: |
539 | default: |
540 | break; |
541 | } |
542 | } |
543 | |
544 | if (hfw == 0 && wfh == 0) |
545 | return GTK_SIZE_REQUEST_CONSTANT_SIZE; |
546 | else |
547 | return wfh > hfw ? |
548 | GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT : |
549 | GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; |
550 | } |
551 | |
552 | static void |
553 | (GtkHeaderBarClass *class) |
554 | { |
555 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
556 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); |
557 | |
558 | object_class->dispose = gtk_header_bar_dispose; |
559 | object_class->finalize = gtk_header_bar_finalize; |
560 | object_class->get_property = gtk_header_bar_get_property; |
561 | object_class->set_property = gtk_header_bar_set_property; |
562 | |
563 | widget_class->root = gtk_header_bar_root; |
564 | widget_class->unroot = gtk_header_bar_unroot; |
565 | widget_class->get_request_mode = gtk_header_bar_get_request_mode; |
566 | |
567 | header_bar_props[PROP_TITLE_WIDGET] = |
568 | g_param_spec_object (name: "title-widget" , |
569 | P_("Title Widget" ), |
570 | P_("Title widget to display" ), |
571 | GTK_TYPE_WIDGET, |
572 | flags: G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS); |
573 | |
574 | /** |
575 | * 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) |
576 | * |
577 | * Whether to show title buttons like close, minimize, maximize. |
578 | * |
579 | * Which buttons are actually shown and where is determined |
580 | * by the [property@Gtk.HeaderBar:decoration-layout] property, |
581 | * and by the state of the window (e.g. a close button will not |
582 | * be shown if the window can't be closed). |
583 | */ |
584 | header_bar_props[PROP_SHOW_TITLE_BUTTONS] = |
585 | g_param_spec_boolean (name: "show-title-buttons" , |
586 | P_("Show title buttons" ), |
587 | P_("Whether to show title buttons" ), |
588 | TRUE, |
589 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
590 | |
591 | /** |
592 | * GtkHeaderBar:decoration-layout: (attributes org.gtk.Property.get=gtk_header_bar_get_decoration_layout org.gtk.Property.set=gtk_header_bar_set_decoration_layout) |
593 | * |
594 | * The decoration layout for buttons. |
595 | * |
596 | * If this property is not set, the |
597 | * [property@Gtk.Settings:gtk-decoration-layout] setting is used. |
598 | */ |
599 | header_bar_props[PROP_DECORATION_LAYOUT] = |
600 | g_param_spec_string (name: "decoration-layout" , |
601 | P_("Decoration Layout" ), |
602 | P_("The layout for window decorations" ), |
603 | NULL, |
604 | GTK_PARAM_READWRITE); |
605 | |
606 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: header_bar_props); |
607 | |
608 | gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); |
609 | gtk_widget_class_set_css_name (widget_class, I_("headerbar" )); |
610 | gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_GROUP); |
611 | } |
612 | |
613 | static void |
614 | (GtkHeaderBar *bar) |
615 | { |
616 | bar->title_widget = NULL; |
617 | bar->decoration_layout = NULL; |
618 | bar->show_title_buttons = TRUE; |
619 | |
620 | bar->handle = gtk_window_handle_new (); |
621 | gtk_widget_set_parent (widget: bar->handle, GTK_WIDGET (bar)); |
622 | |
623 | bar->center_box = gtk_center_box_new (); |
624 | gtk_window_handle_set_child (self: GTK_WINDOW_HANDLE (ptr: bar->handle), child: bar->center_box); |
625 | |
626 | bar->start_box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0); |
627 | gtk_widget_add_css_class (widget: bar->start_box, css_class: "start" ); |
628 | gtk_center_box_set_start_widget (GTK_CENTER_BOX (bar->center_box), child: bar->start_box); |
629 | |
630 | bar->end_box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0); |
631 | gtk_widget_add_css_class (widget: bar->end_box, css_class: "end" ); |
632 | gtk_center_box_set_end_widget (GTK_CENTER_BOX (bar->center_box), child: bar->end_box); |
633 | |
634 | construct_title_label (bar); |
635 | create_window_controls (bar); |
636 | } |
637 | |
638 | static GtkBuildableIface *parent_buildable_iface; |
639 | |
640 | static void |
641 | (GtkBuildable *buildable, |
642 | GtkBuilder *builder, |
643 | GObject *child, |
644 | const char *type) |
645 | { |
646 | if (g_strcmp0 (str1: type, str2: "title" ) == 0) |
647 | gtk_header_bar_set_title_widget (GTK_HEADER_BAR (buildable), GTK_WIDGET (child)); |
648 | else if (g_strcmp0 (str1: type, str2: "start" ) == 0) |
649 | gtk_header_bar_pack_start (GTK_HEADER_BAR (buildable), GTK_WIDGET (child)); |
650 | else if (g_strcmp0 (str1: type, str2: "end" ) == 0) |
651 | gtk_header_bar_pack_end (GTK_HEADER_BAR (buildable), GTK_WIDGET (child)); |
652 | else if (type == NULL && GTK_IS_WIDGET (child)) |
653 | gtk_header_bar_pack_start (GTK_HEADER_BAR (buildable), GTK_WIDGET (child)); |
654 | else |
655 | parent_buildable_iface->add_child (buildable, builder, child, type); |
656 | } |
657 | |
658 | static void |
659 | (GtkBuildableIface *iface) |
660 | { |
661 | parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface); |
662 | |
663 | iface->add_child = gtk_header_bar_buildable_add_child; |
664 | } |
665 | |
666 | /** |
667 | * gtk_header_bar_pack_start: |
668 | * @bar: A `GtkHeaderBar` |
669 | * @child: the `GtkWidget` to be added to @bar |
670 | * |
671 | * Adds @child to @bar, packed with reference to the |
672 | * start of the @bar. |
673 | */ |
674 | void |
675 | (GtkHeaderBar *bar, |
676 | GtkWidget *child) |
677 | { |
678 | gtk_header_bar_pack (bar, widget: child, pack_type: GTK_PACK_START); |
679 | } |
680 | |
681 | /** |
682 | * gtk_header_bar_pack_end: |
683 | * @bar: A `GtkHeaderBar` |
684 | * @child: the `GtkWidget` to be added to @bar |
685 | * |
686 | * Adds @child to @bar, packed with reference to the |
687 | * end of the @bar. |
688 | */ |
689 | void |
690 | (GtkHeaderBar *bar, |
691 | GtkWidget *child) |
692 | { |
693 | gtk_header_bar_pack (bar, widget: child, pack_type: GTK_PACK_END); |
694 | } |
695 | |
696 | /** |
697 | * gtk_header_bar_new: |
698 | * |
699 | * Creates a new `GtkHeaderBar` widget. |
700 | * |
701 | * Returns: a new `GtkHeaderBar` |
702 | */ |
703 | GtkWidget * |
704 | (void) |
705 | { |
706 | return GTK_WIDGET (g_object_new (GTK_TYPE_HEADER_BAR, NULL)); |
707 | } |
708 | |
709 | /** |
710 | * gtk_header_bar_get_show_title_buttons: (attributes org.gtk.Method.get_property=show-title-buttons) |
711 | * @bar: a `GtkHeaderBar` |
712 | * |
713 | * Returns whether this header bar shows the standard window |
714 | * title buttons. |
715 | * |
716 | * Returns: %TRUE if title buttons are shown |
717 | */ |
718 | gboolean |
719 | (GtkHeaderBar *bar) |
720 | { |
721 | g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), FALSE); |
722 | |
723 | return bar->show_title_buttons; |
724 | } |
725 | |
726 | /** |
727 | * gtk_header_bar_set_show_title_buttons: (attributes org.gtk.Method.set_property=show-title-buttons) |
728 | * @bar: a `GtkHeaderBar` |
729 | * @setting: %TRUE to show standard title buttons |
730 | * |
731 | * Sets whether this header bar shows the standard window |
732 | * title buttons. |
733 | */ |
734 | void |
735 | (GtkHeaderBar *bar, |
736 | gboolean setting) |
737 | { |
738 | g_return_if_fail (GTK_IS_HEADER_BAR (bar)); |
739 | |
740 | setting = setting != FALSE; |
741 | |
742 | if (bar->show_title_buttons == setting) |
743 | return; |
744 | |
745 | bar->show_title_buttons = setting; |
746 | |
747 | if (setting) |
748 | create_window_controls (bar); |
749 | else |
750 | { |
751 | if (bar->start_box && bar->start_window_controls) |
752 | { |
753 | gtk_box_remove (GTK_BOX (bar->start_box), child: bar->start_window_controls); |
754 | bar->start_window_controls = NULL; |
755 | } |
756 | |
757 | if (bar->end_box && bar->end_window_controls) |
758 | { |
759 | gtk_box_remove (GTK_BOX (bar->end_box), child: bar->end_window_controls); |
760 | bar->end_window_controls = NULL; |
761 | } |
762 | } |
763 | |
764 | g_object_notify_by_pspec (G_OBJECT (bar), pspec: header_bar_props[PROP_SHOW_TITLE_BUTTONS]); |
765 | } |
766 | |
767 | /** |
768 | * gtk_header_bar_set_decoration_layout: (attributes org.gtk.Method.set_property=decoration-layout) |
769 | * @bar: a `GtkHeaderBar` |
770 | * @layout: (nullable): a decoration layout, or %NULL to unset the layout |
771 | * |
772 | * Sets the decoration layout for this header bar. |
773 | * |
774 | * This property overrides the |
775 | * [property@Gtk.Settings:gtk-decoration-layout] setting. |
776 | * |
777 | * There can be valid reasons for overriding the setting, such |
778 | * as a header bar design that does not allow for buttons to take |
779 | * room on the right, or only offers room for a single close button. |
780 | * Split header bars are another example for overriding the setting. |
781 | * |
782 | * The format of the string is button names, separated by commas. |
783 | * A colon separates the buttons that should appear on the left |
784 | * from those on the right. Recognized button names are minimize, |
785 | * maximize, close and icon (the window icon). |
786 | * |
787 | * For example, “icon:minimize,maximize,close” specifies a icon |
788 | * on the left, and minimize, maximize and close buttons on the right. |
789 | */ |
790 | void |
791 | (GtkHeaderBar *bar, |
792 | const char *layout) |
793 | { |
794 | g_return_if_fail (GTK_IS_HEADER_BAR (bar)); |
795 | |
796 | g_free (mem: bar->decoration_layout); |
797 | bar->decoration_layout = g_strdup (str: layout); |
798 | |
799 | g_object_notify_by_pspec (G_OBJECT (bar), pspec: header_bar_props[PROP_DECORATION_LAYOUT]); |
800 | } |
801 | |
802 | /** |
803 | * gtk_header_bar_get_decoration_layout: (attributes org.gtk.Method.get_property=decoration-layout) |
804 | * @bar: a `GtkHeaderBar` |
805 | * |
806 | * Gets the decoration layout of the `GtkHeaderBar`. |
807 | * |
808 | * Returns: (nullable): the decoration layout |
809 | */ |
810 | const char * |
811 | (GtkHeaderBar *bar) |
812 | { |
813 | g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL); |
814 | |
815 | return bar->decoration_layout; |
816 | } |
817 | |