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
71 typedef struct _GtkActionBarClass GtkActionBarClass;
72
73 struct _GtkActionBar
74 {
75 GtkWidget parent;
76
77 GtkWidget *center_box;
78 GtkWidget *start_box;
79 GtkWidget *end_box;
80 GtkWidget *revealer;
81 };
82
83 struct _GtkActionBarClass
84 {
85 GtkWidgetClass parent_class;
86 };
87
88 enum {
89 PROP_0,
90 PROP_REVEALED,
91 LAST_PROP
92 };
93 static GParamSpec *props[LAST_PROP] = { NULL, };
94
95 static void gtk_action_bar_buildable_interface_init (GtkBuildableIface *iface);
96
G_DEFINE_TYPE_WITH_CODE(GtkActionBar,gtk_action_bar,GTK_TYPE_WIDGET,G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_action_bar_buildable_interface_init))97 G_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
101 static void
102 gtk_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 (self, 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
120 static void
gtk_action_bar_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)121 gtk_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, gtk_action_bar_get_revealed (self));
132 break;
133 default:
134 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
135 break;
136 }
137 }
138
139 static void
gtk_action_bar_dispose(GObject * object)140 gtk_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
153 static void
gtk_action_bar_class_init(GtkActionBarClass * klass)154 gtk_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 ("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 (object_class, LAST_PROP, 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
186 static void
gtk_action_bar_init(GtkActionBar * self)187 gtk_action_bar_init (GtkActionBar *self)
188 {
189 GtkWidget *widget = GTK_WIDGET (self);
190
191 self->revealer = gtk_revealer_new ();
192 gtk_widget_set_parent (self->revealer, widget);
193
194 gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), TRUE);
195 gtk_revealer_set_transition_type (GTK_REVEALER (self->revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
196
197 self->start_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
198 self->end_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
199
200 gtk_widget_add_css_class (self->start_box, "start");
201 gtk_widget_add_css_class (self->end_box, "end");
202
203 self->center_box = gtk_center_box_new ();
204 gtk_center_box_set_start_widget (GTK_CENTER_BOX (self->center_box), self->start_box);
205 gtk_center_box_set_end_widget (GTK_CENTER_BOX (self->center_box), self->end_box);
206
207 gtk_revealer_set_child (GTK_REVEALER (self->revealer), self->center_box);
208 }
209
210 static GtkBuildableIface *parent_buildable_iface;
211
212 static void
gtk_action_bar_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const char * type)213 gtk_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 (type, "start") == 0)
221 gtk_action_bar_pack_start (self, GTK_WIDGET (child));
222 else if (g_strcmp0 (type, "center") == 0)
223 gtk_action_bar_set_center_widget (self, GTK_WIDGET (child));
224 else if (g_strcmp0 (type, "end") == 0)
225 gtk_action_bar_pack_end (self, GTK_WIDGET (child));
226 else if (type == NULL && GTK_IS_WIDGET (child))
227 gtk_action_bar_pack_start (self, GTK_WIDGET (child));
228 else
229 parent_buildable_iface->add_child (buildable, builder, child, type);
230 }
231
232 static void
gtk_action_bar_buildable_interface_init(GtkBuildableIface * iface)233 gtk_action_bar_buildable_interface_init (GtkBuildableIface *iface)
234 {
235 parent_buildable_iface = g_type_interface_peek_parent (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 */
247 void
gtk_action_bar_pack_start(GtkActionBar * action_bar,GtkWidget * child)248 gtk_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 */
262 void
gtk_action_bar_pack_end(GtkActionBar * action_bar,GtkWidget * child)263 gtk_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 */
276 void
gtk_action_bar_remove(GtkActionBar * action_bar,GtkWidget * child)277 gtk_action_bar_remove (GtkActionBar *action_bar,
278 GtkWidget *child)
279 {
280 if (gtk_widget_get_parent (child) == action_bar->start_box)
281 gtk_box_remove (GTK_BOX (action_bar->start_box), child);
282 else if (gtk_widget_get_parent (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 */
298 void
gtk_action_bar_set_center_widget(GtkActionBar * action_bar,GtkWidget * center_widget)299 gtk_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), 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 */
313 GtkWidget *
gtk_action_bar_get_center_widget(GtkActionBar * action_bar)314 gtk_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 */
328 GtkWidget *
gtk_action_bar_new(void)329 gtk_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 */
345 void
gtk_action_bar_set_revealed(GtkActionBar * action_bar,gboolean revealed)346 gtk_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), revealed);
355 g_object_notify_by_pspec (G_OBJECT (action_bar), 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 */
367 gboolean
gtk_action_bar_get_revealed(GtkActionBar * action_bar)368 gtk_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