1#include <gtk/gtk.h>
2
3static void
4unset_title (GtkWidget *window)
5{
6 GtkWidget *box;
7
8 g_assert (GTK_IS_WINDOW (window));
9
10 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
11 gtk_widget_hide (widget: box);
12
13 gtk_window_set_titlebar (GTK_WINDOW (window), titlebar: box);
14}
15
16static void
17load_css (GtkWidget *widget,
18 const char *css)
19{
20 GtkCssProvider *provider;
21 GtkStyleContext *context;
22
23 context = gtk_widget_get_style_context (widget);
24
25 provider = gtk_css_provider_new ();
26 gtk_css_provider_load_from_data (css_provider: provider, data: css, length: -1);
27 gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), priority: 800);
28}
29
30static void
31create_regular (GtkApplication *app)
32{
33 GtkWidget *window, *label;
34
35 window = gtk_application_window_new (application: app);
36 gtk_window_set_title (GTK_WINDOW (window), title: "Regular window");
37
38 label = gtk_label_new (str: "This window has no titlebar set");
39 gtk_label_set_wrap (GTK_LABEL (label), TRUE);
40 gtk_window_set_child (GTK_WINDOW (window), child: label);
41
42 gtk_widget_show (widget: window);
43}
44
45static void
46create_headerbar_as_titlebar (GtkApplication *app)
47{
48 GtkWidget *window, *header, *label;
49
50 window = gtk_application_window_new (application: app);
51 gtk_window_set_title (GTK_WINDOW (window), title: "Headerbar as titlebar");
52
53 header = gtk_header_bar_new ();
54 gtk_window_set_titlebar (GTK_WINDOW (window), titlebar: header);
55
56 label = gtk_label_new (str: "This window has a headerbar set as a titlebar");
57 gtk_label_set_wrap (GTK_LABEL (label), TRUE);
58 gtk_window_set_child (GTK_WINDOW (window), child: label);
59
60 gtk_widget_show (widget: window);
61}
62
63static void
64create_headerbar_inside_window (GtkApplication *app)
65{
66 GtkWidget *window, *box, *header, *label;
67
68 window = gtk_application_window_new (application: app);
69 gtk_window_set_title (GTK_WINDOW (window), title: "Headerbar inside window");
70 unset_title (window);
71
72 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
73 gtk_window_set_child (GTK_WINDOW (window), child: box);
74
75 header = gtk_header_bar_new ();
76 gtk_box_append (GTK_BOX (box), child: header);
77
78 label = gtk_label_new (str: "This window has a headerbar inside the window and no titlebar");
79 gtk_label_set_wrap (GTK_LABEL (label), TRUE);
80 gtk_widget_set_vexpand (widget: label, TRUE);
81 gtk_box_append (GTK_BOX (box), child: label);
82
83 gtk_widget_show (widget: window);
84}
85
86static void
87create_headerbar_overlay (GtkApplication *app)
88{
89 GtkWidget *window, *overlay, *sw, *box, *header, *label;
90
91 window = gtk_application_window_new (application: app);
92 gtk_window_set_title (GTK_WINDOW (window), title: "Headerbar overlaying content");
93 unset_title (window);
94
95 overlay = gtk_overlay_new ();
96 gtk_window_set_child (GTK_WINDOW (window), child: overlay);
97
98 header = gtk_header_bar_new ();
99 gtk_widget_set_valign (widget: header, align: GTK_ALIGN_START);
100 gtk_overlay_add_overlay (GTK_OVERLAY (overlay), widget: header);
101 load_css (widget: header, css: "headerbar { background: alpha(shade(@theme_bg_color, .9), .8); }");
102
103 sw = gtk_scrolled_window_new ();
104 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), hscrollbar_policy: GTK_POLICY_NEVER, vscrollbar_policy: GTK_POLICY_AUTOMATIC);
105 gtk_widget_set_size_request (widget: sw, width: 300, height: 250);
106 gtk_overlay_set_child (GTK_OVERLAY (overlay), child: sw);
107
108 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 12);
109 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: box);
110 gtk_widget_set_size_request (widget: sw, width: 300, height: 250);
111
112 label = gtk_label_new (str: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
113 "Nulla innn urna ac dui malesuada ornare. Nullam dictum "
114 "tempor mi et tincidunt. Aliquam metus nulla, auctor "
115 "vitae pulvinar nec, egestas at mi. Class aptent taciti "
116 "sociosqu ad litora torquent per conubia nostra, per "
117 "inceptos himenaeos. Aliquam sagittis, tellus congue "
118 "cursus congue, diam massa mollis enim, sit amet gravida "
119 "magna turpis egestas sapien. Aenean vel molestie nunc. "
120 "In hac habitasse platea dictumst. Suspendisse lacinia"
121 "mi eu ipsum vestibulum in venenatis enim commodo. "
122 "Vivamus non malesuada ligula.");
123 gtk_label_set_wrap (GTK_LABEL (label), TRUE);
124 gtk_box_append (GTK_BOX (box), child: label);
125
126 label = gtk_label_new (str: "This window has a headerbar inside an overlay, so the text is visible underneath it");
127 gtk_label_set_wrap (GTK_LABEL (label), TRUE);
128 gtk_widget_set_vexpand (widget: label, TRUE);
129 gtk_box_append (GTK_BOX (box), child: label);
130
131 gtk_widget_show (widget: window);
132}
133
134static void
135create_hiding_headerbar (GtkApplication *app)
136{
137 GtkWidget *window, *box, *revealer, *header, *label, *hbox, *toggle;
138
139 window = gtk_application_window_new (application: app);
140 gtk_window_set_title (GTK_WINDOW (window), title: "Hiding headerbar");
141 unset_title (window);
142
143 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
144 gtk_window_set_child (GTK_WINDOW (window), child: box);
145
146 revealer = gtk_revealer_new ();
147 gtk_box_append (GTK_BOX (box), child: revealer);
148
149 header = gtk_header_bar_new ();
150 gtk_revealer_set_child (GTK_REVEALER (revealer), child: header);
151
152 label = gtk_label_new (str: "This window's headerbar can be shown and hidden with animation");
153 gtk_label_set_wrap (GTK_LABEL (label), TRUE);
154 gtk_widget_set_vexpand (widget: label, TRUE);
155 gtk_box_append (GTK_BOX (box), child: label);
156
157 hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 12);
158 gtk_widget_set_halign (widget: hbox, align: GTK_ALIGN_CENTER);
159 gtk_widget_set_margin_top (widget: hbox, margin: 12);
160 gtk_widget_set_margin_bottom (widget: hbox, margin: 12);
161 gtk_widget_set_margin_start (widget: hbox, margin: 12);
162 gtk_widget_set_margin_end (widget: hbox, margin: 12);
163 gtk_box_append (GTK_BOX (box), child: hbox);
164
165 toggle = gtk_switch_new ();
166 gtk_switch_set_active (GTK_SWITCH (toggle), TRUE);
167 gtk_box_append (GTK_BOX (hbox), child: toggle);
168 g_object_bind_property (source: toggle, source_property: "active",
169 target: revealer, target_property: "reveal-child",
170 flags: G_BINDING_SYNC_CREATE);
171
172 label = gtk_label_new (str: "Show headerbar");
173 gtk_box_append (GTK_BOX (hbox), child: label);
174
175 gtk_widget_show (widget: window);
176}
177
178static void
179create_fake_headerbar (GtkApplication *app)
180{
181 GtkWidget *window, *handle, *box, *center_box, *controls, *label;
182
183 window = gtk_application_window_new (application: app);
184 gtk_window_set_title (GTK_WINDOW (window), title: "Fake headerbar");
185 unset_title (window);
186
187 handle = gtk_window_handle_new ();
188 gtk_window_set_child (GTK_WINDOW (window), child: handle);
189
190 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
191 gtk_window_handle_set_child (self: GTK_WINDOW_HANDLE (ptr: handle), child: box);
192
193 center_box = gtk_center_box_new ();
194 gtk_box_append (GTK_BOX (box), child: center_box);
195
196 label = gtk_label_new (str: "Fake headerbar");
197 gtk_center_box_set_center_widget (GTK_CENTER_BOX (center_box), child: label);
198
199 controls = gtk_window_controls_new (side: GTK_PACK_START);
200 gtk_center_box_set_start_widget (GTK_CENTER_BOX (center_box), child: controls);
201
202 controls = gtk_window_controls_new (side: GTK_PACK_END);
203 gtk_center_box_set_end_widget (GTK_CENTER_BOX (center_box), child: controls);
204
205 label = gtk_label_new (str: "This window's titlebar is just a centerbox with a label and window controls.\nThe whole window is draggable.");
206 gtk_label_set_wrap (GTK_LABEL (label), TRUE);
207 gtk_widget_set_vexpand (widget: label, TRUE);
208 gtk_box_append (GTK_BOX (box), child: label);
209
210 gtk_widget_show (widget: window);
211}
212
213/* split headerbar */
214
215static void
216split_decorations (GtkSettings *settings,
217 GParamSpec *pspec,
218 GtkBuilder *builder)
219{
220 GtkWidget *sheader, *mheader;
221 char *layout, *p1, *p2;
222 char **p;
223
224 sheader = (GtkWidget *)gtk_builder_get_object (builder, name: "sidebar-header");
225 mheader = (GtkWidget *)gtk_builder_get_object (builder, name: "main-header");
226
227 g_object_get (object: settings, first_property_name: "gtk-decoration-layout", &layout, NULL);
228
229 p = g_strsplit (string: layout, delimiter: ":", max_tokens: -1);
230
231 p1 = g_strconcat (string1: "", p[0], ":", NULL);
232
233 if (g_strv_length (str_array: p) >= 2)
234 p2 = g_strconcat (string1: ":", p[1], NULL);
235 else
236 p2 = g_strdup (str: "");
237
238 gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (sheader), layout: p1);
239 gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (mheader), layout: p2);
240
241 g_free (mem: p1);
242 g_free (mem: p2);
243 g_strfreev (str_array: p);
244 g_free (mem: layout);
245}
246
247
248static void
249create_split_headerbar (GtkApplication *app)
250{
251 GtkBuilder *builder;
252 GtkSettings *settings;
253 GtkWidget *win;
254 GtkWidget *entry;
255 GtkWidget *check;
256 GtkWidget *header;
257 const char *ui = "tests/testsplitheaders.ui";
258
259 if (!g_file_test (filename: ui, test: G_FILE_TEST_EXISTS))
260 {
261 g_warning ("Can't find %s", ui);
262 return;
263 }
264
265 builder = gtk_builder_new_from_file (filename: ui);
266
267 win = (GtkWidget *)gtk_builder_get_object (builder, name: "window");
268 gtk_window_set_application (GTK_WINDOW (win), application: app);
269
270 settings = gtk_widget_get_settings (widget: win);
271
272 g_signal_connect (settings, "notify::gtk-decoration-layout",
273 G_CALLBACK (split_decorations), builder);
274 split_decorations (settings, NULL, builder);
275
276 entry = (GtkWidget *)gtk_builder_get_object (builder, name: "layout-entry");
277 g_object_bind_property (source: settings, source_property: "gtk-decoration-layout",
278 target: entry, target_property: "text",
279 flags: G_BINDING_BIDIRECTIONAL|G_BINDING_SYNC_CREATE);
280 check = (GtkWidget *)gtk_builder_get_object (builder, name: "decorations");
281 header = (GtkWidget *)gtk_builder_get_object (builder, name: "sidebar-header");
282 g_object_bind_property (source: check, source_property: "active",
283 target: header, target_property: "show-title-buttons",
284 flags: G_BINDING_DEFAULT);
285 header = (GtkWidget *)gtk_builder_get_object (builder, name: "main-header");
286 g_object_bind_property (source: check, source_property: "active",
287 target: header, target_property: "show-title-buttons",
288 flags: G_BINDING_DEFAULT);
289 gtk_window_present (GTK_WINDOW (win));
290}
291
292/* stacked headers */
293
294static void
295back_to_main (GtkButton *button,
296 GtkWidget *win)
297{
298 GtkWidget *header_stack;
299 GtkWidget *page_stack;
300
301 header_stack = GTK_WIDGET (g_object_get_data (G_OBJECT (win), "header-stack"));
302 page_stack = GTK_WIDGET (g_object_get_data (G_OBJECT (win), "page-stack"));
303
304 gtk_stack_set_visible_child_name (GTK_STACK (header_stack), name: "main");
305 gtk_stack_set_visible_child_name (GTK_STACK (page_stack), name: "page1");
306}
307
308static void
309go_to_secondary (GtkButton *button,
310 GtkWidget *win)
311{
312 GtkWidget *header_stack;
313 GtkWidget *page_stack;
314
315 header_stack = GTK_WIDGET (g_object_get_data (G_OBJECT (win), "header-stack"));
316 page_stack = GTK_WIDGET (g_object_get_data (G_OBJECT (win), "page-stack"));
317
318 gtk_stack_set_visible_child_name (GTK_STACK (header_stack), name: "secondary");
319 gtk_stack_set_visible_child_name (GTK_STACK (page_stack), name: "secondary");
320}
321
322static void
323create_stacked_headerbar (GtkApplication *app)
324{
325 GtkBuilder *builder;
326 GtkWidget *win;
327 GtkWidget *new_btn;
328 GtkWidget *back_btn;
329 GtkWidget *header_stack;
330 GtkWidget *page_stack;
331 const char *ui = "tests/teststackedheaders.ui";
332
333 if (!g_file_test (filename: ui, test: G_FILE_TEST_EXISTS))
334 {
335 g_warning ("Can't find %s", ui);
336 return;
337 }
338
339 builder = gtk_builder_new ();
340 gtk_builder_add_from_file (builder, filename: ui, NULL);
341
342 win = (GtkWidget *)gtk_builder_get_object (builder, name: "window");
343 gtk_window_set_application (GTK_WINDOW (win), application: app);
344
345 header_stack = (GtkWidget *)gtk_builder_get_object (builder, name: "header_stack");
346 page_stack = (GtkWidget *)gtk_builder_get_object (builder, name: "page_stack");
347
348 g_object_set_data (G_OBJECT (win), key: "header-stack", data: header_stack);
349 g_object_set_data (G_OBJECT (win), key: "page-stack", data: page_stack);
350
351 new_btn = (GtkWidget *)gtk_builder_get_object (builder, name: "new_btn");
352 back_btn = (GtkWidget *)gtk_builder_get_object (builder, name: "back_btn");
353
354 g_signal_connect (new_btn, "clicked", G_CALLBACK (go_to_secondary), win);
355 g_signal_connect (back_btn, "clicked", G_CALLBACK (back_to_main), win);
356
357 gtk_window_present (GTK_WINDOW (win));
358}
359
360/* controls */
361static void
362create_controls (GtkApplication *app)
363{
364 GtkBuilder *builder;
365 GtkWidget *win;
366 const char *ui = "tests/testheadercontrols.ui";
367
368 if (!g_file_test (filename: ui, test: G_FILE_TEST_EXISTS))
369 {
370 g_warning ("Can't find %s", ui);
371 return;
372 }
373
374 builder = gtk_builder_new_from_file (filename: ui);
375
376 win = (GtkWidget *)gtk_builder_get_object (builder, name: "window");
377 gtk_window_set_application (GTK_WINDOW (win), application: app);
378
379 gtk_window_present (GTK_WINDOW (win));
380}
381
382/* technorama */
383
384static const char css[] =
385 ".main.background { "
386 " background-image: linear-gradient(to bottom, red, blue);"
387 " border-width: 0px; "
388 "}"
389 ".titlebar.backdrop { "
390 " background-image: none; "
391 " background-color: @bg_color; "
392 " border-radius: 10px 10px 0px 0px; "
393 "}"
394 ".titlebar { "
395 " background-image: linear-gradient(to bottom, white, @bg_color);"
396 " border-radius: 10px 10px 0px 0px; "
397 "}";
398
399static void
400on_bookmark_clicked (GtkButton *button, gpointer data)
401{
402 GtkWindow *window = GTK_WINDOW (data);
403 GtkWidget *chooser;
404
405 chooser = gtk_file_chooser_dialog_new (title: "File Chooser Test",
406 parent: window,
407 action: GTK_FILE_CHOOSER_ACTION_OPEN,
408 first_button_text: "_Close",
409 GTK_RESPONSE_CLOSE,
410 NULL);
411
412 g_signal_connect (chooser, "response",
413 G_CALLBACK (gtk_window_destroy), NULL);
414
415 gtk_widget_show (widget: chooser);
416}
417
418static void
419toggle_fullscreen (GtkButton *button, gpointer data)
420{
421 GtkWidget *window = GTK_WIDGET (data);
422 static gboolean fullscreen = FALSE;
423
424 if (fullscreen)
425 {
426 gtk_window_unfullscreen (GTK_WINDOW (window));
427 fullscreen = FALSE;
428 }
429 else
430 {
431 gtk_window_fullscreen (GTK_WINDOW (window));
432 fullscreen = TRUE;
433 }
434}
435
436static void
437change_header (GtkButton *button, gpointer data)
438{
439 GtkWidget *window = GTK_WIDGET (data);
440 GtkWidget *label;
441 GtkWidget *widget;
442 GtkWidget *header;
443
444 if (button && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
445 {
446 header = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10);
447 gtk_widget_add_css_class (widget: header, css_class: "titlebar");
448 gtk_widget_add_css_class (widget: header, css_class: "header-bar");
449 gtk_widget_set_margin_start (widget: header, margin: 10);
450 gtk_widget_set_margin_end (widget: header, margin: 10);
451 gtk_widget_set_margin_top (widget: header, margin: 10);
452 gtk_widget_set_margin_bottom (widget: header, margin: 10);
453 label = gtk_label_new (str: "Label");
454 gtk_box_append (GTK_BOX (header), child: label);
455 widget = gtk_level_bar_new ();
456 gtk_level_bar_set_value (GTK_LEVEL_BAR (widget), value: 0.4);
457 gtk_widget_set_hexpand (widget, TRUE);
458 gtk_box_append (GTK_BOX (header), child: widget);
459 }
460 else
461 {
462 header = gtk_header_bar_new ();
463 gtk_widget_add_css_class (widget: header, css_class: "titlebar");
464
465 widget = gtk_button_new_with_label (label: "_Close");
466 gtk_button_set_use_underline (GTK_BUTTON (widget), TRUE);
467 gtk_widget_add_css_class (widget, css_class: "suggested-action");
468 g_signal_connect_swapped (widget, "clicked", G_CALLBACK (gtk_window_destroy), window);
469
470 gtk_header_bar_pack_end (GTK_HEADER_BAR (header), child: widget);
471
472 widget= gtk_button_new_from_icon_name (icon_name: "bookmark-new-symbolic");
473 g_signal_connect (widget, "clicked", G_CALLBACK (on_bookmark_clicked), window);
474
475 gtk_header_bar_pack_start (GTK_HEADER_BAR (header), child: widget);
476 }
477
478 gtk_window_set_titlebar (GTK_WINDOW (window), titlebar: header);
479}
480
481static void
482create_technorama (GtkApplication *app)
483{
484 GtkWidget *window;
485 GtkWidget *box;
486 GtkWidget *footer;
487 GtkWidget *button;
488 GtkWidget *content;
489 GtkCssProvider *provider;
490
491 window = gtk_window_new ();
492 gtk_window_set_application (GTK_WINDOW (window), application: app);
493
494 gtk_widget_add_css_class (widget: window, css_class: "main");
495
496 provider = gtk_css_provider_new ();
497 gtk_css_provider_load_from_data (css_provider: provider, data: css, length: -1);
498 gtk_style_context_add_provider_for_display (display: gtk_widget_get_display (widget: window),
499 GTK_STYLE_PROVIDER (provider),
500 GTK_STYLE_PROVIDER_PRIORITY_USER);
501
502
503 change_header (NULL, data: window);
504
505 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
506 gtk_window_set_child (GTK_WINDOW (window), child: box);
507
508 content = gtk_image_new_from_icon_name (icon_name: "start-here-symbolic");
509 gtk_image_set_pixel_size (GTK_IMAGE (content), pixel_size: 512);
510 gtk_widget_set_vexpand (widget: content, TRUE);
511
512 gtk_box_append (GTK_BOX (box), child: content);
513
514 footer = gtk_action_bar_new ();
515 gtk_action_bar_set_center_widget (GTK_ACTION_BAR (footer), center_widget: gtk_check_button_new_with_label (label: "Middle"));
516 button = gtk_toggle_button_new_with_label (label: "Custom");
517 g_signal_connect (button, "clicked", G_CALLBACK (change_header), window);
518 gtk_action_bar_pack_start (GTK_ACTION_BAR (footer), child: button);
519 button = gtk_button_new_with_label (label: "Fullscreen");
520 gtk_action_bar_pack_end (GTK_ACTION_BAR (footer), child: button);
521 g_signal_connect (button, "clicked", G_CALLBACK (toggle_fullscreen), window);
522 gtk_box_append (GTK_BOX (box), child: footer);
523 gtk_widget_show (widget: window);
524}
525
526struct {
527 const char *name;
528 void (*cb) (GtkApplication *app);
529} buttons[] =
530{
531 { "Regular window", create_regular },
532 { "Headerbar as titlebar", create_headerbar_as_titlebar },
533 { "Headerbar inside window", create_headerbar_inside_window },
534 { "Headerbar overlaying content", create_headerbar_overlay },
535 { "Hiding headerbar", create_hiding_headerbar },
536 { "Fake headerbar", create_fake_headerbar },
537 { "Split headerbar", create_split_headerbar },
538 { "Stacked headerbar", create_stacked_headerbar },
539 { "Headerbar with controls", create_controls },
540 { "Technorama", create_technorama },
541};
542int n_buttons = sizeof (buttons) / sizeof (buttons[0]);
543
544static void
545app_activate_cb (GtkApplication *app)
546{
547 GtkWidget *window, *box;
548 int i;
549
550 window = gtk_application_window_new (application: app);
551 gtk_window_set_title (GTK_WINDOW (window), title: "Headerbar test");
552
553 box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
554 gtk_widget_set_halign (widget: box, align: GTK_ALIGN_CENTER);
555 gtk_widget_set_valign (widget: box, align: GTK_ALIGN_CENTER);
556 gtk_widget_add_css_class (widget: box, css_class: "linked");
557 gtk_window_set_child (GTK_WINDOW (window), child: box);
558
559 for (i = 0; i < n_buttons; i++)
560 {
561 GtkWidget *btn;
562
563 btn = gtk_button_new_with_label (label: buttons[i].name);
564 g_signal_connect_object (instance: btn,
565 detailed_signal: "clicked",
566 G_CALLBACK (buttons[i].cb),
567 gobject: app,
568 connect_flags: G_CONNECT_SWAPPED);
569 gtk_box_append (GTK_BOX (box), child: btn);
570 }
571
572 gtk_widget_show (widget: window);
573}
574
575int
576main (int argc,
577 char **argv)
578{
579 GtkApplication *app;
580
581 app = gtk_application_new (application_id: "org.gtk.Test.headerbar2", flags: 0);
582
583 g_signal_connect (app,
584 "activate",
585 G_CALLBACK (app_activate_cb),
586 NULL);
587
588 g_application_run (G_APPLICATION (app), argc, argv);
589
590 return 0;
591}
592

source code of gtk/tests/testheaderbar.c