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 #include "gtkstackswitcher.h"
22 #include "gtkradiobutton.h"
23 #include "gtklabel.h"
24 #include "gtkdnd.h"
25 #include "gtkdragdest.h"
26 #include "gtkorientable.h"
27 #include "gtkprivate.h"
28 #include "gtkintl.h"
29
30 /**
31 * SECTION:gtkstackswitcher
32 * @Short_description: A controller for GtkStack
33 * @Title: GtkStackSwitcher
34 * @See_also: #GtkStack
35 *
36 * The GtkStackSwitcher widget acts as a controller for a
37 * #GtkStack; it shows a row of buttons to switch between
38 * the various pages of the associated stack widget.
39 *
40 * All the content for the buttons comes from the child properties
41 * of the #GtkStack; the button visibility in a #GtkStackSwitcher
42 * widget is controlled by the visibility of the child in the
43 * #GtkStack.
44 *
45 * It is possible to associate multiple #GtkStackSwitcher widgets
46 * with the same #GtkStack widget.
47 *
48 * The GtkStackSwitcher widget was added in 3.10.
49 *
50 * # CSS nodes
51 *
52 * GtkStackSwitcher has a single CSS node named stackswitcher and
53 * style class .stack-switcher.
54 *
55 * When circumstances require it, GtkStackSwitcher adds the
56 * .needs-attention style class to the widgets representing the
57 * stack pages.
58 */
59
60 #define TIMEOUT_EXPAND 500
61
62 typedef struct _GtkStackSwitcherPrivate GtkStackSwitcherPrivate;
63 struct _GtkStackSwitcherPrivate
64 {
65 GtkStack *stack;
66 GHashTable *buttons;
67 gint icon_size;
68 gboolean in_child_changed;
69 GtkWidget *switch_button;
70 guint switch_timer;
71 };
72
73 enum {
74 PROP_0,
75 PROP_ICON_SIZE,
76 PROP_STACK
77 };
78
G_DEFINE_TYPE_WITH_PRIVATE(GtkStackSwitcher,gtk_stack_switcher,GTK_TYPE_BOX)79 G_DEFINE_TYPE_WITH_PRIVATE (GtkStackSwitcher, gtk_stack_switcher, GTK_TYPE_BOX)
80
81 static void
82 gtk_stack_switcher_init (GtkStackSwitcher *switcher)
83 {
84 GtkStyleContext *context;
85 GtkStackSwitcherPrivate *priv;
86
87 gtk_widget_set_has_window (GTK_WIDGET (switcher), FALSE);
88
89 priv = gtk_stack_switcher_get_instance_private (switcher);
90
91 priv->icon_size = GTK_ICON_SIZE_MENU;
92 priv->stack = NULL;
93 priv->buttons = g_hash_table_new (g_direct_hash, g_direct_equal);
94
95 context = gtk_widget_get_style_context (GTK_WIDGET (switcher));
96 gtk_style_context_add_class (context, "stack-switcher");
97 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
98
99 gtk_orientable_set_orientation (GTK_ORIENTABLE (switcher), GTK_ORIENTATION_HORIZONTAL);
100
101 gtk_drag_dest_set (GTK_WIDGET (switcher), 0, NULL, 0, 0);
102 gtk_drag_dest_set_track_motion (GTK_WIDGET (switcher), TRUE);
103 }
104
105 static void
on_button_clicked(GtkWidget * widget,GtkStackSwitcher * self)106 on_button_clicked (GtkWidget *widget,
107 GtkStackSwitcher *self)
108 {
109 GtkWidget *child;
110 GtkStackSwitcherPrivate *priv;
111
112 priv = gtk_stack_switcher_get_instance_private (self);
113
114 if (!priv->in_child_changed)
115 {
116 child = g_object_get_data (G_OBJECT (widget), "stack-child");
117 gtk_stack_set_visible_child (priv->stack, child);
118 }
119 }
120
121 static void
rebuild_child(GtkWidget * self,const gchar * icon_name,const gchar * title,gint icon_size)122 rebuild_child (GtkWidget *self,
123 const gchar *icon_name,
124 const gchar *title,
125 gint icon_size)
126 {
127 GtkStyleContext *context;
128 GtkWidget *button_child;
129
130 button_child = gtk_bin_get_child (GTK_BIN (self));
131 if (button_child != NULL)
132 gtk_widget_destroy (button_child);
133
134 button_child = NULL;
135 context = gtk_widget_get_style_context (GTK_WIDGET (self));
136
137 if (icon_name != NULL)
138 {
139 button_child = gtk_image_new_from_icon_name (icon_name, icon_size);
140 if (title != NULL)
141 gtk_widget_set_tooltip_text (GTK_WIDGET (self), title);
142
143 gtk_style_context_remove_class (context, "text-button");
144 gtk_style_context_add_class (context, "image-button");
145 }
146 else if (title != NULL)
147 {
148 button_child = gtk_label_new (title);
149
150 gtk_widget_set_tooltip_text (GTK_WIDGET (self), NULL);
151
152 gtk_style_context_remove_class (context, "image-button");
153 gtk_style_context_add_class (context, "text-button");
154 }
155
156 if (button_child)
157 {
158 gtk_widget_set_halign (GTK_WIDGET (button_child), GTK_ALIGN_CENTER);
159 gtk_widget_show_all (button_child);
160 gtk_container_add (GTK_CONTAINER (self), button_child);
161 }
162 }
163
164 static void
update_needs_attention(GtkWidget * widget,GtkWidget * button,gpointer data)165 update_needs_attention (GtkWidget *widget, GtkWidget *button, gpointer data)
166 {
167 GtkContainer *container;
168 gboolean needs_attention;
169 GtkStyleContext *context;
170
171 container = GTK_CONTAINER (data);
172 gtk_container_child_get (container, widget,
173 "needs-attention", &needs_attention,
174 NULL);
175
176 context = gtk_widget_get_style_context (button);
177 if (needs_attention)
178 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION);
179 else
180 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION);
181 }
182
183 static void
update_button(GtkStackSwitcher * self,GtkWidget * widget,GtkWidget * button)184 update_button (GtkStackSwitcher *self,
185 GtkWidget *widget,
186 GtkWidget *button)
187 {
188 gchar *title;
189 gchar *icon_name;
190 GtkStackSwitcherPrivate *priv;
191
192 priv = gtk_stack_switcher_get_instance_private (self);
193
194 gtk_container_child_get (GTK_CONTAINER (priv->stack), widget,
195 "title", &title,
196 "icon-name", &icon_name,
197 NULL);
198
199 rebuild_child (button, icon_name, title, priv->icon_size);
200
201 gtk_widget_set_visible (button, gtk_widget_get_visible (widget) && (title != NULL || icon_name != NULL));
202
203 g_free (title);
204 g_free (icon_name);
205
206 update_needs_attention (widget, button, priv->stack);
207 }
208
209 static void
on_title_icon_visible_updated(GtkWidget * widget,GParamSpec * pspec,GtkStackSwitcher * self)210 on_title_icon_visible_updated (GtkWidget *widget,
211 GParamSpec *pspec,
212 GtkStackSwitcher *self)
213 {
214 GtkWidget *button;
215 GtkStackSwitcherPrivate *priv;
216
217 priv = gtk_stack_switcher_get_instance_private (self);
218
219 button = g_hash_table_lookup (priv->buttons, widget);
220 update_button (self, widget, button);
221 }
222
223 static void
on_position_updated(GtkWidget * widget,GParamSpec * pspec,GtkStackSwitcher * self)224 on_position_updated (GtkWidget *widget,
225 GParamSpec *pspec,
226 GtkStackSwitcher *self)
227 {
228 GtkWidget *button;
229 gint position;
230 GtkStackSwitcherPrivate *priv;
231
232 priv = gtk_stack_switcher_get_instance_private (self);
233
234 button = g_hash_table_lookup (priv->buttons, widget);
235
236 gtk_container_child_get (GTK_CONTAINER (priv->stack), widget,
237 "position", &position,
238 NULL);
239
240 gtk_box_reorder_child (GTK_BOX (self), button, position);
241 }
242
243 static void
on_needs_attention_updated(GtkWidget * widget,GParamSpec * pspec,GtkStackSwitcher * self)244 on_needs_attention_updated (GtkWidget *widget,
245 GParamSpec *pspec,
246 GtkStackSwitcher *self)
247 {
248 GtkWidget *button;
249 GtkStackSwitcherPrivate *priv;
250
251 priv = gtk_stack_switcher_get_instance_private (self);
252
253 button = g_hash_table_lookup (priv->buttons, widget);
254 update_button (self, widget, button);
255 }
256
257 static void
remove_switch_timer(GtkStackSwitcher * self)258 remove_switch_timer (GtkStackSwitcher *self)
259 {
260 GtkStackSwitcherPrivate *priv;
261
262 priv = gtk_stack_switcher_get_instance_private (self);
263
264 if (priv->switch_timer)
265 {
266 g_source_remove (priv->switch_timer);
267 priv->switch_timer = 0;
268 }
269 }
270
271 static gboolean
gtk_stack_switcher_switch_timeout(gpointer data)272 gtk_stack_switcher_switch_timeout (gpointer data)
273 {
274 GtkStackSwitcher *self = data;
275 GtkStackSwitcherPrivate *priv;
276 GtkWidget *button;
277
278 priv = gtk_stack_switcher_get_instance_private (self);
279
280 priv->switch_timer = 0;
281
282 button = priv->switch_button;
283 priv->switch_button = NULL;
284
285 if (button)
286 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
287
288 return G_SOURCE_REMOVE;
289 }
290
291 static gboolean
gtk_stack_switcher_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)292 gtk_stack_switcher_drag_motion (GtkWidget *widget,
293 GdkDragContext *context,
294 gint x,
295 gint y,
296 guint time)
297 {
298 GtkStackSwitcher *self = GTK_STACK_SWITCHER (widget);
299 GtkStackSwitcherPrivate *priv;
300 GtkAllocation allocation;
301 GtkWidget *button;
302 GHashTableIter iter;
303 gpointer value;
304 gboolean retval = FALSE;
305
306 gtk_widget_get_allocation (widget, &allocation);
307
308 priv = gtk_stack_switcher_get_instance_private (self);
309
310 x += allocation.x;
311 y += allocation.y;
312
313 button = NULL;
314 g_hash_table_iter_init (&iter, priv->buttons);
315 while (g_hash_table_iter_next (&iter, NULL, &value))
316 {
317 gtk_widget_get_allocation (GTK_WIDGET (value), &allocation);
318 if (x >= allocation.x && x <= allocation.x + allocation.width &&
319 y >= allocation.y && y <= allocation.y + allocation.height)
320 {
321 button = GTK_WIDGET (value);
322 retval = TRUE;
323 break;
324 }
325 }
326
327 if (button != priv->switch_button)
328 remove_switch_timer (self);
329
330 priv->switch_button = button;
331
332 if (button && !priv->switch_timer)
333 {
334 priv->switch_timer = gdk_threads_add_timeout (TIMEOUT_EXPAND,
335 gtk_stack_switcher_switch_timeout,
336 self);
337 g_source_set_name_by_id (priv->switch_timer, "[gtk+] gtk_stack_switcher_switch_timeout");
338 }
339
340 return retval;
341 }
342
343 static void
gtk_stack_switcher_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time)344 gtk_stack_switcher_drag_leave (GtkWidget *widget,
345 GdkDragContext *context,
346 guint time)
347 {
348 GtkStackSwitcher *self = GTK_STACK_SWITCHER (widget);
349
350 remove_switch_timer (self);
351 }
352
353 static void
add_child(GtkWidget * widget,GtkStackSwitcher * self)354 add_child (GtkWidget *widget,
355 GtkStackSwitcher *self)
356 {
357 GtkWidget *button;
358 GList *group;
359 GtkStackSwitcherPrivate *priv;
360
361 priv = gtk_stack_switcher_get_instance_private (self);
362
363 button = gtk_radio_button_new (NULL);
364
365 gtk_widget_set_focus_on_click (button, FALSE);
366 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
367
368 update_button (self, widget, button);
369
370 group = gtk_container_get_children (GTK_CONTAINER (self));
371 if (group != NULL)
372 {
373 gtk_radio_button_join_group (GTK_RADIO_BUTTON (button), GTK_RADIO_BUTTON (group->data));
374 g_list_free (group);
375 }
376
377 gtk_container_add (GTK_CONTAINER (self), button);
378
379 g_object_set_data (G_OBJECT (button), "stack-child", widget);
380 g_signal_connect (button, "clicked", G_CALLBACK (on_button_clicked), self);
381 g_signal_connect (widget, "notify::visible", G_CALLBACK (on_title_icon_visible_updated), self);
382 g_signal_connect (widget, "child-notify::title", G_CALLBACK (on_title_icon_visible_updated), self);
383 g_signal_connect (widget, "child-notify::icon-name", G_CALLBACK (on_title_icon_visible_updated), self);
384 g_signal_connect (widget, "child-notify::position", G_CALLBACK (on_position_updated), self);
385 g_signal_connect (widget, "child-notify::needs-attention", G_CALLBACK (on_needs_attention_updated), self);
386
387 g_hash_table_insert (priv->buttons, widget, button);
388 }
389
390 static void
remove_child(GtkWidget * widget,GtkStackSwitcher * self)391 remove_child (GtkWidget *widget,
392 GtkStackSwitcher *self)
393 {
394 GtkWidget *button;
395 GtkStackSwitcherPrivate *priv;
396
397 priv = gtk_stack_switcher_get_instance_private (self);
398
399 g_signal_handlers_disconnect_by_func (widget, on_title_icon_visible_updated, self);
400 g_signal_handlers_disconnect_by_func (widget, on_position_updated, self);
401 g_signal_handlers_disconnect_by_func (widget, on_needs_attention_updated, self);
402
403 button = g_hash_table_lookup (priv->buttons, widget);
404 gtk_container_remove (GTK_CONTAINER (self), button);
405 g_hash_table_remove (priv->buttons, widget);
406 }
407
408 static void
populate_switcher(GtkStackSwitcher * self)409 populate_switcher (GtkStackSwitcher *self)
410 {
411 GtkStackSwitcherPrivate *priv;
412 GtkWidget *widget, *button;
413
414 priv = gtk_stack_switcher_get_instance_private (self);
415 gtk_container_foreach (GTK_CONTAINER (priv->stack), (GtkCallback)add_child, self);
416
417 widget = gtk_stack_get_visible_child (priv->stack);
418 if (widget)
419 {
420 button = g_hash_table_lookup (priv->buttons, widget);
421 priv->in_child_changed = TRUE;
422 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
423 priv->in_child_changed = FALSE;
424 }
425 }
426
427 static void
clear_switcher(GtkStackSwitcher * self)428 clear_switcher (GtkStackSwitcher *self)
429 {
430 GtkStackSwitcherPrivate *priv;
431
432 priv = gtk_stack_switcher_get_instance_private (self);
433 gtk_container_foreach (GTK_CONTAINER (priv->stack), (GtkCallback)remove_child, self);
434 }
435
436 static void
on_child_changed(GtkWidget * widget,GParamSpec * pspec,GtkStackSwitcher * self)437 on_child_changed (GtkWidget *widget,
438 GParamSpec *pspec,
439 GtkStackSwitcher *self)
440 {
441 GtkWidget *child;
442 GtkWidget *button;
443 GtkStackSwitcherPrivate *priv;
444
445 priv = gtk_stack_switcher_get_instance_private (self);
446
447 child = gtk_stack_get_visible_child (GTK_STACK (widget));
448 button = g_hash_table_lookup (priv->buttons, child);
449 if (button != NULL)
450 {
451 priv->in_child_changed = TRUE;
452 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
453 priv->in_child_changed = FALSE;
454 }
455 }
456
457 static void
on_stack_child_added(GtkContainer * container,GtkWidget * widget,GtkStackSwitcher * self)458 on_stack_child_added (GtkContainer *container,
459 GtkWidget *widget,
460 GtkStackSwitcher *self)
461 {
462 add_child (widget, self);
463 }
464
465 static void
on_stack_child_removed(GtkContainer * container,GtkWidget * widget,GtkStackSwitcher * self)466 on_stack_child_removed (GtkContainer *container,
467 GtkWidget *widget,
468 GtkStackSwitcher *self)
469 {
470 remove_child (widget, self);
471 }
472
473 static void
disconnect_stack_signals(GtkStackSwitcher * switcher)474 disconnect_stack_signals (GtkStackSwitcher *switcher)
475 {
476 GtkStackSwitcherPrivate *priv;
477
478 priv = gtk_stack_switcher_get_instance_private (switcher);
479 g_signal_handlers_disconnect_by_func (priv->stack, on_stack_child_added, switcher);
480 g_signal_handlers_disconnect_by_func (priv->stack, on_stack_child_removed, switcher);
481 g_signal_handlers_disconnect_by_func (priv->stack, on_child_changed, switcher);
482 g_signal_handlers_disconnect_by_func (priv->stack, disconnect_stack_signals, switcher);
483 }
484
485 static void
connect_stack_signals(GtkStackSwitcher * switcher)486 connect_stack_signals (GtkStackSwitcher *switcher)
487 {
488 GtkStackSwitcherPrivate *priv;
489
490 priv = gtk_stack_switcher_get_instance_private (switcher);
491 g_signal_connect_after (priv->stack, "add",
492 G_CALLBACK (on_stack_child_added), switcher);
493 g_signal_connect_after (priv->stack, "remove",
494 G_CALLBACK (on_stack_child_removed), switcher);
495 g_signal_connect (priv->stack, "notify::visible-child",
496 G_CALLBACK (on_child_changed), switcher);
497 g_signal_connect_swapped (priv->stack, "destroy",
498 G_CALLBACK (disconnect_stack_signals), switcher);
499 }
500
501 /**
502 * gtk_stack_switcher_set_stack:
503 * @switcher: a #GtkStackSwitcher
504 * @stack: (allow-none): a #GtkStack
505 *
506 * Sets the stack to control.
507 *
508 * Since: 3.10
509 */
510 void
gtk_stack_switcher_set_stack(GtkStackSwitcher * switcher,GtkStack * stack)511 gtk_stack_switcher_set_stack (GtkStackSwitcher *switcher,
512 GtkStack *stack)
513 {
514 GtkStackSwitcherPrivate *priv;
515
516 g_return_if_fail (GTK_IS_STACK_SWITCHER (switcher));
517 g_return_if_fail (GTK_IS_STACK (stack) || stack == NULL);
518
519 priv = gtk_stack_switcher_get_instance_private (switcher);
520
521 if (priv->stack == stack)
522 return;
523
524 if (priv->stack)
525 {
526 disconnect_stack_signals (switcher);
527 clear_switcher (switcher);
528 g_clear_object (&priv->stack);
529 }
530 if (stack)
531 {
532 priv->stack = g_object_ref (stack);
533 populate_switcher (switcher);
534 connect_stack_signals (switcher);
535 }
536
537 gtk_widget_queue_resize (GTK_WIDGET (switcher));
538
539 g_object_notify (G_OBJECT (switcher), "stack");
540 }
541
542 /**
543 * gtk_stack_switcher_get_stack:
544 * @switcher: a #GtkStackSwitcher
545 *
546 * Retrieves the stack.
547 * See gtk_stack_switcher_set_stack().
548 *
549 * Returns: (nullable) (transfer none): the stack, or %NULL if
550 * none has been set explicitly.
551 *
552 * Since: 3.10
553 */
554 GtkStack *
gtk_stack_switcher_get_stack(GtkStackSwitcher * switcher)555 gtk_stack_switcher_get_stack (GtkStackSwitcher *switcher)
556 {
557 GtkStackSwitcherPrivate *priv;
558 g_return_val_if_fail (GTK_IS_STACK_SWITCHER (switcher), NULL);
559
560 priv = gtk_stack_switcher_get_instance_private (switcher);
561 return priv->stack;
562 }
563
564 static void
gtk_stack_switcher_set_icon_size(GtkStackSwitcher * switcher,gint icon_size)565 gtk_stack_switcher_set_icon_size (GtkStackSwitcher *switcher,
566 gint icon_size)
567 {
568 GtkStackSwitcherPrivate *priv;
569
570 g_return_if_fail (GTK_IS_STACK_SWITCHER (switcher));
571
572 priv = gtk_stack_switcher_get_instance_private (switcher);
573
574 if (icon_size != priv->icon_size)
575 {
576 priv->icon_size = icon_size;
577
578 if (priv->stack != NULL)
579 {
580 clear_switcher (switcher);
581 populate_switcher (switcher);
582 }
583
584 g_object_notify (G_OBJECT (switcher), "icon-size");
585 }
586 }
587
588 static void
gtk_stack_switcher_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)589 gtk_stack_switcher_get_property (GObject *object,
590 guint prop_id,
591 GValue *value,
592 GParamSpec *pspec)
593 {
594 GtkStackSwitcher *switcher = GTK_STACK_SWITCHER (object);
595 GtkStackSwitcherPrivate *priv;
596
597 priv = gtk_stack_switcher_get_instance_private (switcher);
598 switch (prop_id)
599 {
600 case PROP_ICON_SIZE:
601 g_value_set_int (value, priv->icon_size);
602 break;
603
604 case PROP_STACK:
605 g_value_set_object (value, priv->stack);
606 break;
607
608 default:
609 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
610 break;
611 }
612 }
613
614 static void
gtk_stack_switcher_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)615 gtk_stack_switcher_set_property (GObject *object,
616 guint prop_id,
617 const GValue *value,
618 GParamSpec *pspec)
619 {
620 GtkStackSwitcher *switcher = GTK_STACK_SWITCHER (object);
621
622 switch (prop_id)
623 {
624 case PROP_ICON_SIZE:
625 gtk_stack_switcher_set_icon_size (switcher, g_value_get_int (value));
626 break;
627
628 case PROP_STACK:
629 gtk_stack_switcher_set_stack (switcher, g_value_get_object (value));
630 break;
631
632 default:
633 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
634 break;
635 }
636 }
637
638 static void
gtk_stack_switcher_dispose(GObject * object)639 gtk_stack_switcher_dispose (GObject *object)
640 {
641 GtkStackSwitcher *switcher = GTK_STACK_SWITCHER (object);
642
643 remove_switch_timer (switcher);
644 gtk_stack_switcher_set_stack (switcher, NULL);
645
646 G_OBJECT_CLASS (gtk_stack_switcher_parent_class)->dispose (object);
647 }
648
649 static void
gtk_stack_switcher_finalize(GObject * object)650 gtk_stack_switcher_finalize (GObject *object)
651 {
652 GtkStackSwitcher *switcher = GTK_STACK_SWITCHER (object);
653 GtkStackSwitcherPrivate *priv;
654
655 priv = gtk_stack_switcher_get_instance_private (switcher);
656
657 g_hash_table_destroy (priv->buttons);
658
659 G_OBJECT_CLASS (gtk_stack_switcher_parent_class)->finalize (object);
660 }
661
662 static void
gtk_stack_switcher_class_init(GtkStackSwitcherClass * class)663 gtk_stack_switcher_class_init (GtkStackSwitcherClass *class)
664 {
665 GObjectClass *object_class = G_OBJECT_CLASS (class);
666 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
667
668 object_class->get_property = gtk_stack_switcher_get_property;
669 object_class->set_property = gtk_stack_switcher_set_property;
670 object_class->dispose = gtk_stack_switcher_dispose;
671 object_class->finalize = gtk_stack_switcher_finalize;
672
673 widget_class->drag_motion = gtk_stack_switcher_drag_motion;
674 widget_class->drag_leave = gtk_stack_switcher_drag_leave;
675 /**
676 * GtkStackSwitcher:icon-size:
677 *
678 * Use the "icon-size" property to change the size of the image displayed
679 * when a #GtkStackSwitcher is displaying icons.
680 *
681 * Since: 3.20
682 */
683 g_object_class_install_property (object_class,
684 PROP_ICON_SIZE,
685 g_param_spec_int ("icon-size",
686 P_("Icon Size"),
687 P_("Symbolic size to use for named icon"),
688 0, G_MAXINT,
689 GTK_ICON_SIZE_MENU,
690 G_PARAM_EXPLICIT_NOTIFY | GTK_PARAM_READWRITE));
691
692 g_object_class_install_property (object_class,
693 PROP_STACK,
694 g_param_spec_object ("stack",
695 P_("Stack"),
696 P_("Stack"),
697 GTK_TYPE_STACK,
698 GTK_PARAM_READWRITE |
699 G_PARAM_CONSTRUCT));
700
701 gtk_widget_class_set_css_name (widget_class, "stackswitcher");
702 }
703
704 /**
705 * gtk_stack_switcher_new:
706 *
707 * Create a new #GtkStackSwitcher.
708 *
709 * Returns: a new #GtkStackSwitcher.
710 *
711 * Since: 3.10
712 */
713 GtkWidget *
gtk_stack_switcher_new(void)714 gtk_stack_switcher_new (void)
715 {
716 return g_object_new (GTK_TYPE_STACK_SWITCHER, NULL);
717 }
718