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