1/*
2 * Copyright (c) 2013 - 2014 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 "gtkactionbar.h"
23#include "gtkintl.h"
24#include "gtkbuildable.h"
25#include "gtktypebuiltins.h"
26#include "gtkbox.h"
27#include "gtkrevealer.h"
28#include "gtkwidgetprivate.h"
29#include "gtkprivate.h"
30#include "gtkcenterbox.h"
31#include "gtkbinlayout.h"
32
33#include <string.h>
34
35/**
36 * GtkActionBar:
37 *
38 * `GtkActionBar` is designed to present contextual actions.
39 *
40 * ![An example GtkActionBar](action-bar.png)
41 *
42 * It is expected to be displayed below the content and expand
43 * horizontally to fill the area.
44 *
45 * It allows placing children at the start or the end. In addition, it
46 * contains an internal centered box which is centered with respect to
47 * the full width of the box, even if the children at either side take
48 * up different amounts of space.
49 *
50 * # CSS nodes
51 *
52 * ```
53 * actionbar
54 * ╰── revealer
55 * ╰── box
56 * ├── box.start
57 * │ ╰── [start children]
58 * ├── [center widget]
59 * ╰── box.end
60 * ╰── [end children]
61 * ```
62 *
63 * A `GtkActionBar`'s CSS node is called `actionbar`. It contains a `revealer`
64 * subnode, which contains a `box` subnode, which contains two `box` subnodes at
65 * the start and end of the action bar, with `start` and `end style classes
66 * respectively, as well as a center node that represents the center child.
67 *
68 * Each of the boxes contains children packed for that side.
69 */
70
71typedef struct _GtkActionBarClass GtkActionBarClass;
72
73struct _GtkActionBar
74{
75 GtkWidget parent;
76
77 GtkWidget *center_box;
78 GtkWidget *start_box;
79 GtkWidget *end_box;
80 GtkWidget *revealer;
81};
82
83struct _GtkActionBarClass
84{
85 GtkWidgetClass parent_class;
86};
87
88enum {
89 PROP_0,
90 PROP_REVEALED,
91 LAST_PROP
92};
93static GParamSpec *props[LAST_PROP] = { NULL, };
94
95static void gtk_action_bar_buildable_interface_init (GtkBuildableIface *iface);
96
97G_DEFINE_TYPE_WITH_CODE (GtkActionBar, gtk_action_bar, GTK_TYPE_WIDGET,
98 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
99 gtk_action_bar_buildable_interface_init))
100
101static void
102gtk_action_bar_set_property (GObject *object,
103 guint prop_id,
104 const GValue *value,
105 GParamSpec *pspec)
106{
107 GtkActionBar *self = GTK_ACTION_BAR (object);
108
109 switch (prop_id)
110 {
111 case PROP_REVEALED:
112 gtk_action_bar_set_revealed (action_bar: self, revealed: g_value_get_boolean (value));
113 break;
114 default:
115 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116 break;
117 }
118}
119
120static void
121gtk_action_bar_get_property (GObject *object,
122 guint prop_id,
123 GValue *value,
124 GParamSpec *pspec)
125{
126 GtkActionBar *self = GTK_ACTION_BAR (object);
127
128 switch (prop_id)
129 {
130 case PROP_REVEALED:
131 g_value_set_boolean (value, v_boolean: gtk_action_bar_get_revealed (action_bar: self));
132 break;
133 default:
134 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
135 break;
136 }
137}
138
139static void
140gtk_action_bar_dispose (GObject *object)
141{
142 GtkActionBar *self = GTK_ACTION_BAR (object);
143
144 g_clear_pointer (&self->revealer, gtk_widget_unparent);
145
146 self->center_box = NULL;
147 self->start_box = NULL;
148 self->end_box = NULL;
149
150 G_OBJECT_CLASS (gtk_action_bar_parent_class)->dispose (object);
151}
152
153static void
154gtk_action_bar_class_init (GtkActionBarClass *klass)
155{
156 GObjectClass *object_class;
157 GtkWidgetClass *widget_class;
158
159 object_class = G_OBJECT_CLASS (klass);
160 widget_class = GTK_WIDGET_CLASS (klass);
161
162 object_class->set_property = gtk_action_bar_set_property;
163 object_class->get_property = gtk_action_bar_get_property;
164 object_class->dispose = gtk_action_bar_dispose;
165
166 widget_class->focus = gtk_widget_focus_child;
167
168 /**
169 * GtkActionBar:revealed: (attributes org.gtk.Property.get=gtk_action_bar_get_revealed org.gtk.Property.set=gtk_action_bar_set_revealed)
170 *
171 * Controls whether the action bar shows its contents.
172 */
173 props[PROP_REVEALED] =
174 g_param_spec_boolean (name: "revealed",
175 P_("Reveal"),
176 P_("Controls whether the action bar shows its contents or not"),
177 TRUE,
178 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
179
180 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: props);
181
182 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
183 gtk_widget_class_set_css_name (widget_class, I_("actionbar"));
184}
185
186static void
187gtk_action_bar_init (GtkActionBar *self)
188{
189 GtkWidget *widget = GTK_WIDGET (self);
190
191 self->revealer = gtk_revealer_new ();
192 gtk_widget_set_parent (widget: self->revealer, parent: widget);
193
194 gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), TRUE);
195 gtk_revealer_set_transition_type (GTK_REVEALER (self->revealer), transition: GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
196
197 self->start_box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
198 self->end_box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
199
200 gtk_widget_add_css_class (widget: self->start_box, css_class: "start");
201 gtk_widget_add_css_class (widget: self->end_box, css_class: "end");
202
203 self->center_box = gtk_center_box_new ();
204 gtk_center_box_set_start_widget (GTK_CENTER_BOX (self->center_box), child: self->start_box);
205 gtk_center_box_set_end_widget (GTK_CENTER_BOX (self->center_box), child: self->end_box);
206
207 gtk_revealer_set_child (GTK_REVEALER (self->revealer), child: self->center_box);
208}
209
210static GtkBuildableIface *parent_buildable_iface;
211
212static void
213gtk_action_bar_buildable_add_child (GtkBuildable *buildable,
214 GtkBuilder *builder,
215 GObject *child,
216 const char *type)
217{
218 GtkActionBar *self = GTK_ACTION_BAR (buildable);
219
220 if (g_strcmp0 (str1: type, str2: "start") == 0)
221 gtk_action_bar_pack_start (action_bar: self, GTK_WIDGET (child));
222 else if (g_strcmp0 (str1: type, str2: "center") == 0)
223 gtk_action_bar_set_center_widget (action_bar: self, GTK_WIDGET (child));
224 else if (g_strcmp0 (str1: type, str2: "end") == 0)
225 gtk_action_bar_pack_end (action_bar: self, GTK_WIDGET (child));
226 else if (type == NULL && GTK_IS_WIDGET (child))
227 gtk_action_bar_pack_start (action_bar: self, GTK_WIDGET (child));
228 else
229 parent_buildable_iface->add_child (buildable, builder, child, type);
230}
231
232static void
233gtk_action_bar_buildable_interface_init (GtkBuildableIface *iface)
234{
235 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
236 iface->add_child = gtk_action_bar_buildable_add_child;
237}
238
239/**
240 * gtk_action_bar_pack_start:
241 * @action_bar: A `GtkActionBar`
242 * @child: the `GtkWidget` to be added to @action_bar
243 *
244 * Adds @child to @action_bar, packed with reference to the
245 * start of the @action_bar.
246 */
247void
248gtk_action_bar_pack_start (GtkActionBar *action_bar,
249 GtkWidget *child)
250{
251 gtk_box_append (GTK_BOX (action_bar->start_box), child);
252}
253
254/**
255 * gtk_action_bar_pack_end:
256 * @action_bar: A `GtkActionBar`
257 * @child: the `GtkWidget` to be added to @action_bar
258 *
259 * Adds @child to @action_bar, packed with reference to the
260 * end of the @action_bar.
261 */
262void
263gtk_action_bar_pack_end (GtkActionBar *action_bar,
264 GtkWidget *child)
265{
266 gtk_box_insert_child_after (GTK_BOX (action_bar->end_box), child, NULL);
267}
268
269/**
270 * gtk_action_bar_remove:
271 * @action_bar: a `GtkActionBar`
272 * @child: the `GtkWidget` to be removed
273 *
274 * Removes a child from @action_bar.
275 */
276void
277gtk_action_bar_remove (GtkActionBar *action_bar,
278 GtkWidget *child)
279{
280 if (gtk_widget_get_parent (widget: child) == action_bar->start_box)
281 gtk_box_remove (GTK_BOX (action_bar->start_box), child);
282 else if (gtk_widget_get_parent (widget: child) == action_bar->end_box)
283 gtk_box_remove (GTK_BOX (action_bar->end_box), child);
284 else if (child == gtk_center_box_get_center_widget (GTK_CENTER_BOX (action_bar->center_box)))
285 gtk_center_box_set_center_widget (GTK_CENTER_BOX (action_bar->center_box), NULL);
286 else
287 g_warning ("Can't remove non-child %s %p from GtkActionBar %p",
288 G_OBJECT_TYPE_NAME (child), child, action_bar);
289}
290
291/**
292 * gtk_action_bar_set_center_widget:
293 * @action_bar: a `GtkActionBar`
294 * @center_widget: (nullable): a widget to use for the center
295 *
296 * Sets the center widget for the `GtkActionBar`.
297 */
298void
299gtk_action_bar_set_center_widget (GtkActionBar *action_bar,
300 GtkWidget *center_widget)
301{
302 gtk_center_box_set_center_widget (GTK_CENTER_BOX (action_bar->center_box), child: center_widget);
303}
304
305/**
306 * gtk_action_bar_get_center_widget:
307 * @action_bar: a `GtkActionBar`
308 *
309 * Retrieves the center bar widget of the bar.
310 *
311 * Returns: (transfer none) (nullable): the center `GtkWidget`
312 */
313GtkWidget *
314gtk_action_bar_get_center_widget (GtkActionBar *action_bar)
315{
316 g_return_val_if_fail (GTK_IS_ACTION_BAR (action_bar), NULL);
317
318 return gtk_center_box_get_center_widget (GTK_CENTER_BOX (action_bar->center_box));
319}
320
321/**
322 * gtk_action_bar_new:
323 *
324 * Creates a new `GtkActionBar` widget.
325 *
326 * Returns: a new `GtkActionBar`
327 */
328GtkWidget *
329gtk_action_bar_new (void)
330{
331 return GTK_WIDGET (g_object_new (GTK_TYPE_ACTION_BAR, NULL));
332}
333
334/**
335 * gtk_action_bar_set_revealed: (attributes org.gtk.Method.set_property=revealed)
336 * @action_bar: a `GtkActionBar`
337 * @revealed: The new value of the property
338 *
339 * Reveals or conceals the content of the action bar.
340 *
341 * Note: this does not show or hide @action_bar in the
342 * [property@Gtk.Widget:visible] sense, so revealing has
343 * no effect if the action bar is hidden.
344 */
345void
346gtk_action_bar_set_revealed (GtkActionBar *action_bar,
347 gboolean revealed)
348{
349 g_return_if_fail (GTK_IS_ACTION_BAR (action_bar));
350
351 if (revealed == gtk_revealer_get_reveal_child (GTK_REVEALER (action_bar->revealer)))
352 return;
353
354 gtk_revealer_set_reveal_child (GTK_REVEALER (action_bar->revealer), reveal_child: revealed);
355 g_object_notify_by_pspec (G_OBJECT (action_bar), pspec: props[PROP_REVEALED]);
356}
357
358/**
359 * gtk_action_bar_get_revealed: (attributes org.gtk.Method.get_property=revealed)
360 * @action_bar: a `GtkActionBar`
361 *
362 * Gets whether the contents of the action bar are revealed.
363 *
364 * Returns: the current value of the [property@Gtk.ActionBar:revealed]
365 * property
366 */
367gboolean
368gtk_action_bar_get_revealed (GtkActionBar *action_bar)
369{
370 g_return_val_if_fail (GTK_IS_ACTION_BAR (action_bar), FALSE);
371
372 return gtk_revealer_get_reveal_child (GTK_REVEALER (action_bar->revealer));
373}
374

source code of gtk/gtk/gtkactionbar.c