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 * Author: Alexander Larsson <alexl@redhat.com>
19 *
20 */
21
22 #include "config.h"
23
24 #include <gtk/gtk.h>
25 #include "gtkstack.h"
26 #include "gtkprivate.h"
27 #include "gtkintl.h"
28 #include "gtkcsscustomgadgetprivate.h"
29 #include "gtkcontainerprivate.h"
30 #include "gtkprogresstrackerprivate.h"
31 #include "gtksettingsprivate.h"
32 #include "gtkwidgetprivate.h"
33 #include "a11y/gtkstackaccessible.h"
34 #include "a11y/gtkstackaccessibleprivate.h"
35 #include <math.h>
36 #include <string.h>
37
38 /**
39 * SECTION:gtkstack
40 * @Short_description: A stacking container
41 * @Title: GtkStack
42 * @See_also: #GtkNotebook, #GtkStackSwitcher
43 *
44 * The GtkStack widget is a container which only shows
45 * one of its children at a time. In contrast to GtkNotebook,
46 * GtkStack does not provide a means for users to change the
47 * visible child. Instead, the #GtkStackSwitcher widget can be
48 * used with GtkStack to provide this functionality.
49 *
50 * Transitions between pages can be animated as slides or
51 * fades. This can be controlled with gtk_stack_set_transition_type().
52 * These animations respect the #GtkSettings:gtk-enable-animations
53 * setting.
54 *
55 * The GtkStack widget was added in GTK+ 3.10.
56 *
57 * # CSS nodes
58 *
59 * GtkStack has a single CSS node named stack.
60 */
61
62 /**
63 * GtkStackTransitionType:
64 * @GTK_STACK_TRANSITION_TYPE_NONE: No transition
65 * @GTK_STACK_TRANSITION_TYPE_CROSSFADE: A cross-fade
66 * @GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT: Slide from left to right
67 * @GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT: Slide from right to left
68 * @GTK_STACK_TRANSITION_TYPE_SLIDE_UP: Slide from bottom up
69 * @GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN: Slide from top down
70 * @GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT: Slide from left or right according to the children order
71 * @GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN: Slide from top down or bottom up according to the order
72 * @GTK_STACK_TRANSITION_TYPE_OVER_UP: Cover the old page by sliding up. Since 3.12
73 * @GTK_STACK_TRANSITION_TYPE_OVER_DOWN: Cover the old page by sliding down. Since: 3.12
74 * @GTK_STACK_TRANSITION_TYPE_OVER_LEFT: Cover the old page by sliding to the left. Since: 3.12
75 * @GTK_STACK_TRANSITION_TYPE_OVER_RIGHT: Cover the old page by sliding to the right. Since: 3.12
76 * @GTK_STACK_TRANSITION_TYPE_UNDER_UP: Uncover the new page by sliding up. Since 3.12
77 * @GTK_STACK_TRANSITION_TYPE_UNDER_DOWN: Uncover the new page by sliding down. Since: 3.12
78 * @GTK_STACK_TRANSITION_TYPE_UNDER_LEFT: Uncover the new page by sliding to the left. Since: 3.12
79 * @GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT: Uncover the new page by sliding to the right. Since: 3.12
80 * @GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN: Cover the old page sliding up or uncover the new page sliding down, according to order. Since: 3.12
81 * @GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP: Cover the old page sliding down or uncover the new page sliding up, according to order. Since: 3.14
82 * @GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT: Cover the old page sliding left or uncover the new page sliding right, according to order. Since: 3.14
83 * @GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT: Cover the old page sliding right or uncover the new page sliding left, according to order. Since: 3.14
84 *
85 * These enumeration values describe the possible transitions
86 * between pages in a #GtkStack widget.
87 *
88 * New values may be added to this enumeration over time.
89 */
90
91 /* TODO:
92 * filter events out events to the last_child widget during transitions
93 */
94
95 enum {
96 PROP_0,
97 PROP_HOMOGENEOUS,
98 PROP_HHOMOGENEOUS,
99 PROP_VHOMOGENEOUS,
100 PROP_VISIBLE_CHILD,
101 PROP_VISIBLE_CHILD_NAME,
102 PROP_TRANSITION_DURATION,
103 PROP_TRANSITION_TYPE,
104 PROP_TRANSITION_RUNNING,
105 PROP_INTERPOLATE_SIZE,
106 LAST_PROP
107 };
108
109 enum
110 {
111 CHILD_PROP_0,
112 CHILD_PROP_NAME,
113 CHILD_PROP_TITLE,
114 CHILD_PROP_ICON_NAME,
115 CHILD_PROP_POSITION,
116 CHILD_PROP_NEEDS_ATTENTION,
117 LAST_CHILD_PROP
118 };
119
120 typedef struct _GtkStackChildInfo GtkStackChildInfo;
121
122 struct _GtkStackChildInfo {
123 GtkWidget *widget;
124 gchar *name;
125 gchar *title;
126 gchar *icon_name;
127 gboolean needs_attention;
128 GtkWidget *last_focus;
129 };
130
131 typedef struct {
132 GList *children;
133
134 GdkWindow* bin_window;
135 GdkWindow* view_window;
136
137 GtkStackChildInfo *visible_child;
138
139 GtkCssGadget *gadget;
140
141 gboolean hhomogeneous;
142 gboolean vhomogeneous;
143
144 GtkStackTransitionType transition_type;
145 guint transition_duration;
146
147 GtkStackChildInfo *last_visible_child;
148 cairo_surface_t *last_visible_surface;
149 GtkAllocation last_visible_surface_allocation;
150 guint tick_id;
151 GtkProgressTracker tracker;
152 gboolean first_frame_skipped;
153
154 gint last_visible_widget_width;
155 gint last_visible_widget_height;
156
157 gboolean interpolate_size;
158
159 GtkStackTransitionType active_transition_type;
160
161 } GtkStackPrivate;
162
163 static GParamSpec *stack_props[LAST_PROP] = { NULL, };
164 static GParamSpec *stack_child_props[LAST_CHILD_PROP] = { NULL, };
165
166 static void gtk_stack_add (GtkContainer *widget,
167 GtkWidget *child);
168 static void gtk_stack_remove (GtkContainer *widget,
169 GtkWidget *child);
170 static void gtk_stack_forall (GtkContainer *container,
171 gboolean include_internals,
172 GtkCallback callback,
173 gpointer callback_data);
174 static void gtk_stack_compute_expand (GtkWidget *widget,
175 gboolean *hexpand,
176 gboolean *vexpand);
177 static void gtk_stack_size_allocate (GtkWidget *widget,
178 GtkAllocation *allocation);
179 static gboolean gtk_stack_draw (GtkWidget *widget,
180 cairo_t *cr);
181 static void gtk_stack_get_preferred_height (GtkWidget *widget,
182 gint *minimum_height,
183 gint *natural_height);
184 static void gtk_stack_get_preferred_height_for_width (GtkWidget *widget,
185 gint width,
186 gint *minimum_height,
187 gint *natural_height);
188 static void gtk_stack_get_preferred_width (GtkWidget *widget,
189 gint *minimum_width,
190 gint *natural_width);
191 static void gtk_stack_get_preferred_width_for_height (GtkWidget *widget,
192 gint height,
193 gint *minimum_width,
194 gint *natural_width);
195 static void gtk_stack_finalize (GObject *obj);
196 static void gtk_stack_get_property (GObject *object,
197 guint property_id,
198 GValue *value,
199 GParamSpec *pspec);
200 static void gtk_stack_set_property (GObject *object,
201 guint property_id,
202 const GValue *value,
203 GParamSpec *pspec);
204 static void gtk_stack_get_child_property (GtkContainer *container,
205 GtkWidget *child,
206 guint property_id,
207 GValue *value,
208 GParamSpec *pspec);
209 static void gtk_stack_set_child_property (GtkContainer *container,
210 GtkWidget *child,
211 guint property_id,
212 const GValue *value,
213 GParamSpec *pspec);
214 static void gtk_stack_unschedule_ticks (GtkStack *stack);
215 static gint get_bin_window_x (GtkStack *stack,
216 const GtkAllocation *allocation);
217 static gint get_bin_window_y (GtkStack *stack,
218 const GtkAllocation *allocation);
219
G_DEFINE_TYPE_WITH_PRIVATE(GtkStack,gtk_stack,GTK_TYPE_CONTAINER)220 G_DEFINE_TYPE_WITH_PRIVATE (GtkStack, gtk_stack, GTK_TYPE_CONTAINER)
221
222 static void
223 gtk_stack_dispose (GObject *obj)
224 {
225 GtkStack *stack = GTK_STACK (obj);
226 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
227
228 priv->visible_child = NULL;
229
230 G_OBJECT_CLASS (gtk_stack_parent_class)->dispose (obj);
231 }
232
233 static void
gtk_stack_finalize(GObject * obj)234 gtk_stack_finalize (GObject *obj)
235 {
236 GtkStack *stack = GTK_STACK (obj);
237 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
238
239 gtk_stack_unschedule_ticks (stack);
240
241 if (priv->last_visible_surface != NULL)
242 cairo_surface_destroy (priv->last_visible_surface);
243
244 g_clear_object (&priv->gadget);
245
246 G_OBJECT_CLASS (gtk_stack_parent_class)->finalize (obj);
247 }
248
249 static void
gtk_stack_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)250 gtk_stack_get_property (GObject *object,
251 guint property_id,
252 GValue *value,
253 GParamSpec *pspec)
254 {
255 GtkStack *stack = GTK_STACK (object);
256
257 switch (property_id)
258 {
259 case PROP_HOMOGENEOUS:
260 g_value_set_boolean (value, gtk_stack_get_homogeneous (stack));
261 break;
262 case PROP_HHOMOGENEOUS:
263 g_value_set_boolean (value, gtk_stack_get_hhomogeneous (stack));
264 break;
265 case PROP_VHOMOGENEOUS:
266 g_value_set_boolean (value, gtk_stack_get_vhomogeneous (stack));
267 break;
268 case PROP_VISIBLE_CHILD:
269 g_value_set_object (value, gtk_stack_get_visible_child (stack));
270 break;
271 case PROP_VISIBLE_CHILD_NAME:
272 g_value_set_string (value, gtk_stack_get_visible_child_name (stack));
273 break;
274 case PROP_TRANSITION_DURATION:
275 g_value_set_uint (value, gtk_stack_get_transition_duration (stack));
276 break;
277 case PROP_TRANSITION_TYPE:
278 g_value_set_enum (value, gtk_stack_get_transition_type (stack));
279 break;
280 case PROP_TRANSITION_RUNNING:
281 g_value_set_boolean (value, gtk_stack_get_transition_running (stack));
282 break;
283 case PROP_INTERPOLATE_SIZE:
284 g_value_set_boolean (value, gtk_stack_get_interpolate_size (stack));
285 break;
286 default:
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
288 break;
289 }
290 }
291
292 static void
gtk_stack_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)293 gtk_stack_set_property (GObject *object,
294 guint property_id,
295 const GValue *value,
296 GParamSpec *pspec)
297 {
298 GtkStack *stack = GTK_STACK (object);
299
300 switch (property_id)
301 {
302 case PROP_HOMOGENEOUS:
303 gtk_stack_set_homogeneous (stack, g_value_get_boolean (value));
304 break;
305 case PROP_HHOMOGENEOUS:
306 gtk_stack_set_hhomogeneous (stack, g_value_get_boolean (value));
307 break;
308 case PROP_VHOMOGENEOUS:
309 gtk_stack_set_vhomogeneous (stack, g_value_get_boolean (value));
310 break;
311 case PROP_VISIBLE_CHILD:
312 gtk_stack_set_visible_child (stack, g_value_get_object (value));
313 break;
314 case PROP_VISIBLE_CHILD_NAME:
315 gtk_stack_set_visible_child_name (stack, g_value_get_string (value));
316 break;
317 case PROP_TRANSITION_DURATION:
318 gtk_stack_set_transition_duration (stack, g_value_get_uint (value));
319 break;
320 case PROP_TRANSITION_TYPE:
321 gtk_stack_set_transition_type (stack, g_value_get_enum (value));
322 break;
323 case PROP_INTERPOLATE_SIZE:
324 gtk_stack_set_interpolate_size (stack, g_value_get_boolean (value));
325 break;
326 default:
327 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
328 break;
329 }
330 }
331
332 static void
gtk_stack_realize(GtkWidget * widget)333 gtk_stack_realize (GtkWidget *widget)
334 {
335 GtkStack *stack = GTK_STACK (widget);
336 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
337 GtkAllocation allocation;
338 GdkWindowAttr attributes = { 0 };
339 GdkWindowAttributesType attributes_mask;
340 GtkStackChildInfo *info;
341 GList *l;
342
343 gtk_widget_set_realized (widget, TRUE);
344 gtk_widget_set_window (widget, g_object_ref (gtk_widget_get_parent_window (widget)));
345
346 gtk_css_gadget_get_content_allocation (priv->gadget, &allocation, NULL);
347
348 attributes.x = allocation.x;
349 attributes.y = allocation.y;
350 attributes.width = allocation.width;
351 attributes.height = allocation.height;
352 attributes.window_type = GDK_WINDOW_CHILD;
353 attributes.wclass = GDK_INPUT_OUTPUT;
354 attributes.visual = gtk_widget_get_visual (widget);
355 attributes.event_mask =
356 gtk_widget_get_events (widget);
357 attributes_mask = (GDK_WA_X | GDK_WA_Y) | GDK_WA_VISUAL;
358
359 priv->view_window =
360 gdk_window_new (gtk_widget_get_window (GTK_WIDGET (stack)),
361 &attributes, attributes_mask);
362 gtk_widget_register_window (widget, priv->view_window);
363
364 attributes.x = get_bin_window_x (stack, &allocation);
365 attributes.y = get_bin_window_y (stack, &allocation);
366 attributes.width = allocation.width;
367 attributes.height = allocation.height;
368
369 for (l = priv->children; l != NULL; l = l->next)
370 {
371 info = l->data;
372 attributes.event_mask |= gtk_widget_get_events (info->widget);
373 }
374
375 priv->bin_window =
376 gdk_window_new (priv->view_window, &attributes, attributes_mask);
377 gtk_widget_register_window (widget, priv->bin_window);
378
379 for (l = priv->children; l != NULL; l = l->next)
380 {
381 info = l->data;
382
383 gtk_widget_set_parent_window (info->widget, priv->bin_window);
384 }
385
386 gdk_window_show (priv->bin_window);
387 }
388
389 static void
gtk_stack_unrealize(GtkWidget * widget)390 gtk_stack_unrealize (GtkWidget *widget)
391 {
392 GtkStack *stack = GTK_STACK (widget);
393 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
394
395 gtk_widget_unregister_window (widget, priv->bin_window);
396 gdk_window_destroy (priv->bin_window);
397 priv->bin_window = NULL;
398 gtk_widget_unregister_window (widget, priv->view_window);
399 gdk_window_destroy (priv->view_window);
400 priv->view_window = NULL;
401
402 GTK_WIDGET_CLASS (gtk_stack_parent_class)->unrealize (widget);
403 }
404
405 static void
gtk_stack_map(GtkWidget * widget)406 gtk_stack_map (GtkWidget *widget)
407 {
408 GtkStack *stack = GTK_STACK (widget);
409 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
410
411 GTK_WIDGET_CLASS (gtk_stack_parent_class)->map (widget);
412
413 gdk_window_show (priv->view_window);
414 }
415
416 static void
gtk_stack_unmap(GtkWidget * widget)417 gtk_stack_unmap (GtkWidget *widget)
418 {
419 GtkStack *stack = GTK_STACK (widget);
420 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
421
422 gdk_window_hide (priv->view_window);
423
424 GTK_WIDGET_CLASS (gtk_stack_parent_class)->unmap (widget);
425 }
426
427 static void
gtk_stack_class_init(GtkStackClass * klass)428 gtk_stack_class_init (GtkStackClass *klass)
429 {
430 GObjectClass *object_class = G_OBJECT_CLASS (klass);
431 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
432 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
433
434 object_class->get_property = gtk_stack_get_property;
435 object_class->set_property = gtk_stack_set_property;
436 object_class->dispose = gtk_stack_dispose;
437 object_class->finalize = gtk_stack_finalize;
438
439 widget_class->size_allocate = gtk_stack_size_allocate;
440 widget_class->draw = gtk_stack_draw;
441 widget_class->realize = gtk_stack_realize;
442 widget_class->unrealize = gtk_stack_unrealize;
443 widget_class->map = gtk_stack_map;
444 widget_class->unmap = gtk_stack_unmap;
445 widget_class->get_preferred_height = gtk_stack_get_preferred_height;
446 widget_class->get_preferred_height_for_width = gtk_stack_get_preferred_height_for_width;
447 widget_class->get_preferred_width = gtk_stack_get_preferred_width;
448 widget_class->get_preferred_width_for_height = gtk_stack_get_preferred_width_for_height;
449 widget_class->compute_expand = gtk_stack_compute_expand;
450
451 container_class->add = gtk_stack_add;
452 container_class->remove = gtk_stack_remove;
453 container_class->forall = gtk_stack_forall;
454 container_class->set_child_property = gtk_stack_set_child_property;
455 container_class->get_child_property = gtk_stack_get_child_property;
456 gtk_container_class_handle_border_width (container_class);
457
458 stack_props[PROP_HOMOGENEOUS] =
459 g_param_spec_boolean ("homogeneous", P_("Homogeneous"), P_("Homogeneous sizing"),
460 TRUE,
461 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
462
463 /**
464 * GtkStack:hhomogeneous:
465 *
466 * %TRUE if the stack allocates the same width for all children.
467 *
468 * Since: 3.16
469 */
470 stack_props[PROP_HHOMOGENEOUS] =
471 g_param_spec_boolean ("hhomogeneous", P_("Horizontally homogeneous"), P_("Horizontally homogeneous sizing"),
472 TRUE,
473 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
474
475 /**
476 * GtkStack:vhomogeneous:
477 *
478 * %TRUE if the stack allocates the same height for all children.
479 *
480 * Since: 3.16
481 */
482 stack_props[PROP_VHOMOGENEOUS] =
483 g_param_spec_boolean ("vhomogeneous", P_("Vertically homogeneous"), P_("Vertically homogeneous sizing"),
484 TRUE,
485 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
486 stack_props[PROP_VISIBLE_CHILD] =
487 g_param_spec_object ("visible-child", P_("Visible child"), P_("The widget currently visible in the stack"),
488 GTK_TYPE_WIDGET,
489 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
490 stack_props[PROP_VISIBLE_CHILD_NAME] =
491 g_param_spec_string ("visible-child-name", P_("Name of visible child"), P_("The name of the widget currently visible in the stack"),
492 NULL,
493 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
494 stack_props[PROP_TRANSITION_DURATION] =
495 g_param_spec_uint ("transition-duration", P_("Transition duration"), P_("The animation duration, in milliseconds"),
496 0, G_MAXUINT, 200,
497 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
498 stack_props[PROP_TRANSITION_TYPE] =
499 g_param_spec_enum ("transition-type", P_("Transition type"), P_("The type of animation used to transition"),
500 GTK_TYPE_STACK_TRANSITION_TYPE, GTK_STACK_TRANSITION_TYPE_NONE,
501 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
502 stack_props[PROP_TRANSITION_RUNNING] =
503 g_param_spec_boolean ("transition-running", P_("Transition running"), P_("Whether or not the transition is currently running"),
504 FALSE,
505 GTK_PARAM_READABLE);
506 stack_props[PROP_INTERPOLATE_SIZE] =
507 g_param_spec_boolean ("interpolate-size", P_("Interpolate size"), P_("Whether or not the size should smoothly change when changing between differently sized children"),
508 FALSE,
509 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
510
511
512 g_object_class_install_properties (object_class, LAST_PROP, stack_props);
513
514 stack_child_props[CHILD_PROP_NAME] =
515 g_param_spec_string ("name",
516 P_("Name"),
517 P_("The name of the child page"),
518 NULL,
519 GTK_PARAM_READWRITE);
520
521 stack_child_props[CHILD_PROP_TITLE] =
522 g_param_spec_string ("title",
523 P_("Title"),
524 P_("The title of the child page"),
525 NULL,
526 GTK_PARAM_READWRITE);
527
528 stack_child_props[CHILD_PROP_ICON_NAME] =
529 g_param_spec_string ("icon-name",
530 P_("Icon name"),
531 P_("The icon name of the child page"),
532 NULL,
533 GTK_PARAM_READWRITE);
534
535 stack_child_props[CHILD_PROP_POSITION] =
536 g_param_spec_int ("position",
537 P_("Position"),
538 P_("The index of the child in the parent"),
539 -1, G_MAXINT,
540 0,
541 GTK_PARAM_READWRITE);
542
543 /**
544 * GtkStack:needs-attention:
545 *
546 * Sets a flag specifying whether the child requires the user attention.
547 * This is used by the #GtkStackSwitcher to change the appearance of the
548 * corresponding button when a page needs attention and it is not the
549 * current one.
550 *
551 * Since: 3.12
552 */
553 stack_child_props[CHILD_PROP_NEEDS_ATTENTION] =
554 g_param_spec_boolean ("needs-attention",
555 P_("Needs Attention"),
556 P_("Whether this page needs attention"),
557 FALSE,
558 GTK_PARAM_READWRITE);
559
560 gtk_container_class_install_child_properties (container_class, LAST_CHILD_PROP, stack_child_props);
561
562 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_STACK_ACCESSIBLE);
563 gtk_widget_class_set_css_name (widget_class, "stack");
564 }
565
566 /**
567 * gtk_stack_new:
568 *
569 * Creates a new #GtkStack container.
570 *
571 * Returns: a new #GtkStack
572 *
573 * Since: 3.10
574 */
575 GtkWidget *
gtk_stack_new(void)576 gtk_stack_new (void)
577 {
578 return g_object_new (GTK_TYPE_STACK, NULL);
579 }
580
581 static GtkStackChildInfo *
find_child_info_for_widget(GtkStack * stack,GtkWidget * child)582 find_child_info_for_widget (GtkStack *stack,
583 GtkWidget *child)
584 {
585 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
586 GtkStackChildInfo *info;
587 GList *l;
588
589 for (l = priv->children; l != NULL; l = l->next)
590 {
591 info = l->data;
592 if (info->widget == child)
593 return info;
594 }
595
596 return NULL;
597 }
598
599 static void
reorder_child(GtkStack * stack,GtkWidget * child,gint position)600 reorder_child (GtkStack *stack,
601 GtkWidget *child,
602 gint position)
603 {
604 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
605 GList *l;
606 GList *old_link = NULL;
607 GList *new_link = NULL;
608 GtkStackChildInfo *child_info = NULL;
609 gint num = 0;
610
611 l = priv->children;
612
613 /* Loop to find the old position and link of child, new link of child and
614 * total number of children. new_link will be NULL if the child should be
615 * moved to the end (in case of position being < 0 || >= num)
616 */
617 while (l && (new_link == NULL || old_link == NULL))
618 {
619 /* Record the new position if found */
620 if (position == num)
621 new_link = l;
622
623 if (old_link == NULL)
624 {
625 GtkStackChildInfo *info;
626 info = l->data;
627
628 /* Keep trying to find the current position and link location of the child */
629 if (info->widget == child)
630 {
631 old_link = l;
632 child_info = info;
633 }
634 }
635
636 l = l->next;
637 num++;
638 }
639
640 g_return_if_fail (old_link != NULL);
641
642 if (old_link == new_link || (old_link->next == NULL && new_link == NULL))
643 return;
644
645 priv->children = g_list_delete_link (priv->children, old_link);
646 priv->children = g_list_insert_before (priv->children, new_link, child_info);
647
648 gtk_container_child_notify_by_pspec (GTK_CONTAINER (stack), child, stack_child_props[CHILD_PROP_POSITION]);
649 }
650
651 static void
gtk_stack_get_child_property(GtkContainer * container,GtkWidget * child,guint property_id,GValue * value,GParamSpec * pspec)652 gtk_stack_get_child_property (GtkContainer *container,
653 GtkWidget *child,
654 guint property_id,
655 GValue *value,
656 GParamSpec *pspec)
657 {
658 GtkStack *stack = GTK_STACK (container);
659 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
660 GtkStackChildInfo *info;
661
662 info = find_child_info_for_widget (stack, child);
663 if (info == NULL)
664 {
665 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
666 return;
667 }
668
669 switch (property_id)
670 {
671 case CHILD_PROP_NAME:
672 g_value_set_string (value, info->name);
673 break;
674
675 case CHILD_PROP_TITLE:
676 g_value_set_string (value, info->title);
677 break;
678
679 case CHILD_PROP_ICON_NAME:
680 g_value_set_string (value, info->icon_name);
681 break;
682
683 case CHILD_PROP_POSITION:
684 g_value_set_int (value, g_list_index (priv->children, info));
685 break;
686
687 case CHILD_PROP_NEEDS_ATTENTION:
688 g_value_set_boolean (value, info->needs_attention);
689 break;
690
691 default:
692 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
693 break;
694 }
695 }
696
697 static void
gtk_stack_set_child_property(GtkContainer * container,GtkWidget * child,guint property_id,const GValue * value,GParamSpec * pspec)698 gtk_stack_set_child_property (GtkContainer *container,
699 GtkWidget *child,
700 guint property_id,
701 const GValue *value,
702 GParamSpec *pspec)
703 {
704 GtkStack *stack = GTK_STACK (container);
705 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
706 GtkStackChildInfo *info;
707 GtkStackChildInfo *info2;
708 gchar *name;
709 GList *l;
710
711 info = find_child_info_for_widget (stack, child);
712 if (info == NULL)
713 {
714 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
715 return;
716 }
717
718 switch (property_id)
719 {
720 case CHILD_PROP_NAME:
721 name = g_value_dup_string (value);
722 for (l = priv->children; l != NULL; l = l->next)
723 {
724 info2 = l->data;
725 if (info == info2)
726 continue;
727 if (g_strcmp0 (info2->name, name) == 0)
728 {
729 g_warning ("Duplicate child name in GtkStack: %s", name);
730 break;
731 }
732 }
733
734 g_free (info->name);
735 info->name = name;
736
737 gtk_container_child_notify_by_pspec (container, child, pspec);
738
739 if (priv->visible_child == info)
740 g_object_notify_by_pspec (G_OBJECT (stack),
741 stack_props[PROP_VISIBLE_CHILD_NAME]);
742
743 break;
744
745 case CHILD_PROP_TITLE:
746 g_free (info->title);
747 info->title = g_value_dup_string (value);
748 gtk_container_child_notify_by_pspec (container, child, pspec);
749 break;
750
751 case CHILD_PROP_ICON_NAME:
752 g_free (info->icon_name);
753 info->icon_name = g_value_dup_string (value);
754 gtk_container_child_notify_by_pspec (container, child, pspec);
755 break;
756
757 case CHILD_PROP_POSITION:
758 reorder_child (stack, child, g_value_get_int (value));
759 break;
760
761 case CHILD_PROP_NEEDS_ATTENTION:
762 info->needs_attention = g_value_get_boolean (value);
763 gtk_container_child_notify_by_pspec (container, child, pspec);
764 break;
765
766 default:
767 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
768 break;
769 }
770 }
771
772 static inline gboolean
is_left_transition(GtkStackTransitionType transition_type)773 is_left_transition (GtkStackTransitionType transition_type)
774 {
775 return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT ||
776 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_LEFT);
777 }
778
779 static inline gboolean
is_right_transition(GtkStackTransitionType transition_type)780 is_right_transition (GtkStackTransitionType transition_type)
781 {
782 return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT ||
783 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_RIGHT);
784 }
785
786 static inline gboolean
is_up_transition(GtkStackTransitionType transition_type)787 is_up_transition (GtkStackTransitionType transition_type)
788 {
789 return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_UP ||
790 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_UP);
791 }
792
793 static inline gboolean
is_down_transition(GtkStackTransitionType transition_type)794 is_down_transition (GtkStackTransitionType transition_type)
795 {
796 return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN ||
797 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_DOWN);
798 }
799
800 /* Transitions that cause the bin window to move */
801 static inline gboolean
is_window_moving_transition(GtkStackTransitionType transition_type)802 is_window_moving_transition (GtkStackTransitionType transition_type)
803 {
804 return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT ||
805 transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT ||
806 transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_UP ||
807 transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN ||
808 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_UP ||
809 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_DOWN ||
810 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_LEFT ||
811 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_RIGHT);
812 }
813
814 /* Transitions that change direction depending on the relative order of the
815 old and new child */
816 static inline gboolean
is_direction_dependent_transition(GtkStackTransitionType transition_type)817 is_direction_dependent_transition (GtkStackTransitionType transition_type)
818 {
819 return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT ||
820 transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN ||
821 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN ||
822 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP ||
823 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT ||
824 transition_type == GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT);
825 }
826
827 /* Returns simple transition type for a direction dependent transition, given
828 whether the new child (the one being switched to) is first in the stacking order
829 (added earlier). */
830 static inline GtkStackTransitionType
get_simple_transition_type(gboolean new_child_first,GtkStackTransitionType transition_type)831 get_simple_transition_type (gboolean new_child_first,
832 GtkStackTransitionType transition_type)
833 {
834 switch (transition_type)
835 {
836 case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT:
837 return new_child_first ? GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT : GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT;
838 case GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN:
839 return new_child_first ? GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN : GTK_STACK_TRANSITION_TYPE_SLIDE_UP;
840 case GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN:
841 return new_child_first ? GTK_STACK_TRANSITION_TYPE_UNDER_DOWN : GTK_STACK_TRANSITION_TYPE_OVER_UP;
842 case GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP:
843 return new_child_first ? GTK_STACK_TRANSITION_TYPE_UNDER_UP : GTK_STACK_TRANSITION_TYPE_OVER_DOWN;
844 case GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT:
845 return new_child_first ? GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT : GTK_STACK_TRANSITION_TYPE_OVER_LEFT;
846 case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT:
847 return new_child_first ? GTK_STACK_TRANSITION_TYPE_UNDER_LEFT : GTK_STACK_TRANSITION_TYPE_OVER_RIGHT;
848 default: ;
849 }
850 return transition_type;
851 }
852
853 static gint
get_bin_window_x(GtkStack * stack,const GtkAllocation * allocation)854 get_bin_window_x (GtkStack *stack,
855 const GtkAllocation *allocation)
856 {
857 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
858 int x = 0;
859
860 if (gtk_progress_tracker_get_state (&priv->tracker) != GTK_PROGRESS_STATE_AFTER)
861 {
862 if (is_left_transition (priv->active_transition_type))
863 x = allocation->width * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
864 if (is_right_transition (priv->active_transition_type))
865 x = -allocation->width * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
866 }
867
868 return x;
869 }
870
871 static gint
get_bin_window_y(GtkStack * stack,const GtkAllocation * allocation)872 get_bin_window_y (GtkStack *stack,
873 const GtkAllocation *allocation)
874 {
875 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
876 int y = 0;
877
878 if (gtk_progress_tracker_get_state (&priv->tracker) != GTK_PROGRESS_STATE_AFTER)
879 {
880 if (is_up_transition (priv->active_transition_type))
881 y = allocation->height * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
882 if (is_down_transition(priv->active_transition_type))
883 y = -allocation->height * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
884 }
885
886 return y;
887 }
888
889 static void
gtk_stack_progress_updated(GtkStack * stack)890 gtk_stack_progress_updated (GtkStack *stack)
891 {
892 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
893
894 gtk_widget_queue_draw (GTK_WIDGET (stack));
895
896 if (!priv->vhomogeneous || !priv->hhomogeneous)
897 gtk_widget_queue_resize (GTK_WIDGET (stack));
898
899 if (priv->bin_window != NULL &&
900 is_window_moving_transition (priv->active_transition_type))
901 {
902 GtkAllocation allocation;
903 gtk_widget_get_allocation (GTK_WIDGET (stack), &allocation);
904 gdk_window_move (priv->bin_window,
905 get_bin_window_x (stack, &allocation), get_bin_window_y (stack, &allocation));
906 }
907
908 if (gtk_progress_tracker_get_state (&priv->tracker) == GTK_PROGRESS_STATE_AFTER)
909 {
910 if (priv->last_visible_surface != NULL)
911 {
912 cairo_surface_destroy (priv->last_visible_surface);
913 priv->last_visible_surface = NULL;
914 }
915
916 if (priv->last_visible_child != NULL)
917 {
918 gtk_widget_set_child_visible (priv->last_visible_child->widget, FALSE);
919 priv->last_visible_child = NULL;
920 }
921 }
922 }
923
924 static gboolean
gtk_stack_transition_cb(GtkWidget * widget,GdkFrameClock * frame_clock,gpointer user_data)925 gtk_stack_transition_cb (GtkWidget *widget,
926 GdkFrameClock *frame_clock,
927 gpointer user_data)
928 {
929 GtkStack *stack = GTK_STACK (widget);
930 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
931
932 if (priv->first_frame_skipped)
933 gtk_progress_tracker_advance_frame (&priv->tracker,
934 gdk_frame_clock_get_frame_time (frame_clock));
935 else
936 priv->first_frame_skipped = TRUE;
937
938 /* Finish animation early if not mapped anymore */
939 if (!gtk_widget_get_mapped (widget))
940 gtk_progress_tracker_finish (&priv->tracker);
941
942 gtk_stack_progress_updated (GTK_STACK (widget));
943
944 if (gtk_progress_tracker_get_state (&priv->tracker) == GTK_PROGRESS_STATE_AFTER)
945 {
946 priv->tick_id = 0;
947 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_TRANSITION_RUNNING]);
948
949 return FALSE;
950 }
951
952 return TRUE;
953 }
954
955 static void
gtk_stack_schedule_ticks(GtkStack * stack)956 gtk_stack_schedule_ticks (GtkStack *stack)
957 {
958 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
959
960 if (priv->tick_id == 0)
961 {
962 priv->tick_id =
963 gtk_widget_add_tick_callback (GTK_WIDGET (stack), gtk_stack_transition_cb, stack, NULL);
964 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_TRANSITION_RUNNING]);
965 }
966 }
967
968 static void
gtk_stack_unschedule_ticks(GtkStack * stack)969 gtk_stack_unschedule_ticks (GtkStack *stack)
970 {
971 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
972
973 if (priv->tick_id != 0)
974 {
975 gtk_widget_remove_tick_callback (GTK_WIDGET (stack), priv->tick_id);
976 priv->tick_id = 0;
977 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_TRANSITION_RUNNING]);
978 }
979 }
980
981 static GtkStackTransitionType
effective_transition_type(GtkStack * stack,GtkStackTransitionType transition_type)982 effective_transition_type (GtkStack *stack,
983 GtkStackTransitionType transition_type)
984 {
985 if (gtk_widget_get_direction (GTK_WIDGET (stack)) == GTK_TEXT_DIR_RTL)
986 {
987 switch (transition_type)
988 {
989 case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT:
990 return GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT;
991 case GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT:
992 return GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT;
993 case GTK_STACK_TRANSITION_TYPE_OVER_LEFT:
994 return GTK_STACK_TRANSITION_TYPE_OVER_RIGHT;
995 case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT:
996 return GTK_STACK_TRANSITION_TYPE_OVER_LEFT;
997 case GTK_STACK_TRANSITION_TYPE_UNDER_LEFT:
998 return GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT;
999 case GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT:
1000 return GTK_STACK_TRANSITION_TYPE_UNDER_LEFT;
1001 default: ;
1002 }
1003 }
1004
1005 return transition_type;
1006 }
1007
1008 static void
gtk_stack_start_transition(GtkStack * stack,GtkStackTransitionType transition_type,guint transition_duration)1009 gtk_stack_start_transition (GtkStack *stack,
1010 GtkStackTransitionType transition_type,
1011 guint transition_duration)
1012 {
1013 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1014 GtkWidget *widget = GTK_WIDGET (stack);
1015
1016 if (gtk_widget_get_mapped (widget) &&
1017 gtk_settings_get_enable_animations (gtk_widget_get_settings (widget)) &&
1018 transition_type != GTK_STACK_TRANSITION_TYPE_NONE &&
1019 transition_duration != 0 &&
1020 priv->last_visible_child != NULL)
1021 {
1022 priv->active_transition_type = effective_transition_type (stack, transition_type);
1023 priv->first_frame_skipped = FALSE;
1024 gtk_stack_schedule_ticks (stack);
1025 gtk_progress_tracker_start (&priv->tracker,
1026 priv->transition_duration * 1000,
1027 0,
1028 1.0);
1029 }
1030 else
1031 {
1032 gtk_stack_unschedule_ticks (stack);
1033 priv->active_transition_type = GTK_STACK_TRANSITION_TYPE_NONE;
1034 gtk_progress_tracker_finish (&priv->tracker);
1035 }
1036
1037 gtk_stack_progress_updated (GTK_STACK (widget));
1038 }
1039
1040 static void
set_visible_child(GtkStack * stack,GtkStackChildInfo * child_info,GtkStackTransitionType transition_type,guint transition_duration)1041 set_visible_child (GtkStack *stack,
1042 GtkStackChildInfo *child_info,
1043 GtkStackTransitionType transition_type,
1044 guint transition_duration)
1045 {
1046 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1047 GtkStackChildInfo *info;
1048 GtkWidget *widget = GTK_WIDGET (stack);
1049 GList *l;
1050 GtkWidget *toplevel;
1051 GtkWidget *focus;
1052 gboolean contains_focus = FALSE;
1053
1054 /* if we are being destroyed, do not bother with transitions
1055 * and notifications
1056 */
1057 if (gtk_widget_in_destruction (widget))
1058 return;
1059
1060 /* If none, pick first visible */
1061 if (child_info == NULL)
1062 {
1063 for (l = priv->children; l != NULL; l = l->next)
1064 {
1065 info = l->data;
1066 if (gtk_widget_get_visible (info->widget))
1067 {
1068 child_info = info;
1069 break;
1070 }
1071 }
1072 }
1073
1074 if (child_info == priv->visible_child)
1075 return;
1076
1077 toplevel = gtk_widget_get_toplevel (widget);
1078 if (GTK_IS_WINDOW (toplevel))
1079 {
1080 focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
1081 if (focus &&
1082 priv->visible_child &&
1083 priv->visible_child->widget &&
1084 gtk_widget_is_ancestor (focus, priv->visible_child->widget))
1085 {
1086 contains_focus = TRUE;
1087
1088 if (priv->visible_child->last_focus)
1089 g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus),
1090 (gpointer *)&priv->visible_child->last_focus);
1091 priv->visible_child->last_focus = focus;
1092 g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus),
1093 (gpointer *)&priv->visible_child->last_focus);
1094 }
1095 }
1096
1097 if (priv->last_visible_child)
1098 gtk_widget_set_child_visible (priv->last_visible_child->widget, FALSE);
1099 priv->last_visible_child = NULL;
1100
1101 if (priv->last_visible_surface != NULL)
1102 cairo_surface_destroy (priv->last_visible_surface);
1103 priv->last_visible_surface = NULL;
1104
1105 if (priv->visible_child && priv->visible_child->widget)
1106 {
1107 if (gtk_widget_is_visible (widget))
1108 {
1109 GtkAllocation allocation;
1110
1111 priv->last_visible_child = priv->visible_child;
1112 gtk_widget_get_allocated_size (priv->last_visible_child->widget, &allocation, NULL);
1113 priv->last_visible_widget_width = allocation.width;
1114 priv->last_visible_widget_height = allocation.height;
1115 }
1116 else
1117 {
1118 gtk_widget_set_child_visible (priv->visible_child->widget, FALSE);
1119 }
1120 }
1121
1122 gtk_stack_accessible_update_visible_child (stack,
1123 priv->visible_child ? priv->visible_child->widget : NULL,
1124 child_info ? child_info->widget : NULL);
1125
1126 priv->visible_child = child_info;
1127
1128 if (child_info)
1129 {
1130 gtk_widget_set_child_visible (child_info->widget, TRUE);
1131
1132 if (contains_focus)
1133 {
1134 if (child_info->last_focus)
1135 gtk_widget_grab_focus (child_info->last_focus);
1136 else
1137 gtk_widget_child_focus (child_info->widget, GTK_DIR_TAB_FORWARD);
1138 }
1139 }
1140
1141 if ((child_info == NULL || priv->last_visible_child == NULL) &&
1142 is_direction_dependent_transition (transition_type))
1143 {
1144 transition_type = GTK_STACK_TRANSITION_TYPE_NONE;
1145 }
1146 else if (is_direction_dependent_transition (transition_type))
1147 {
1148 gboolean i_first = FALSE;
1149 for (l = priv->children; l != NULL; l = l->next)
1150 {
1151 if (child_info == l->data)
1152 {
1153 i_first = TRUE;
1154 break;
1155 }
1156 if (priv->last_visible_child == l->data)
1157 break;
1158 }
1159
1160 transition_type = get_simple_transition_type (i_first, transition_type);
1161 }
1162
1163 if (priv->hhomogeneous && priv->vhomogeneous)
1164 gtk_widget_queue_allocate (widget);
1165 else
1166 gtk_widget_queue_resize (widget);
1167
1168 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_VISIBLE_CHILD]);
1169 g_object_notify_by_pspec (G_OBJECT (stack),
1170 stack_props[PROP_VISIBLE_CHILD_NAME]);
1171
1172 gtk_stack_start_transition (stack, transition_type, transition_duration);
1173 }
1174
1175 static void
stack_child_visibility_notify_cb(GObject * obj,GParamSpec * pspec,gpointer user_data)1176 stack_child_visibility_notify_cb (GObject *obj,
1177 GParamSpec *pspec,
1178 gpointer user_data)
1179 {
1180 GtkStack *stack = GTK_STACK (user_data);
1181 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1182 GtkWidget *child = GTK_WIDGET (obj);
1183 GtkStackChildInfo *child_info;
1184
1185 child_info = find_child_info_for_widget (stack, child);
1186
1187 if (priv->visible_child == NULL &&
1188 gtk_widget_get_visible (child))
1189 set_visible_child (stack, child_info, priv->transition_type, priv->transition_duration);
1190 else if (priv->visible_child == child_info &&
1191 !gtk_widget_get_visible (child))
1192 set_visible_child (stack, NULL, priv->transition_type, priv->transition_duration);
1193
1194 if (child_info == priv->last_visible_child)
1195 {
1196 gtk_widget_set_child_visible (priv->last_visible_child->widget, FALSE);
1197 priv->last_visible_child = NULL;
1198 }
1199 }
1200
1201 /**
1202 * gtk_stack_add_titled:
1203 * @stack: a #GtkStack
1204 * @child: the widget to add
1205 * @name: the name for @child
1206 * @title: a human-readable title for @child
1207 *
1208 * Adds a child to @stack.
1209 * The child is identified by the @name. The @title
1210 * will be used by #GtkStackSwitcher to represent
1211 * @child in a tab bar, so it should be short.
1212 *
1213 * Since: 3.10
1214 */
1215 void
gtk_stack_add_titled(GtkStack * stack,GtkWidget * child,const gchar * name,const gchar * title)1216 gtk_stack_add_titled (GtkStack *stack,
1217 GtkWidget *child,
1218 const gchar *name,
1219 const gchar *title)
1220 {
1221 g_return_if_fail (GTK_IS_STACK (stack));
1222 g_return_if_fail (GTK_IS_WIDGET (child));
1223
1224 gtk_container_add_with_properties (GTK_CONTAINER (stack),
1225 child,
1226 "name", name,
1227 "title", title,
1228 NULL);
1229 }
1230
1231 /**
1232 * gtk_stack_add_named:
1233 * @stack: a #GtkStack
1234 * @child: the widget to add
1235 * @name: the name for @child
1236 *
1237 * Adds a child to @stack.
1238 * The child is identified by the @name.
1239 *
1240 * Since: 3.10
1241 */
1242 void
gtk_stack_add_named(GtkStack * stack,GtkWidget * child,const gchar * name)1243 gtk_stack_add_named (GtkStack *stack,
1244 GtkWidget *child,
1245 const gchar *name)
1246 {
1247 g_return_if_fail (GTK_IS_STACK (stack));
1248 g_return_if_fail (GTK_IS_WIDGET (child));
1249
1250 gtk_container_add_with_properties (GTK_CONTAINER (stack),
1251 child,
1252 "name", name,
1253 NULL);
1254 }
1255
1256 static void
gtk_stack_add(GtkContainer * container,GtkWidget * child)1257 gtk_stack_add (GtkContainer *container,
1258 GtkWidget *child)
1259 {
1260 GtkStack *stack = GTK_STACK (container);
1261 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1262 GtkStackChildInfo *child_info;
1263
1264 g_return_if_fail (child != NULL);
1265
1266 child_info = g_slice_new (GtkStackChildInfo);
1267 child_info->widget = child;
1268 child_info->name = NULL;
1269 child_info->title = NULL;
1270 child_info->icon_name = NULL;
1271 child_info->needs_attention = FALSE;
1272 child_info->last_focus = NULL;
1273
1274 priv->children = g_list_append (priv->children, child_info);
1275
1276 gtk_widget_set_child_visible (child, FALSE);
1277 gtk_widget_set_parent_window (child, priv->bin_window);
1278 gtk_widget_set_parent (child, GTK_WIDGET (stack));
1279
1280 if (priv->bin_window)
1281 gdk_window_set_events (priv->bin_window,
1282 gdk_window_get_events (priv->bin_window) |
1283 gtk_widget_get_events (child));
1284
1285 g_signal_connect (child, "notify::visible",
1286 G_CALLBACK (stack_child_visibility_notify_cb), stack);
1287
1288 gtk_container_child_notify_by_pspec (container, child, stack_child_props[CHILD_PROP_POSITION]);
1289
1290 if (priv->visible_child == NULL &&
1291 gtk_widget_get_visible (child))
1292 set_visible_child (stack, child_info, priv->transition_type, priv->transition_duration);
1293
1294 if (priv->hhomogeneous || priv->vhomogeneous || priv->visible_child == child_info)
1295 gtk_widget_queue_resize (GTK_WIDGET (stack));
1296 }
1297
1298 static void
gtk_stack_remove(GtkContainer * container,GtkWidget * child)1299 gtk_stack_remove (GtkContainer *container,
1300 GtkWidget *child)
1301 {
1302 GtkStack *stack = GTK_STACK (container);
1303 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1304 GtkStackChildInfo *child_info;
1305 gboolean was_visible;
1306
1307 child_info = find_child_info_for_widget (stack, child);
1308 if (child_info == NULL)
1309 return;
1310
1311 priv->children = g_list_remove (priv->children, child_info);
1312
1313 g_signal_handlers_disconnect_by_func (child,
1314 stack_child_visibility_notify_cb,
1315 stack);
1316
1317 was_visible = gtk_widget_get_visible (child);
1318
1319 child_info->widget = NULL;
1320
1321 if (priv->visible_child == child_info)
1322 set_visible_child (stack, NULL, priv->transition_type, priv->transition_duration);
1323
1324 if (priv->last_visible_child == child_info)
1325 priv->last_visible_child = NULL;
1326
1327 gtk_widget_unparent (child);
1328
1329 g_free (child_info->name);
1330 g_free (child_info->title);
1331 g_free (child_info->icon_name);
1332
1333 if (child_info->last_focus)
1334 g_object_remove_weak_pointer (G_OBJECT (child_info->last_focus),
1335 (gpointer *)&child_info->last_focus);
1336
1337 g_slice_free (GtkStackChildInfo, child_info);
1338
1339 if ((priv->hhomogeneous || priv->vhomogeneous) && was_visible)
1340 gtk_widget_queue_resize (GTK_WIDGET (stack));
1341 }
1342
1343 /**
1344 * gtk_stack_get_child_by_name:
1345 * @stack: a #GtkStack
1346 * @name: the name of the child to find
1347 *
1348 * Finds the child of the #GtkStack with the name given as
1349 * the argument. Returns %NULL if there is no child with this
1350 * name.
1351 *
1352 * Returns: (transfer none) (nullable): the requested child of the #GtkStack
1353 *
1354 * Since: 3.12
1355 */
1356 GtkWidget *
gtk_stack_get_child_by_name(GtkStack * stack,const gchar * name)1357 gtk_stack_get_child_by_name (GtkStack *stack,
1358 const gchar *name)
1359 {
1360 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1361 GtkStackChildInfo *info;
1362 GList *l;
1363
1364 g_return_val_if_fail (GTK_IS_STACK (stack), NULL);
1365 g_return_val_if_fail (name != NULL, NULL);
1366
1367 for (l = priv->children; l != NULL; l = l->next)
1368 {
1369 info = l->data;
1370 if (info->name && strcmp (info->name, name) == 0)
1371 return info->widget;
1372 }
1373
1374 return NULL;
1375 }
1376
1377 /**
1378 * gtk_stack_set_homogeneous:
1379 * @stack: a #GtkStack
1380 * @homogeneous: %TRUE to make @stack homogeneous
1381 *
1382 * Sets the #GtkStack to be homogeneous or not. If it
1383 * is homogeneous, the #GtkStack will request the same
1384 * size for all its children. If it isn't, the stack
1385 * may change size when a different child becomes visible.
1386 *
1387 * Since 3.16, homogeneity can be controlled separately
1388 * for horizontal and vertical size, with the
1389 * #GtkStack:hhomogeneous and #GtkStack:vhomogeneous.
1390 *
1391 * Since: 3.10
1392 */
1393 void
gtk_stack_set_homogeneous(GtkStack * stack,gboolean homogeneous)1394 gtk_stack_set_homogeneous (GtkStack *stack,
1395 gboolean homogeneous)
1396 {
1397 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1398
1399 g_return_if_fail (GTK_IS_STACK (stack));
1400
1401 homogeneous = !!homogeneous;
1402
1403 if ((priv->hhomogeneous && priv->vhomogeneous) == homogeneous)
1404 return;
1405
1406 g_object_freeze_notify (G_OBJECT (stack));
1407
1408 if (priv->hhomogeneous != homogeneous)
1409 {
1410 priv->hhomogeneous = homogeneous;
1411 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_HHOMOGENEOUS]);
1412 }
1413
1414 if (priv->vhomogeneous != homogeneous)
1415 {
1416 priv->vhomogeneous = homogeneous;
1417 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_VHOMOGENEOUS]);
1418 }
1419
1420 if (gtk_widget_get_visible (GTK_WIDGET(stack)))
1421 gtk_widget_queue_resize (GTK_WIDGET (stack));
1422
1423 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_HOMOGENEOUS]);
1424 g_object_thaw_notify (G_OBJECT (stack));
1425 }
1426
1427 /**
1428 * gtk_stack_get_homogeneous:
1429 * @stack: a #GtkStack
1430 *
1431 * Gets whether @stack is homogeneous.
1432 * See gtk_stack_set_homogeneous().
1433 *
1434 * Returns: whether @stack is homogeneous.
1435 *
1436 * Since: 3.10
1437 */
1438 gboolean
gtk_stack_get_homogeneous(GtkStack * stack)1439 gtk_stack_get_homogeneous (GtkStack *stack)
1440 {
1441 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1442
1443 g_return_val_if_fail (GTK_IS_STACK (stack), FALSE);
1444
1445 return priv->hhomogeneous && priv->vhomogeneous;
1446 }
1447
1448 /**
1449 * gtk_stack_set_hhomogeneous:
1450 * @stack: a #GtkStack
1451 * @hhomogeneous: %TRUE to make @stack horizontally homogeneous
1452 *
1453 * Sets the #GtkStack to be horizontally homogeneous or not.
1454 * If it is homogeneous, the #GtkStack will request the same
1455 * width for all its children. If it isn't, the stack
1456 * may change width when a different child becomes visible.
1457 *
1458 * Since: 3.16
1459 */
1460 void
gtk_stack_set_hhomogeneous(GtkStack * stack,gboolean hhomogeneous)1461 gtk_stack_set_hhomogeneous (GtkStack *stack,
1462 gboolean hhomogeneous)
1463 {
1464 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1465
1466 g_return_if_fail (GTK_IS_STACK (stack));
1467
1468 hhomogeneous = !!hhomogeneous;
1469
1470 if (priv->hhomogeneous == hhomogeneous)
1471 return;
1472
1473 priv->hhomogeneous = hhomogeneous;
1474
1475 if (gtk_widget_get_visible (GTK_WIDGET(stack)))
1476 gtk_widget_queue_resize (GTK_WIDGET (stack));
1477
1478 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_HHOMOGENEOUS]);
1479 }
1480
1481 /**
1482 * gtk_stack_get_hhomogeneous:
1483 * @stack: a #GtkStack
1484 *
1485 * Gets whether @stack is horizontally homogeneous.
1486 * See gtk_stack_set_hhomogeneous().
1487 *
1488 * Returns: whether @stack is horizontally homogeneous.
1489 *
1490 * Since: 3.16
1491 */
1492 gboolean
gtk_stack_get_hhomogeneous(GtkStack * stack)1493 gtk_stack_get_hhomogeneous (GtkStack *stack)
1494 {
1495 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1496
1497 g_return_val_if_fail (GTK_IS_STACK (stack), FALSE);
1498
1499 return priv->hhomogeneous;
1500 }
1501
1502 /**
1503 * gtk_stack_set_vhomogeneous:
1504 * @stack: a #GtkStack
1505 * @vhomogeneous: %TRUE to make @stack vertically homogeneous
1506 *
1507 * Sets the #GtkStack to be vertically homogeneous or not.
1508 * If it is homogeneous, the #GtkStack will request the same
1509 * height for all its children. If it isn't, the stack
1510 * may change height when a different child becomes visible.
1511 *
1512 * Since: 3.16
1513 */
1514 void
gtk_stack_set_vhomogeneous(GtkStack * stack,gboolean vhomogeneous)1515 gtk_stack_set_vhomogeneous (GtkStack *stack,
1516 gboolean vhomogeneous)
1517 {
1518 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1519
1520 g_return_if_fail (GTK_IS_STACK (stack));
1521
1522 vhomogeneous = !!vhomogeneous;
1523
1524 if (priv->vhomogeneous == vhomogeneous)
1525 return;
1526
1527 priv->vhomogeneous = vhomogeneous;
1528
1529 if (gtk_widget_get_visible (GTK_WIDGET(stack)))
1530 gtk_widget_queue_resize (GTK_WIDGET (stack));
1531
1532 g_object_notify_by_pspec (G_OBJECT (stack), stack_props[PROP_VHOMOGENEOUS]);
1533 }
1534
1535 /**
1536 * gtk_stack_get_vhomogeneous:
1537 * @stack: a #GtkStack
1538 *
1539 * Gets whether @stack is vertically homogeneous.
1540 * See gtk_stack_set_vhomogeneous().
1541 *
1542 * Returns: whether @stack is vertically homogeneous.
1543 *
1544 * Since: 3.16
1545 */
1546 gboolean
gtk_stack_get_vhomogeneous(GtkStack * stack)1547 gtk_stack_get_vhomogeneous (GtkStack *stack)
1548 {
1549 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1550
1551 g_return_val_if_fail (GTK_IS_STACK (stack), FALSE);
1552
1553 return priv->vhomogeneous;
1554 }
1555
1556 /**
1557 * gtk_stack_get_transition_duration:
1558 * @stack: a #GtkStack
1559 *
1560 * Returns the amount of time (in milliseconds) that
1561 * transitions between pages in @stack will take.
1562 *
1563 * Returns: the transition duration
1564 *
1565 * Since: 3.10
1566 */
1567 guint
gtk_stack_get_transition_duration(GtkStack * stack)1568 gtk_stack_get_transition_duration (GtkStack *stack)
1569 {
1570 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1571
1572 g_return_val_if_fail (GTK_IS_STACK (stack), 0);
1573
1574 return priv->transition_duration;
1575 }
1576
1577 /**
1578 * gtk_stack_set_transition_duration:
1579 * @stack: a #GtkStack
1580 * @duration: the new duration, in milliseconds
1581 *
1582 * Sets the duration that transitions between pages in @stack
1583 * will take.
1584 *
1585 * Since: 3.10
1586 */
1587 void
gtk_stack_set_transition_duration(GtkStack * stack,guint duration)1588 gtk_stack_set_transition_duration (GtkStack *stack,
1589 guint duration)
1590 {
1591 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1592
1593 g_return_if_fail (GTK_IS_STACK (stack));
1594
1595 if (priv->transition_duration == duration)
1596 return;
1597
1598 priv->transition_duration = duration;
1599 g_object_notify_by_pspec (G_OBJECT (stack),
1600 stack_props[PROP_TRANSITION_DURATION]);
1601 }
1602
1603 /**
1604 * gtk_stack_get_transition_type:
1605 * @stack: a #GtkStack
1606 *
1607 * Gets the type of animation that will be used
1608 * for transitions between pages in @stack.
1609 *
1610 * Returns: the current transition type of @stack
1611 *
1612 * Since: 3.10
1613 */
1614 GtkStackTransitionType
gtk_stack_get_transition_type(GtkStack * stack)1615 gtk_stack_get_transition_type (GtkStack *stack)
1616 {
1617 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1618
1619 g_return_val_if_fail (GTK_IS_STACK (stack), GTK_STACK_TRANSITION_TYPE_NONE);
1620
1621 return priv->transition_type;
1622 }
1623
1624 /**
1625 * gtk_stack_set_transition_type:
1626 * @stack: a #GtkStack
1627 * @transition: the new transition type
1628 *
1629 * Sets the type of animation that will be used for
1630 * transitions between pages in @stack. Available
1631 * types include various kinds of fades and slides.
1632 *
1633 * The transition type can be changed without problems
1634 * at runtime, so it is possible to change the animation
1635 * based on the page that is about to become current.
1636 *
1637 * Since: 3.10
1638 */
1639 void
gtk_stack_set_transition_type(GtkStack * stack,GtkStackTransitionType transition)1640 gtk_stack_set_transition_type (GtkStack *stack,
1641 GtkStackTransitionType transition)
1642 {
1643 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1644
1645 g_return_if_fail (GTK_IS_STACK (stack));
1646
1647 if (priv->transition_type == transition)
1648 return;
1649
1650 priv->transition_type = transition;
1651 g_object_notify_by_pspec (G_OBJECT (stack),
1652 stack_props[PROP_TRANSITION_TYPE]);
1653 }
1654
1655 /**
1656 * gtk_stack_get_transition_running:
1657 * @stack: a #GtkStack
1658 *
1659 * Returns whether the @stack is currently in a transition from one page to
1660 * another.
1661 *
1662 * Returns: %TRUE if the transition is currently running, %FALSE otherwise.
1663 *
1664 * Since: 3.12
1665 */
1666 gboolean
gtk_stack_get_transition_running(GtkStack * stack)1667 gtk_stack_get_transition_running (GtkStack *stack)
1668 {
1669 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1670
1671 g_return_val_if_fail (GTK_IS_STACK (stack), FALSE);
1672
1673 return (priv->tick_id != 0);
1674 }
1675
1676 /**
1677 * gtk_stack_set_interpolate_size:
1678 * @stack: A #GtkStack
1679 * @interpolate_size: the new value
1680 *
1681 * Sets whether or not @stack will interpolate its size when
1682 * changing the visible child. If the #GtkStack:interpolate-size
1683 * property is set to %TRUE, @stack will interpolate its size between
1684 * the current one and the one it'll take after changing the
1685 * visible child, according to the set transition duration.
1686 *
1687 * Since: 3.18
1688 */
1689 void
gtk_stack_set_interpolate_size(GtkStack * stack,gboolean interpolate_size)1690 gtk_stack_set_interpolate_size (GtkStack *stack,
1691 gboolean interpolate_size)
1692 {
1693 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1694 g_return_if_fail (GTK_IS_STACK (stack));
1695
1696 interpolate_size = !!interpolate_size;
1697
1698 if (priv->interpolate_size == interpolate_size)
1699 return;
1700
1701 priv->interpolate_size = interpolate_size;
1702 g_object_notify_by_pspec (G_OBJECT (stack),
1703 stack_props[PROP_INTERPOLATE_SIZE]);
1704 }
1705
1706 /**
1707 * gtk_stack_get_interpolate_size:
1708 * @stack: A #GtkStack
1709 *
1710 * Returns wether the #GtkStack is set up to interpolate between
1711 * the sizes of children on page switch.
1712 *
1713 * Returns: %TRUE if child sizes are interpolated
1714 *
1715 * Since: 3.18
1716 */
1717 gboolean
gtk_stack_get_interpolate_size(GtkStack * stack)1718 gtk_stack_get_interpolate_size (GtkStack *stack)
1719 {
1720 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1721 g_return_val_if_fail (GTK_IS_STACK (stack), FALSE);
1722
1723 return priv->interpolate_size;
1724 }
1725
1726
1727
1728 /**
1729 * gtk_stack_get_visible_child:
1730 * @stack: a #GtkStack
1731 *
1732 * Gets the currently visible child of @stack, or %NULL if
1733 * there are no visible children.
1734 *
1735 * Returns: (transfer none) (nullable): the visible child of the #GtkStack
1736 *
1737 * Since: 3.10
1738 */
1739 GtkWidget *
gtk_stack_get_visible_child(GtkStack * stack)1740 gtk_stack_get_visible_child (GtkStack *stack)
1741 {
1742 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1743
1744 g_return_val_if_fail (GTK_IS_STACK (stack), NULL);
1745
1746 return priv->visible_child ? priv->visible_child->widget : NULL;
1747 }
1748
1749 /**
1750 * gtk_stack_get_visible_child_name:
1751 * @stack: a #GtkStack
1752 *
1753 * Returns the name of the currently visible child of @stack, or
1754 * %NULL if there is no visible child.
1755 *
1756 * Returns: (transfer none) (nullable): the name of the visible child of the #GtkStack
1757 *
1758 * Since: 3.10
1759 */
1760 const gchar *
gtk_stack_get_visible_child_name(GtkStack * stack)1761 gtk_stack_get_visible_child_name (GtkStack *stack)
1762 {
1763 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1764
1765 g_return_val_if_fail (GTK_IS_STACK (stack), NULL);
1766
1767 if (priv->visible_child)
1768 return priv->visible_child->name;
1769
1770 return NULL;
1771 }
1772
1773 /**
1774 * gtk_stack_set_visible_child:
1775 * @stack: a #GtkStack
1776 * @child: a child of @stack
1777 *
1778 * Makes @child the visible child of @stack.
1779 *
1780 * If @child is different from the currently
1781 * visible child, the transition between the
1782 * two will be animated with the current
1783 * transition type of @stack.
1784 *
1785 * Note that the @child widget has to be visible itself
1786 * (see gtk_widget_show()) in order to become the visible
1787 * child of @stack.
1788 *
1789 * Since: 3.10
1790 */
1791 void
gtk_stack_set_visible_child(GtkStack * stack,GtkWidget * child)1792 gtk_stack_set_visible_child (GtkStack *stack,
1793 GtkWidget *child)
1794 {
1795 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1796 GtkStackChildInfo *child_info;
1797
1798 g_return_if_fail (GTK_IS_STACK (stack));
1799 g_return_if_fail (GTK_IS_WIDGET (child));
1800
1801 child_info = find_child_info_for_widget (stack, child);
1802 if (child_info == NULL)
1803 {
1804 g_warning ("Given child of type '%s' not found in GtkStack",
1805 G_OBJECT_TYPE_NAME (child));
1806 return;
1807 }
1808
1809 if (gtk_widget_get_visible (child_info->widget))
1810 set_visible_child (stack, child_info,
1811 priv->transition_type,
1812 priv->transition_duration);
1813 }
1814
1815 /**
1816 * gtk_stack_set_visible_child_name:
1817 * @stack: a #GtkStack
1818 * @name: the name of the child to make visible
1819 *
1820 * Makes the child with the given name visible.
1821 *
1822 * If @child is different from the currently
1823 * visible child, the transition between the
1824 * two will be animated with the current
1825 * transition type of @stack.
1826 *
1827 * Note that the child widget has to be visible itself
1828 * (see gtk_widget_show()) in order to become the visible
1829 * child of @stack.
1830 *
1831 * Since: 3.10
1832 */
1833 void
gtk_stack_set_visible_child_name(GtkStack * stack,const gchar * name)1834 gtk_stack_set_visible_child_name (GtkStack *stack,
1835 const gchar *name)
1836 {
1837 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1838
1839 g_return_if_fail (GTK_IS_STACK (stack));
1840
1841 gtk_stack_set_visible_child_full (stack, name, priv->transition_type);
1842 }
1843
1844 /**
1845 * gtk_stack_set_visible_child_full:
1846 * @stack: a #GtkStack
1847 * @name: the name of the child to make visible
1848 * @transition: the transition type to use
1849 *
1850 * Makes the child with the given name visible.
1851 *
1852 * Note that the child widget has to be visible itself
1853 * (see gtk_widget_show()) in order to become the visible
1854 * child of @stack.
1855 *
1856 * Since: 3.10
1857 */
1858 void
gtk_stack_set_visible_child_full(GtkStack * stack,const gchar * name,GtkStackTransitionType transition)1859 gtk_stack_set_visible_child_full (GtkStack *stack,
1860 const gchar *name,
1861 GtkStackTransitionType transition)
1862 {
1863 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1864 GtkStackChildInfo *child_info, *info;
1865 GList *l;
1866
1867 g_return_if_fail (GTK_IS_STACK (stack));
1868
1869 if (name == NULL)
1870 return;
1871
1872 child_info = NULL;
1873 for (l = priv->children; l != NULL; l = l->next)
1874 {
1875 info = l->data;
1876 if (info->name != NULL &&
1877 strcmp (info->name, name) == 0)
1878 {
1879 child_info = info;
1880 break;
1881 }
1882 }
1883
1884 if (child_info == NULL)
1885 {
1886 g_warning ("Child name '%s' not found in GtkStack", name);
1887 return;
1888 }
1889
1890 if (gtk_widget_get_visible (child_info->widget))
1891 set_visible_child (stack, child_info, transition, priv->transition_duration);
1892 }
1893
1894 static void
gtk_stack_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)1895 gtk_stack_forall (GtkContainer *container,
1896 gboolean include_internals,
1897 GtkCallback callback,
1898 gpointer callback_data)
1899 {
1900 GtkStack *stack = GTK_STACK (container);
1901 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1902 GtkStackChildInfo *child_info;
1903 GList *l;
1904
1905 l = priv->children;
1906 while (l)
1907 {
1908 child_info = l->data;
1909 l = l->next;
1910
1911 (* callback) (child_info->widget, callback_data);
1912 }
1913 }
1914
1915 static void
gtk_stack_compute_expand(GtkWidget * widget,gboolean * hexpand_p,gboolean * vexpand_p)1916 gtk_stack_compute_expand (GtkWidget *widget,
1917 gboolean *hexpand_p,
1918 gboolean *vexpand_p)
1919 {
1920 GtkStack *stack = GTK_STACK (widget);
1921 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1922 gboolean hexpand, vexpand;
1923 GtkStackChildInfo *child_info;
1924 GtkWidget *child;
1925 GList *l;
1926
1927 hexpand = FALSE;
1928 vexpand = FALSE;
1929 for (l = priv->children; l != NULL; l = l->next)
1930 {
1931 child_info = l->data;
1932 child = child_info->widget;
1933
1934 if (!hexpand &&
1935 gtk_widget_compute_expand (child, GTK_ORIENTATION_HORIZONTAL))
1936 hexpand = TRUE;
1937
1938 if (!vexpand &&
1939 gtk_widget_compute_expand (child, GTK_ORIENTATION_VERTICAL))
1940 vexpand = TRUE;
1941
1942 if (hexpand && vexpand)
1943 break;
1944 }
1945
1946 *hexpand_p = hexpand;
1947 *vexpand_p = vexpand;
1948 }
1949
1950 static void
gtk_stack_draw_crossfade(GtkWidget * widget,cairo_t * cr)1951 gtk_stack_draw_crossfade (GtkWidget *widget,
1952 cairo_t *cr)
1953 {
1954 GtkStack *stack = GTK_STACK (widget);
1955 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1956 gdouble progress = gtk_progress_tracker_get_progress (&priv->tracker, FALSE);
1957
1958 cairo_push_group (cr);
1959 gtk_container_propagate_draw (GTK_CONTAINER (stack),
1960 priv->visible_child->widget,
1961 cr);
1962 cairo_save (cr);
1963
1964 /* Multiply alpha by progress */
1965 cairo_set_source_rgba (cr, 1, 1, 1, progress);
1966 cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
1967 cairo_paint (cr);
1968
1969 if (priv->last_visible_surface)
1970 {
1971 cairo_set_source_surface (cr, priv->last_visible_surface,
1972 priv->last_visible_surface_allocation.x,
1973 priv->last_visible_surface_allocation.y);
1974 cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
1975 cairo_paint_with_alpha (cr, MAX (1.0 - progress, 0));
1976 }
1977
1978 cairo_restore (cr);
1979
1980 cairo_pop_group_to_source (cr);
1981 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1982 cairo_paint (cr);
1983 }
1984
1985 static void
gtk_stack_draw_under(GtkWidget * widget,cairo_t * cr)1986 gtk_stack_draw_under (GtkWidget *widget,
1987 cairo_t *cr)
1988 {
1989 GtkStack *stack = GTK_STACK (widget);
1990 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
1991 GtkAllocation allocation;
1992 gint x, y, width, height, pos_x, pos_y;
1993
1994 gtk_widget_get_allocation (widget, &allocation);
1995 x = y = 0;
1996 width = allocation.width;
1997 height = allocation.height;
1998 pos_x = pos_y = 0;
1999
2000 switch (priv->active_transition_type)
2001 {
2002 case GTK_STACK_TRANSITION_TYPE_UNDER_DOWN:
2003 y = 0;
2004 height = allocation.height * (gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
2005 pos_y = height;
2006 break;
2007 case GTK_STACK_TRANSITION_TYPE_UNDER_UP:
2008 y = allocation.height * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
2009 height = allocation.height - y;
2010 pos_y = y - allocation.height;
2011 break;
2012 case GTK_STACK_TRANSITION_TYPE_UNDER_LEFT:
2013 x = allocation.width * (1 - gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
2014 width = allocation.width - x;
2015 pos_x = x - allocation.width;
2016 break;
2017 case GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT:
2018 x = 0;
2019 width = allocation.width * (gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE));
2020 pos_x = width;
2021 break;
2022 default:
2023 g_assert_not_reached ();
2024 }
2025
2026 cairo_save (cr);
2027 cairo_rectangle (cr, x, y, width, height);
2028 cairo_clip (cr);
2029
2030 gtk_container_propagate_draw (GTK_CONTAINER (stack),
2031 priv->visible_child->widget,
2032 cr);
2033
2034 cairo_restore (cr);
2035
2036 if (priv->last_visible_surface)
2037 {
2038 cairo_set_source_surface (cr, priv->last_visible_surface, pos_x, pos_y);
2039 cairo_paint (cr);
2040 }
2041 }
2042
2043 static void
gtk_stack_draw_slide(GtkWidget * widget,cairo_t * cr)2044 gtk_stack_draw_slide (GtkWidget *widget,
2045 cairo_t *cr)
2046 {
2047 GtkStack *stack = GTK_STACK (widget);
2048 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2049
2050 if (priv->last_visible_surface &&
2051 gtk_cairo_should_draw_window (cr, priv->view_window))
2052 {
2053 GtkAllocation allocation;
2054 int x, y;
2055
2056 gtk_widget_get_allocation (widget, &allocation);
2057
2058 x = get_bin_window_x (stack, &allocation);
2059 y = get_bin_window_y (stack, &allocation);
2060
2061 switch (priv->active_transition_type)
2062 {
2063 case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT:
2064 x -= allocation.width;
2065 break;
2066 case GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT:
2067 x += allocation.width;
2068 break;
2069 case GTK_STACK_TRANSITION_TYPE_SLIDE_UP:
2070 y -= allocation.height;
2071 break;
2072 case GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN:
2073 y += allocation.height;
2074 break;
2075 case GTK_STACK_TRANSITION_TYPE_OVER_UP:
2076 case GTK_STACK_TRANSITION_TYPE_OVER_DOWN:
2077 y = 0;
2078 break;
2079 case GTK_STACK_TRANSITION_TYPE_OVER_LEFT:
2080 case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT:
2081 x = 0;
2082 break;
2083 default:
2084 g_assert_not_reached ();
2085 break;
2086 }
2087
2088 x += priv->last_visible_surface_allocation.x;
2089 y += priv->last_visible_surface_allocation.y;
2090
2091 if (priv->last_visible_child != NULL)
2092 {
2093 if (gtk_widget_get_valign (priv->last_visible_child->widget) == GTK_ALIGN_END &&
2094 priv->last_visible_widget_height > allocation.height)
2095 y -= priv->last_visible_widget_height - allocation.height;
2096 else if (gtk_widget_get_valign (priv->last_visible_child->widget) == GTK_ALIGN_CENTER)
2097 y -= (priv->last_visible_widget_height - allocation.height) / 2;
2098 }
2099
2100 cairo_save (cr);
2101 cairo_set_source_surface (cr, priv->last_visible_surface, x, y);
2102 cairo_paint (cr);
2103 cairo_restore (cr);
2104 }
2105
2106 if (gtk_cairo_should_draw_window (cr, priv->bin_window))
2107 gtk_container_propagate_draw (GTK_CONTAINER (stack),
2108 priv->visible_child->widget,
2109 cr);
2110 }
2111
2112 static gboolean
gtk_stack_draw(GtkWidget * widget,cairo_t * cr)2113 gtk_stack_draw (GtkWidget *widget,
2114 cairo_t *cr)
2115 {
2116 GtkStack *stack = GTK_STACK (widget);
2117 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2118
2119 gtk_css_gadget_draw (priv->gadget, cr);
2120
2121 return FALSE;
2122 }
2123
2124 static gboolean
gtk_stack_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)2125 gtk_stack_render (GtkCssGadget *gadget,
2126 cairo_t *cr,
2127 int x,
2128 int y,
2129 int width,
2130 int height,
2131 gpointer data)
2132 {
2133 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
2134 GtkStack *stack = GTK_STACK (widget);
2135 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2136 cairo_t *pattern_cr;
2137
2138 if (gtk_cairo_should_draw_window (cr, priv->view_window))
2139 {
2140 GtkStyleContext *context;
2141
2142 context = gtk_widget_get_style_context (widget);
2143 gtk_render_background (context,
2144 cr,
2145 0, 0,
2146 gtk_widget_get_allocated_width (widget),
2147 gtk_widget_get_allocated_height (widget));
2148 }
2149
2150 if (priv->visible_child)
2151 {
2152 if (gtk_progress_tracker_get_state (&priv->tracker) != GTK_PROGRESS_STATE_AFTER)
2153 {
2154 if (priv->last_visible_surface == NULL &&
2155 priv->last_visible_child != NULL)
2156 {
2157 gtk_widget_get_allocation (priv->last_visible_child->widget,
2158 &priv->last_visible_surface_allocation);
2159 priv->last_visible_surface =
2160 gdk_window_create_similar_surface (gtk_widget_get_window (widget),
2161 CAIRO_CONTENT_COLOR_ALPHA,
2162 priv->last_visible_surface_allocation.width,
2163 priv->last_visible_surface_allocation.height);
2164 pattern_cr = cairo_create (priv->last_visible_surface);
2165 /* We don't use propagate_draw here, because we don't want to apply
2166 * the bin_window offset
2167 */
2168 gtk_widget_draw (priv->last_visible_child->widget, pattern_cr);
2169 cairo_destroy (pattern_cr);
2170 }
2171
2172 cairo_rectangle (cr,
2173 0, 0,
2174 gtk_widget_get_allocated_width (widget),
2175 gtk_widget_get_allocated_height (widget));
2176 cairo_clip (cr);
2177
2178 switch (priv->active_transition_type)
2179 {
2180 case GTK_STACK_TRANSITION_TYPE_CROSSFADE:
2181 if (gtk_cairo_should_draw_window (cr, priv->bin_window))
2182 gtk_stack_draw_crossfade (widget, cr);
2183 break;
2184 case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT:
2185 case GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT:
2186 case GTK_STACK_TRANSITION_TYPE_SLIDE_UP:
2187 case GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN:
2188 case GTK_STACK_TRANSITION_TYPE_OVER_UP:
2189 case GTK_STACK_TRANSITION_TYPE_OVER_DOWN:
2190 case GTK_STACK_TRANSITION_TYPE_OVER_LEFT:
2191 case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT:
2192 gtk_stack_draw_slide (widget, cr);
2193 break;
2194 case GTK_STACK_TRANSITION_TYPE_UNDER_UP:
2195 case GTK_STACK_TRANSITION_TYPE_UNDER_DOWN:
2196 case GTK_STACK_TRANSITION_TYPE_UNDER_LEFT:
2197 case GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT:
2198 if (gtk_cairo_should_draw_window (cr, priv->bin_window))
2199 gtk_stack_draw_under (widget, cr);
2200 break;
2201 default:
2202 g_assert_not_reached ();
2203 }
2204
2205 }
2206 else if (gtk_cairo_should_draw_window (cr, priv->bin_window))
2207 gtk_container_propagate_draw (GTK_CONTAINER (stack),
2208 priv->visible_child->widget,
2209 cr);
2210 }
2211
2212 return FALSE;
2213 }
2214
2215 static void
gtk_stack_size_allocate(GtkWidget * widget,GtkAllocation * allocation)2216 gtk_stack_size_allocate (GtkWidget *widget,
2217 GtkAllocation *allocation)
2218 {
2219 GtkStack *stack = GTK_STACK (widget);
2220 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2221 GtkAllocation clip;
2222
2223 gtk_widget_set_allocation (widget, allocation);
2224
2225 gtk_css_gadget_allocate (priv->gadget,
2226 allocation,
2227 gtk_widget_get_allocated_baseline (widget),
2228 &clip);
2229
2230 gtk_widget_set_clip (widget, &clip);
2231 }
2232
2233 static void
gtk_stack_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)2234 gtk_stack_allocate (GtkCssGadget *gadget,
2235 const GtkAllocation *allocation,
2236 int baseline,
2237 GtkAllocation *out_clip,
2238 gpointer data)
2239 {
2240 GtkWidget *widget;
2241 GtkStack *stack;
2242 GtkStackPrivate *priv;
2243 GtkAllocation child_allocation;
2244
2245 widget = gtk_css_gadget_get_owner (gadget);
2246 stack = GTK_STACK (widget);
2247 priv = gtk_stack_get_instance_private (stack);
2248
2249 child_allocation.x = 0;
2250 child_allocation.y = 0;
2251
2252 if (gtk_widget_get_realized (widget))
2253 {
2254 gdk_window_move_resize (priv->view_window,
2255 allocation->x, allocation->y,
2256 allocation->width, allocation->height);
2257 gdk_window_move_resize (priv->bin_window,
2258 get_bin_window_x (stack, allocation), get_bin_window_y (stack, allocation),
2259 allocation->width, allocation->height);
2260 }
2261
2262 if (priv->last_visible_child)
2263 {
2264 int min, nat;
2265 gtk_widget_get_preferred_width (priv->last_visible_child->widget, &min, &nat);
2266 child_allocation.width = MAX (min, allocation->width);
2267 gtk_widget_get_preferred_height_for_width (priv->last_visible_child->widget,
2268 child_allocation.width,
2269 &min, &nat);
2270 child_allocation.height = MAX (min, allocation->height);
2271
2272 gtk_widget_size_allocate (priv->last_visible_child->widget, &child_allocation);
2273 }
2274
2275 child_allocation.width = allocation->width;
2276 child_allocation.height = allocation->height;
2277
2278 if (priv->visible_child)
2279 {
2280 int min, nat;
2281 GtkAlign valign;
2282
2283 gtk_widget_get_preferred_height_for_width (priv->visible_child->widget,
2284 allocation->width,
2285 &min, &nat);
2286 if (priv->interpolate_size)
2287 {
2288 valign = gtk_widget_get_valign (priv->visible_child->widget);
2289 child_allocation.height = MAX (nat, allocation->height);
2290 if (valign == GTK_ALIGN_END &&
2291 child_allocation.height > allocation->height)
2292 child_allocation.y -= nat - allocation->height;
2293 else if (valign == GTK_ALIGN_CENTER &&
2294 child_allocation.height > allocation->height)
2295 child_allocation.y -= (nat - allocation->height) / 2;
2296 }
2297
2298 gtk_widget_size_allocate (priv->visible_child->widget, &child_allocation);
2299 }
2300 gtk_container_get_children_clip (GTK_CONTAINER (widget), out_clip);
2301 }
2302
2303 static void
gtk_stack_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)2304 gtk_stack_get_preferred_width (GtkWidget *widget,
2305 gint *minimum,
2306 gint *natural)
2307 {
2308 GtkStack *stack = GTK_STACK (widget);
2309 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2310
2311 gtk_css_gadget_get_preferred_size (priv->gadget,
2312 GTK_ORIENTATION_HORIZONTAL,
2313 -1,
2314 minimum, natural,
2315 NULL, NULL);
2316 }
2317
2318 static void
gtk_stack_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum,gint * natural)2319 gtk_stack_get_preferred_width_for_height (GtkWidget *widget,
2320 gint height,
2321 gint *minimum,
2322 gint *natural)
2323 {
2324 GtkStack *stack = GTK_STACK (widget);
2325 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2326
2327 gtk_css_gadget_get_preferred_size (priv->gadget,
2328 GTK_ORIENTATION_HORIZONTAL,
2329 height,
2330 minimum, natural,
2331 NULL, NULL);
2332 }
2333
2334 static void
gtk_stack_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)2335 gtk_stack_get_preferred_height (GtkWidget *widget,
2336 gint *minimum,
2337 gint *natural)
2338 {
2339 GtkStack *stack = GTK_STACK (widget);
2340 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2341
2342 gtk_css_gadget_get_preferred_size (priv->gadget,
2343 GTK_ORIENTATION_VERTICAL,
2344 -1,
2345 minimum, natural,
2346 NULL, NULL);
2347 }
2348
2349 static void
gtk_stack_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural)2350 gtk_stack_get_preferred_height_for_width (GtkWidget *widget,
2351 gint width,
2352 gint *minimum,
2353 gint *natural)
2354 {
2355 GtkStack *stack = GTK_STACK (widget);
2356 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2357
2358 gtk_css_gadget_get_preferred_size (priv->gadget,
2359 GTK_ORIENTATION_VERTICAL,
2360 width,
2361 minimum, natural,
2362 NULL, NULL);
2363 }
2364
2365 #define LERP(a, b, t) ((a) + (((b) - (a)) * (1.0 - (t))))
2366
2367 static void
gtk_stack_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer data)2368 gtk_stack_measure (GtkCssGadget *gadget,
2369 GtkOrientation orientation,
2370 int for_size,
2371 int *minimum,
2372 int *natural,
2373 int *minimum_baseline,
2374 int *natural_baseline,
2375 gpointer data)
2376 {
2377 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
2378 GtkStack *stack = GTK_STACK (widget);
2379 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2380 GtkStackChildInfo *child_info;
2381 GtkWidget *child;
2382 gint child_min, child_nat;
2383 GList *l;
2384
2385 *minimum = 0;
2386 *natural = 0;
2387
2388 for (l = priv->children; l != NULL; l = l->next)
2389 {
2390 child_info = l->data;
2391 child = child_info->widget;
2392
2393 if (((orientation == GTK_ORIENTATION_VERTICAL && !priv->vhomogeneous) ||
2394 (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->hhomogeneous)) &&
2395 priv->visible_child != child_info)
2396 continue;
2397
2398 if (gtk_widget_get_visible (child))
2399 {
2400 if (orientation == GTK_ORIENTATION_VERTICAL)
2401 {
2402 if (for_size < 0)
2403 gtk_widget_get_preferred_height (child, &child_min, &child_nat);
2404 else
2405 gtk_widget_get_preferred_height_for_width (child, for_size, &child_min, &child_nat);
2406 }
2407 else
2408 {
2409 if (for_size < 0)
2410 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
2411 else
2412 gtk_widget_get_preferred_width_for_height (child, for_size, &child_min, &child_nat);
2413 }
2414
2415 *minimum = MAX (*minimum, child_min);
2416 *natural = MAX (*natural, child_nat);
2417 }
2418 }
2419
2420 if (priv->last_visible_child != NULL)
2421 {
2422 if (orientation == GTK_ORIENTATION_VERTICAL && !priv->vhomogeneous)
2423 {
2424 gdouble t = priv->interpolate_size ? gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE) : 1.0;
2425 *minimum = LERP (*minimum, priv->last_visible_widget_height, t);
2426 *natural = LERP (*natural, priv->last_visible_widget_height, t);
2427 }
2428 if (orientation == GTK_ORIENTATION_HORIZONTAL && !priv->hhomogeneous)
2429 {
2430 gdouble t = priv->interpolate_size ? gtk_progress_tracker_get_ease_out_cubic (&priv->tracker, FALSE) : 1.0;
2431 *minimum = LERP (*minimum, priv->last_visible_widget_width, t);
2432 *natural = LERP (*natural, priv->last_visible_widget_width, t);
2433 }
2434 }
2435 }
2436
2437 static void
gtk_stack_init(GtkStack * stack)2438 gtk_stack_init (GtkStack *stack)
2439 {
2440 GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
2441
2442 gtk_widget_set_has_window (GTK_WIDGET (stack), FALSE);
2443
2444 priv->vhomogeneous = TRUE;
2445 priv->hhomogeneous = TRUE;
2446 priv->transition_duration = 200;
2447 priv->transition_type = GTK_STACK_TRANSITION_TYPE_NONE;
2448
2449 priv->gadget = gtk_css_custom_gadget_new_for_node (gtk_widget_get_css_node (GTK_WIDGET (stack)),
2450 GTK_WIDGET (stack),
2451 gtk_stack_measure,
2452 gtk_stack_allocate,
2453 gtk_stack_render,
2454 NULL,
2455 NULL);
2456
2457 }
2458