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 | *  |
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 | |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 * |
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 * |
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 |
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 |
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 | |