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 "gtkenums.h" |
27 | #include "gtkaccessibleprivate.h" |
28 | #include "gtkatcontextprivate.h" |
29 | #include "gtkprivate.h" |
30 | #include "gtkintl.h" |
31 | #include "gtkprogresstrackerprivate.h" |
32 | #include "gtksettingsprivate.h" |
33 | #include "gtksnapshot.h" |
34 | #include "gtkwidgetprivate.h" |
35 | #include "gtksingleselection.h" |
36 | #include "gtklistlistmodelprivate.h" |
37 | #include <math.h> |
38 | #include <string.h> |
39 | |
40 | /** |
41 | * GtkStack: |
42 | * |
43 | * `GtkStack` is a container which only shows one of its children |
44 | * at a time. |
45 | * |
46 | * In contrast to `GtkNotebook`, `GtkStack` does not provide a means |
47 | * for users to change the visible child. Instead, a separate widget |
48 | * such as [class@Gtk.StackSwitcher] or [class@Gtk.StackSidebar] can |
49 | * be used with `GtkStack` to provide this functionality. |
50 | * |
51 | * Transitions between pages can be animated as slides or fades. This |
52 | * can be controlled with [method@Gtk.Stack.set_transition_type]. |
53 | * These animations respect the [property@Gtk.Settings:gtk-enable-animations] |
54 | * setting. |
55 | * |
56 | * `GtkStack` maintains a [class@Gtk.StackPage] object for each added |
57 | * child, which holds additional per-child properties. You |
58 | * obtain the `GtkStackPage` for a child with [method@Gtk.Stack.get_page] |
59 | * and you can obtain a `GtkSelectionModel` containing all the pages |
60 | * with [method@Gtk.Stack.get_pages]. |
61 | * |
62 | * # GtkStack as GtkBuildable |
63 | * |
64 | * To set child-specific properties in a .ui file, create `GtkStackPage` |
65 | * objects explicitly, and set the child widget as a property on it: |
66 | * |
67 | * ```xml |
68 | * <object class="GtkStack" id="stack"> |
69 | * <child> |
70 | * <object class="GtkStackPage"> |
71 | * <property name="name">page1</property> |
72 | * <property name="title">In the beginning…</property> |
73 | * <property name="child"> |
74 | * <object class="GtkLabel"> |
75 | * <property name="label">It was dark</property> |
76 | * </object> |
77 | * </property> |
78 | * </object> |
79 | * </child> |
80 | * ``` |
81 | * |
82 | * # CSS nodes |
83 | * |
84 | * `GtkStack` has a single CSS node named stack. |
85 | * |
86 | * # Accessibility |
87 | * |
88 | * `GtkStack` uses the %GTK_ACCESSIBLE_ROLE_TAB_PANEL for the stack |
89 | * pages, which are the accessible parent objects of the child widgets. |
90 | */ |
91 | |
92 | /** |
93 | * GtkStackTransitionType: |
94 | * @GTK_STACK_TRANSITION_TYPE_NONE: No transition |
95 | * @GTK_STACK_TRANSITION_TYPE_CROSSFADE: A cross-fade |
96 | * @GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT: Slide from left to right |
97 | * @GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT: Slide from right to left |
98 | * @GTK_STACK_TRANSITION_TYPE_SLIDE_UP: Slide from bottom up |
99 | * @GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN: Slide from top down |
100 | * @GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT: Slide from left or right according to the children order |
101 | * @GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN: Slide from top down or bottom up according to the order |
102 | * @GTK_STACK_TRANSITION_TYPE_OVER_UP: Cover the old page by sliding up |
103 | * @GTK_STACK_TRANSITION_TYPE_OVER_DOWN: Cover the old page by sliding down |
104 | * @GTK_STACK_TRANSITION_TYPE_OVER_LEFT: Cover the old page by sliding to the left |
105 | * @GTK_STACK_TRANSITION_TYPE_OVER_RIGHT: Cover the old page by sliding to the right |
106 | * @GTK_STACK_TRANSITION_TYPE_UNDER_UP: Uncover the new page by sliding up |
107 | * @GTK_STACK_TRANSITION_TYPE_UNDER_DOWN: Uncover the new page by sliding down |
108 | * @GTK_STACK_TRANSITION_TYPE_UNDER_LEFT: Uncover the new page by sliding to the left |
109 | * @GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT: Uncover the new page by sliding to the right |
110 | * @GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN: Cover the old page sliding up or uncover the new page sliding down, according to order |
111 | * @GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP: Cover the old page sliding down or uncover the new page sliding up, according to order |
112 | * @GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT: Cover the old page sliding left or uncover the new page sliding right, according to order |
113 | * @GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT: Cover the old page sliding right or uncover the new page sliding left, according to order |
114 | * @GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT: Pretend the pages are sides of a cube and rotate that cube to the left |
115 | * @GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT: Pretend the pages are sides of a cube and rotate that cube to the right |
116 | * @GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT: Pretend the pages are sides of a cube and rotate that cube to the left or right according to the children order |
117 | * |
118 | * Possible transitions between pages in a `GtkStack` widget. |
119 | * |
120 | * New values may be added to this enumeration over time. |
121 | */ |
122 | |
123 | /** |
124 | * GtkStackPage: |
125 | * |
126 | * `GtkStackPage` is an auxiliary class used by `GtkStack`. |
127 | */ |
128 | |
129 | /* TODO: |
130 | * filter events out events to the last_child widget during transitions |
131 | */ |
132 | |
133 | struct _GtkStack { |
134 | GtkWidget parent_instance; |
135 | }; |
136 | |
137 | typedef struct _GtkStackClass GtkStackClass; |
138 | struct _GtkStackClass { |
139 | GtkWidgetClass parent_class; |
140 | }; |
141 | |
142 | typedef struct { |
143 | GList *children; |
144 | |
145 | GtkStackPage *visible_child; |
146 | |
147 | gboolean homogeneous[2]; |
148 | |
149 | GtkStackTransitionType transition_type; |
150 | guint transition_duration; |
151 | |
152 | GtkStackPage *last_visible_child; |
153 | guint tick_id; |
154 | GtkProgressTracker tracker; |
155 | gboolean first_frame_skipped; |
156 | |
157 | int last_visible_widget_width; |
158 | int last_visible_widget_height; |
159 | |
160 | gboolean interpolate_size; |
161 | |
162 | GtkStackTransitionType active_transition_type; |
163 | |
164 | GtkSelectionModel *pages; |
165 | |
166 | } GtkStackPrivate; |
167 | |
168 | static void gtk_stack_buildable_interface_init (GtkBuildableIface *iface); |
169 | |
170 | G_DEFINE_TYPE_WITH_CODE (GtkStack, gtk_stack, GTK_TYPE_WIDGET, |
171 | G_ADD_PRIVATE (GtkStack) |
172 | G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, |
173 | gtk_stack_buildable_interface_init)) |
174 | enum { |
175 | PROP_0, |
176 | PROP_HHOMOGENEOUS, |
177 | PROP_VHOMOGENEOUS, |
178 | PROP_VISIBLE_CHILD, |
179 | PROP_VISIBLE_CHILD_NAME, |
180 | PROP_TRANSITION_DURATION, |
181 | PROP_TRANSITION_TYPE, |
182 | PROP_TRANSITION_RUNNING, |
183 | PROP_INTERPOLATE_SIZE, |
184 | PROP_PAGES, |
185 | LAST_PROP |
186 | }; |
187 | |
188 | enum |
189 | { |
190 | CHILD_PROP_0, |
191 | CHILD_PROP_CHILD, |
192 | CHILD_PROP_NAME, |
193 | CHILD_PROP_TITLE, |
194 | CHILD_PROP_ICON_NAME, |
195 | CHILD_PROP_NEEDS_ATTENTION, |
196 | CHILD_PROP_VISIBLE, |
197 | CHILD_PROP_USE_UNDERLINE, |
198 | LAST_CHILD_PROP, |
199 | |
200 | PROP_ACCESSIBLE_ROLE |
201 | }; |
202 | |
203 | struct _GtkStackPage |
204 | { |
205 | GObject parent_instance; |
206 | |
207 | GtkWidget *widget; |
208 | char *name; |
209 | char *title; |
210 | char *icon_name; |
211 | GtkWidget *last_focus; |
212 | |
213 | GtkATContext *at_context; |
214 | |
215 | guint needs_attention : 1; |
216 | guint visible : 1; |
217 | guint use_underline : 1; |
218 | }; |
219 | |
220 | typedef struct _GtkStackPageClass GtkStackPageClass; |
221 | struct _GtkStackPageClass |
222 | { |
223 | GObjectClass parent_class; |
224 | }; |
225 | |
226 | static GParamSpec *stack_props[LAST_PROP] = { NULL, }; |
227 | static GParamSpec *stack_page_props[LAST_CHILD_PROP] = { NULL, }; |
228 | |
229 | static GtkATContext * |
230 | gtk_stack_page_accessible_get_at_context (GtkAccessible *accessible) |
231 | { |
232 | GtkStackPage *page = GTK_STACK_PAGE (accessible); |
233 | |
234 | if (page->at_context == NULL) |
235 | { |
236 | GtkAccessibleRole role = GTK_ACCESSIBLE_ROLE_TAB_PANEL; |
237 | GdkDisplay *display; |
238 | |
239 | if (page->widget != NULL) |
240 | display = gtk_widget_get_display (widget: page->widget); |
241 | else |
242 | display = gdk_display_get_default (); |
243 | |
244 | page->at_context = gtk_at_context_create (accessible_role: role, accessible, display); |
245 | } |
246 | |
247 | return page->at_context; |
248 | } |
249 | |
250 | static gboolean |
251 | gtk_stack_page_accessible_get_platform_state (GtkAccessible *self, |
252 | GtkAccessiblePlatformState state) |
253 | { |
254 | return FALSE; |
255 | } |
256 | |
257 | static void |
258 | gtk_stack_page_accessible_init (GtkAccessibleInterface *iface) |
259 | { |
260 | iface->get_at_context = gtk_stack_page_accessible_get_at_context; |
261 | iface->get_platform_state = gtk_stack_page_accessible_get_platform_state; |
262 | } |
263 | |
264 | G_DEFINE_TYPE_WITH_CODE (GtkStackPage, gtk_stack_page, G_TYPE_OBJECT, |
265 | G_IMPLEMENT_INTERFACE (GTK_TYPE_ACCESSIBLE, |
266 | gtk_stack_page_accessible_init)) |
267 | |
268 | static void |
269 | gtk_stack_page_init (GtkStackPage *page) |
270 | { |
271 | page->visible = TRUE; |
272 | } |
273 | |
274 | static void |
275 | gtk_stack_page_finalize (GObject *object) |
276 | { |
277 | GtkStackPage *page = GTK_STACK_PAGE (object); |
278 | |
279 | g_clear_object (&page->widget); |
280 | g_free (mem: page->name); |
281 | g_free (mem: page->title); |
282 | g_free (mem: page->icon_name); |
283 | |
284 | if (page->last_focus) |
285 | g_object_remove_weak_pointer (G_OBJECT (page->last_focus), |
286 | weak_pointer_location: (gpointer *)&page->last_focus); |
287 | |
288 | g_clear_object (&page->at_context); |
289 | |
290 | G_OBJECT_CLASS (gtk_stack_page_parent_class)->finalize (object); |
291 | } |
292 | |
293 | static void |
294 | gtk_stack_page_dispose (GObject *object) |
295 | { |
296 | GtkStackPage *page = GTK_STACK_PAGE (object); |
297 | |
298 | if (page->at_context != NULL) |
299 | gtk_at_context_unrealize (self: page->at_context); |
300 | |
301 | G_OBJECT_CLASS (gtk_stack_page_parent_class)->dispose (object); |
302 | } |
303 | |
304 | static void |
305 | gtk_stack_page_get_property (GObject *object, |
306 | guint property_id, |
307 | GValue *value, |
308 | GParamSpec *pspec) |
309 | { |
310 | GtkStackPage *info = GTK_STACK_PAGE (object); |
311 | |
312 | switch (property_id) |
313 | { |
314 | case CHILD_PROP_CHILD: |
315 | g_value_set_object (value, v_object: info->widget); |
316 | break; |
317 | |
318 | case CHILD_PROP_NAME: |
319 | g_value_set_string (value, v_string: gtk_stack_page_get_name (self: info)); |
320 | break; |
321 | |
322 | case CHILD_PROP_TITLE: |
323 | g_value_set_string (value, v_string: gtk_stack_page_get_title (self: info)); |
324 | break; |
325 | |
326 | case CHILD_PROP_ICON_NAME: |
327 | g_value_set_string (value, v_string: gtk_stack_page_get_icon_name (self: info)); |
328 | break; |
329 | |
330 | case CHILD_PROP_NEEDS_ATTENTION: |
331 | g_value_set_boolean (value, v_boolean: gtk_stack_page_get_needs_attention (self: info)); |
332 | break; |
333 | |
334 | case CHILD_PROP_VISIBLE: |
335 | g_value_set_boolean (value, v_boolean: gtk_stack_page_get_visible (self: info)); |
336 | break; |
337 | |
338 | case CHILD_PROP_USE_UNDERLINE: |
339 | g_value_set_boolean (value, v_boolean: gtk_stack_page_get_use_underline (self: info)); |
340 | break; |
341 | |
342 | case PROP_ACCESSIBLE_ROLE: |
343 | g_value_set_enum (value, v_enum: GTK_ACCESSIBLE_ROLE_TAB_PANEL); |
344 | break; |
345 | |
346 | default: |
347 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
348 | break; |
349 | } |
350 | } |
351 | |
352 | static void |
353 | gtk_stack_page_set_property (GObject *object, |
354 | guint property_id, |
355 | const GValue *value, |
356 | GParamSpec *pspec) |
357 | { |
358 | GtkStackPage *info = GTK_STACK_PAGE (object); |
359 | |
360 | switch (property_id) |
361 | { |
362 | case CHILD_PROP_CHILD: |
363 | g_set_object (&info->widget, g_value_get_object (value)); |
364 | break; |
365 | |
366 | case CHILD_PROP_NAME: |
367 | gtk_stack_page_set_name (self: info, setting: g_value_get_string (value)); |
368 | break; |
369 | |
370 | case CHILD_PROP_TITLE: |
371 | gtk_stack_page_set_title (self: info, setting: g_value_get_string (value)); |
372 | break; |
373 | |
374 | case CHILD_PROP_ICON_NAME: |
375 | gtk_stack_page_set_icon_name (self: info, setting: g_value_get_string (value)); |
376 | break; |
377 | |
378 | case CHILD_PROP_NEEDS_ATTENTION: |
379 | gtk_stack_page_set_needs_attention (self: info, setting: g_value_get_boolean (value)); |
380 | break; |
381 | |
382 | case CHILD_PROP_VISIBLE: |
383 | gtk_stack_page_set_visible (self: info, visible: g_value_get_boolean (value)); |
384 | break; |
385 | |
386 | case CHILD_PROP_USE_UNDERLINE: |
387 | gtk_stack_page_set_use_underline (self: info, setting: g_value_get_boolean (value)); |
388 | break; |
389 | |
390 | case PROP_ACCESSIBLE_ROLE: |
391 | break; |
392 | |
393 | default: |
394 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
395 | break; |
396 | } |
397 | } |
398 | static void |
399 | gtk_stack_page_class_init (GtkStackPageClass *class) |
400 | { |
401 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
402 | |
403 | object_class->finalize = gtk_stack_page_finalize; |
404 | object_class->dispose = gtk_stack_page_dispose; |
405 | object_class->get_property = gtk_stack_page_get_property; |
406 | object_class->set_property = gtk_stack_page_set_property; |
407 | |
408 | /** |
409 | * GtkStackPage:child: (attributes org.gtk.Property.get=gtk_stack_page_get_child) |
410 | * |
411 | * The child that this page is for. |
412 | */ |
413 | stack_page_props[CHILD_PROP_CHILD] = |
414 | g_param_spec_object (name: "child" , |
415 | P_("Child" ), |
416 | P_("The child of the page" ), |
417 | GTK_TYPE_WIDGET, |
418 | GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); |
419 | |
420 | /** |
421 | * GtkStackPage:name: (attributes org.gtk.Property.get=gtk_stack_page_get_name org.gtk.Property.set=gtk_stack_page_set_name) |
422 | * |
423 | * The name of the child page. |
424 | */ |
425 | stack_page_props[CHILD_PROP_NAME] = |
426 | g_param_spec_string (name: "name" , |
427 | P_("Name" ), |
428 | P_("The name of the child page" ), |
429 | NULL, |
430 | GTK_PARAM_READWRITE); |
431 | |
432 | /** |
433 | * GtkStackPage:title: (attributes org.gtk.Property.get=gtk_stack_page_get_title org.gtk.Property.set=gtk_stack_page_set_title) |
434 | * |
435 | * The title of the child page. |
436 | */ |
437 | stack_page_props[CHILD_PROP_TITLE] = |
438 | g_param_spec_string (name: "title" , |
439 | P_("Title" ), |
440 | P_("The title of the child page" ), |
441 | NULL, |
442 | GTK_PARAM_READWRITE); |
443 | |
444 | /** |
445 | * GtkStackPage:icon-name: (attributes org.gtk.Property.get=gtk_stack_page_get_icon_name org.gtk.Property.set=gtk_stack_page_set_icon_name) |
446 | * |
447 | * The icon name of the child page. |
448 | */ |
449 | stack_page_props[CHILD_PROP_ICON_NAME] = |
450 | g_param_spec_string (name: "icon-name" , |
451 | P_("Icon name" ), |
452 | P_("The icon name of the child page" ), |
453 | NULL, |
454 | GTK_PARAM_READWRITE); |
455 | |
456 | /** |
457 | * GtkStackPage:needs-attention: (attributes org.gtk.Property.get=gtk_stack_page_get_needs_attention org.gtk.Property.set=gtk_stack_page_set_needs_attention) |
458 | * |
459 | * Whether the page requires the user attention. |
460 | * |
461 | * This is used by the [class@Gtk.StackSwitcher] to change the |
462 | * appearance of the corresponding button when a page needs |
463 | * attention and it is not the current one. |
464 | */ |
465 | stack_page_props[CHILD_PROP_NEEDS_ATTENTION] = |
466 | g_param_spec_boolean (name: "needs-attention" , |
467 | P_("Needs Attention" ), |
468 | P_("Whether this page needs attention" ), |
469 | FALSE, |
470 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
471 | |
472 | /** |
473 | * GtkStackPage:visible: (attributes org.gtk.Property.get=gtk_stack_page_get_visible org.gtk.Property.set=gtk_stack_page_set_visible) |
474 | * |
475 | * Whether this page is visible. |
476 | */ |
477 | stack_page_props[CHILD_PROP_VISIBLE] = |
478 | g_param_spec_boolean (name: "visible" , |
479 | P_("Visible" ), |
480 | P_("Whether this page is visible" ), |
481 | TRUE, |
482 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
483 | |
484 | /** |
485 | * GtkStackPage:use-underline: (attributes org.gtk.Property.get=gtk_stack_page_get_use_underline org.gtk.Property.set=gtk_stack_page_set_use_underline) |
486 | * |
487 | * If set, an underline in the title indicates a mnemonic. |
488 | */ |
489 | stack_page_props[CHILD_PROP_USE_UNDERLINE] = |
490 | g_param_spec_boolean (name: "use-underline" , |
491 | P_("Use underline" ), |
492 | P_("If set, an underline in the title indicates the next character should be used for the mnemonic accelerator key" ), |
493 | FALSE, |
494 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
495 | |
496 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_CHILD_PROP, pspecs: stack_page_props); |
497 | |
498 | g_object_class_override_property (oclass: object_class, property_id: PROP_ACCESSIBLE_ROLE, name: "accessible-role" ); |
499 | } |
500 | |
501 | #define GTK_TYPE_STACK_PAGES (gtk_stack_pages_get_type ()) |
502 | G_DECLARE_FINAL_TYPE (GtkStackPages, gtk_stack_pages, GTK, STACK_PAGES, GObject) |
503 | |
504 | struct _GtkStackPages |
505 | { |
506 | GObject parent_instance; |
507 | GtkStack *stack; |
508 | }; |
509 | |
510 | struct _GtkStackPagesClass |
511 | { |
512 | GObjectClass parent_class; |
513 | }; |
514 | |
515 | static GType |
516 | gtk_stack_pages_get_item_type (GListModel *model) |
517 | { |
518 | return GTK_TYPE_STACK_PAGE; |
519 | } |
520 | |
521 | static guint |
522 | gtk_stack_pages_get_n_items (GListModel *model) |
523 | { |
524 | GtkStackPages *pages = GTK_STACK_PAGES (ptr: model); |
525 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: pages->stack); |
526 | |
527 | return g_list_length (list: priv->children); |
528 | } |
529 | |
530 | static gpointer |
531 | gtk_stack_pages_get_item (GListModel *model, |
532 | guint position) |
533 | { |
534 | GtkStackPages *pages = GTK_STACK_PAGES (ptr: model); |
535 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: pages->stack); |
536 | GtkStackPage *page; |
537 | |
538 | page = g_list_nth_data (list: priv->children, n: position); |
539 | |
540 | if (!page) |
541 | return NULL; |
542 | |
543 | return g_object_ref (page); |
544 | } |
545 | |
546 | static void |
547 | gtk_stack_pages_list_model_init (GListModelInterface *iface) |
548 | { |
549 | iface->get_item_type = gtk_stack_pages_get_item_type; |
550 | iface->get_n_items = gtk_stack_pages_get_n_items; |
551 | iface->get_item = gtk_stack_pages_get_item; |
552 | } |
553 | |
554 | static gboolean |
555 | gtk_stack_pages_is_selected (GtkSelectionModel *model, |
556 | guint position) |
557 | { |
558 | GtkStackPages *pages = GTK_STACK_PAGES (ptr: model); |
559 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: pages->stack); |
560 | GtkStackPage *page; |
561 | |
562 | page = g_list_nth_data (list: priv->children, n: position); |
563 | |
564 | return page && page == priv->visible_child; |
565 | } |
566 | |
567 | static void set_visible_child (GtkStack *stack, |
568 | GtkStackPage *child_info, |
569 | GtkStackTransitionType transition_type, |
570 | guint transition_duration); |
571 | |
572 | static gboolean |
573 | gtk_stack_pages_select_item (GtkSelectionModel *model, |
574 | guint position, |
575 | gboolean exclusive) |
576 | { |
577 | GtkStackPages *pages = GTK_STACK_PAGES (ptr: model); |
578 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: pages->stack); |
579 | GtkStackPage *page; |
580 | |
581 | page = g_list_nth_data (list: priv->children, n: position); |
582 | |
583 | set_visible_child (stack: pages->stack, child_info: page, transition_type: priv->transition_type, transition_duration: priv->transition_duration); |
584 | |
585 | return TRUE; |
586 | } |
587 | |
588 | static void |
589 | gtk_stack_pages_selection_model_init (GtkSelectionModelInterface *iface) |
590 | { |
591 | iface->is_selected = gtk_stack_pages_is_selected; |
592 | iface->select_item = gtk_stack_pages_select_item; |
593 | } |
594 | |
595 | G_DEFINE_TYPE_WITH_CODE (GtkStackPages, gtk_stack_pages, G_TYPE_OBJECT, |
596 | G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_stack_pages_list_model_init) |
597 | G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL, gtk_stack_pages_selection_model_init)) |
598 | |
599 | static void |
600 | gtk_stack_pages_init (GtkStackPages *pages) |
601 | { |
602 | } |
603 | |
604 | static void |
605 | gtk_stack_pages_class_init (GtkStackPagesClass *class) |
606 | { |
607 | } |
608 | |
609 | static GtkStackPages * |
610 | gtk_stack_pages_new (GtkStack *stack) |
611 | { |
612 | GtkStackPages *pages; |
613 | |
614 | pages = g_object_new (GTK_TYPE_STACK_PAGES, NULL); |
615 | pages->stack = stack; |
616 | |
617 | return pages; |
618 | } |
619 | |
620 | static GtkStackPage *gtk_stack_add_internal (GtkStack *stack, |
621 | GtkWidget *child, |
622 | const char *name, |
623 | const char *title); |
624 | |
625 | static GtkSizeRequestMode gtk_stack_get_request_mode (GtkWidget *widget); |
626 | static void gtk_stack_compute_expand (GtkWidget *widget, |
627 | gboolean *hexpand, |
628 | gboolean *vexpand); |
629 | static void gtk_stack_size_allocate (GtkWidget *widget, |
630 | int width, |
631 | int height, |
632 | int baseline); |
633 | static void gtk_stack_snapshot (GtkWidget *widget, |
634 | GtkSnapshot *snapshot); |
635 | static void gtk_stack_measure (GtkWidget *widget, |
636 | GtkOrientation orientation, |
637 | int for_size, |
638 | int *minimum, |
639 | int *natural, |
640 | int *minimum_baseline, |
641 | int *natural_baseline); |
642 | static void gtk_stack_dispose (GObject *obj); |
643 | static void gtk_stack_finalize (GObject *obj); |
644 | static void gtk_stack_get_property (GObject *object, |
645 | guint property_id, |
646 | GValue *value, |
647 | GParamSpec *pspec); |
648 | static void gtk_stack_set_property (GObject *object, |
649 | guint property_id, |
650 | const GValue *value, |
651 | GParamSpec *pspec); |
652 | static void gtk_stack_unschedule_ticks (GtkStack *stack); |
653 | |
654 | |
655 | static void gtk_stack_add_page (GtkStack *stack, |
656 | GtkStackPage *page); |
657 | |
658 | static GtkBuildableIface *parent_buildable_iface; |
659 | |
660 | static void |
661 | gtk_stack_buildable_add_child (GtkBuildable *buildable, |
662 | GtkBuilder *builder, |
663 | GObject *child, |
664 | const char *type) |
665 | { |
666 | if (GTK_IS_STACK_PAGE (child)) |
667 | gtk_stack_add_page (GTK_STACK (buildable), GTK_STACK_PAGE (child)); |
668 | else if (GTK_IS_WIDGET (child)) |
669 | gtk_stack_add_internal (GTK_STACK (buildable), GTK_WIDGET (child), NULL, NULL); |
670 | else |
671 | parent_buildable_iface->add_child (buildable, builder, child, type); |
672 | } |
673 | |
674 | static void |
675 | gtk_stack_buildable_interface_init (GtkBuildableIface *iface) |
676 | { |
677 | parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface); |
678 | |
679 | iface->add_child = gtk_stack_buildable_add_child; |
680 | } |
681 | |
682 | static void stack_remove (GtkStack *stack, |
683 | GtkWidget *child, |
684 | gboolean in_dispose); |
685 | |
686 | static void |
687 | gtk_stack_dispose (GObject *obj) |
688 | { |
689 | GtkStack *stack = GTK_STACK (obj); |
690 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
691 | GtkWidget *child; |
692 | guint n_pages = g_list_length (list: priv->children); |
693 | |
694 | while ((child = gtk_widget_get_first_child (GTK_WIDGET (stack)))) |
695 | stack_remove (stack, child, TRUE); |
696 | |
697 | if (priv->pages) |
698 | g_list_model_items_changed (list: G_LIST_MODEL (ptr: priv->pages), position: 0, removed: n_pages, added: 0); |
699 | |
700 | G_OBJECT_CLASS (gtk_stack_parent_class)->dispose (obj); |
701 | } |
702 | |
703 | static void |
704 | gtk_stack_finalize (GObject *obj) |
705 | { |
706 | GtkStack *stack = GTK_STACK (obj); |
707 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
708 | |
709 | if (priv->pages) |
710 | g_object_remove_weak_pointer (G_OBJECT (priv->pages), weak_pointer_location: (gpointer *)&priv->pages); |
711 | |
712 | gtk_stack_unschedule_ticks (stack); |
713 | |
714 | G_OBJECT_CLASS (gtk_stack_parent_class)->finalize (obj); |
715 | } |
716 | |
717 | static void |
718 | gtk_stack_get_property (GObject *object, |
719 | guint property_id, |
720 | GValue *value, |
721 | GParamSpec *pspec) |
722 | { |
723 | GtkStack *stack = GTK_STACK (object); |
724 | |
725 | switch (property_id) |
726 | { |
727 | case PROP_HHOMOGENEOUS: |
728 | g_value_set_boolean (value, v_boolean: gtk_stack_get_hhomogeneous (stack)); |
729 | break; |
730 | case PROP_VHOMOGENEOUS: |
731 | g_value_set_boolean (value, v_boolean: gtk_stack_get_vhomogeneous (stack)); |
732 | break; |
733 | case PROP_VISIBLE_CHILD: |
734 | g_value_set_object (value, v_object: gtk_stack_get_visible_child (stack)); |
735 | break; |
736 | case PROP_VISIBLE_CHILD_NAME: |
737 | g_value_set_string (value, v_string: gtk_stack_get_visible_child_name (stack)); |
738 | break; |
739 | case PROP_TRANSITION_DURATION: |
740 | g_value_set_uint (value, v_uint: gtk_stack_get_transition_duration (stack)); |
741 | break; |
742 | case PROP_TRANSITION_TYPE: |
743 | g_value_set_enum (value, v_enum: gtk_stack_get_transition_type (stack)); |
744 | break; |
745 | case PROP_TRANSITION_RUNNING: |
746 | g_value_set_boolean (value, v_boolean: gtk_stack_get_transition_running (stack)); |
747 | break; |
748 | case PROP_INTERPOLATE_SIZE: |
749 | g_value_set_boolean (value, v_boolean: gtk_stack_get_interpolate_size (stack)); |
750 | break; |
751 | case PROP_PAGES: |
752 | g_value_take_object (value, v_object: gtk_stack_get_pages (stack)); |
753 | break; |
754 | default: |
755 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
756 | break; |
757 | } |
758 | } |
759 | |
760 | static void |
761 | gtk_stack_set_property (GObject *object, |
762 | guint property_id, |
763 | const GValue *value, |
764 | GParamSpec *pspec) |
765 | { |
766 | GtkStack *stack = GTK_STACK (object); |
767 | |
768 | switch (property_id) |
769 | { |
770 | case PROP_HHOMOGENEOUS: |
771 | gtk_stack_set_hhomogeneous (stack, hhomogeneous: g_value_get_boolean (value)); |
772 | break; |
773 | case PROP_VHOMOGENEOUS: |
774 | gtk_stack_set_vhomogeneous (stack, vhomogeneous: g_value_get_boolean (value)); |
775 | break; |
776 | case PROP_VISIBLE_CHILD: |
777 | gtk_stack_set_visible_child (stack, child: g_value_get_object (value)); |
778 | break; |
779 | case PROP_VISIBLE_CHILD_NAME: |
780 | gtk_stack_set_visible_child_name (stack, name: g_value_get_string (value)); |
781 | break; |
782 | case PROP_TRANSITION_DURATION: |
783 | gtk_stack_set_transition_duration (stack, duration: g_value_get_uint (value)); |
784 | break; |
785 | case PROP_TRANSITION_TYPE: |
786 | gtk_stack_set_transition_type (stack, transition: g_value_get_enum (value)); |
787 | break; |
788 | case PROP_INTERPOLATE_SIZE: |
789 | gtk_stack_set_interpolate_size (stack, interpolate_size: g_value_get_boolean (value)); |
790 | break; |
791 | default: |
792 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
793 | break; |
794 | } |
795 | } |
796 | |
797 | static void |
798 | gtk_stack_class_init (GtkStackClass *klass) |
799 | { |
800 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
801 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
802 | |
803 | object_class->get_property = gtk_stack_get_property; |
804 | object_class->set_property = gtk_stack_set_property; |
805 | object_class->dispose = gtk_stack_dispose; |
806 | object_class->finalize = gtk_stack_finalize; |
807 | |
808 | widget_class->size_allocate = gtk_stack_size_allocate; |
809 | widget_class->snapshot = gtk_stack_snapshot; |
810 | widget_class->measure = gtk_stack_measure; |
811 | widget_class->compute_expand = gtk_stack_compute_expand; |
812 | widget_class->get_request_mode = gtk_stack_get_request_mode; |
813 | |
814 | /** |
815 | * GtkStack:hhomogeneous: (attributes org.gtk.Property.get=gtk_stack_get_hhomogeneous org.gtk.Property.set=gtk_stack_set_hhomogeneous) |
816 | * |
817 | * %TRUE if the stack allocates the same width for all children. |
818 | */ |
819 | stack_props[PROP_HHOMOGENEOUS] = |
820 | g_param_spec_boolean (name: "hhomogeneous" , P_("Horizontally homogeneous" ), P_("Horizontally homogeneous sizing" ), |
821 | TRUE, |
822 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
823 | |
824 | /** |
825 | * GtkStack:vhomogeneous: (attributes org.gtk.Property.get=gtk_stack_get_vhomogeneous org.gtk.Property.set=gtk_stack_set_vhomogeneous) |
826 | * |
827 | * %TRUE if the stack allocates the same height for all children. |
828 | */ |
829 | stack_props[PROP_VHOMOGENEOUS] = |
830 | g_param_spec_boolean (name: "vhomogeneous" , P_("Vertically homogeneous" ), P_("Vertically homogeneous sizing" ), |
831 | TRUE, |
832 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
833 | |
834 | /** |
835 | * GtkStack:visible-child: (attributes org.gtk.Property.get=gtk_stack_get_visible_child org.gtk.Property.set=gtk_stack_set_visible_child) |
836 | * |
837 | * The widget currently visible in the stack. |
838 | */ |
839 | stack_props[PROP_VISIBLE_CHILD] = |
840 | g_param_spec_object (name: "visible-child" , P_("Visible child" ), P_("The widget currently visible in the stack" ), |
841 | GTK_TYPE_WIDGET, |
842 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
843 | |
844 | /** |
845 | * GtkStack:visible-child-name: (attributes org.gtk.Property.get=gtk_stack_get_visible_child_name org.gtk.Poperty.set=gtk_stack_set_visible_child_name) |
846 | * |
847 | * The name of the widget currently visible in the stack. |
848 | */ |
849 | stack_props[PROP_VISIBLE_CHILD_NAME] = |
850 | g_param_spec_string (name: "visible-child-name" , P_("Name of visible child" ), P_("The name of the widget currently visible in the stack" ), |
851 | NULL, |
852 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
853 | |
854 | /** |
855 | * GtkStack:transition-duration: (attributes org.gtk.Property.get=gtk_stack_get_transition_duration org.gtk.Property.set=gtk_stack_set_transition_duration) |
856 | * |
857 | * The animation duration, in milliseconds. |
858 | */ |
859 | stack_props[PROP_TRANSITION_DURATION] = |
860 | g_param_spec_uint (name: "transition-duration" , P_("Transition duration" ), P_("The animation duration, in milliseconds" ), |
861 | minimum: 0, G_MAXUINT, default_value: 200, |
862 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
863 | |
864 | /** |
865 | * GtkStack:transition-type: (attributes org.gtk.Property.get=gtk_stack_get_transition_type org.gtk.Property.set=gtk_stack_set_transition_type) |
866 | * |
867 | * The type of animation used to transition. |
868 | */ |
869 | stack_props[PROP_TRANSITION_TYPE] = |
870 | g_param_spec_enum (name: "transition-type" , P_("Transition type" ), P_("The type of animation used to transition" ), |
871 | enum_type: GTK_TYPE_STACK_TRANSITION_TYPE, default_value: GTK_STACK_TRANSITION_TYPE_NONE, |
872 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
873 | |
874 | /** |
875 | * GtkStack:transition-running: (attributes org.gtk.Property.get=gtk_stack_get_transition_running) |
876 | * |
877 | * Whether or not the transition is currently running. |
878 | */ |
879 | stack_props[PROP_TRANSITION_RUNNING] = |
880 | g_param_spec_boolean (name: "transition-running" , P_("Transition running" ), P_("Whether or not the transition is currently running" ), |
881 | FALSE, |
882 | GTK_PARAM_READABLE); |
883 | |
884 | /** |
885 | * GtkStack:interpolate-size: (attributes org.gtk.Property.get=gtk_stack_get_interpolate_size org.gtk.Property.set=gtk_stack_set_interpolate_size) |
886 | * |
887 | * Whether or not the size should smoothly change during the transition. |
888 | */ |
889 | stack_props[PROP_INTERPOLATE_SIZE] = |
890 | g_param_spec_boolean (name: "interpolate-size" , P_("Interpolate size" ), P_("Whether or not the size should smoothly change when changing between differently sized children" ), |
891 | FALSE, |
892 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
893 | |
894 | /** |
895 | * GtkStack:pages: (attributes org.gtk.Property.get=gtk_stack_get_pages) |
896 | * |
897 | * A selection model with the stack pages. |
898 | */ |
899 | stack_props[PROP_PAGES] = |
900 | g_param_spec_object (name: "pages" , P_("Pages" ), P_("A selection model with the stacks pages" ), |
901 | GTK_TYPE_SELECTION_MODEL, |
902 | GTK_PARAM_READABLE); |
903 | |
904 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: stack_props); |
905 | |
906 | gtk_widget_class_set_css_name (widget_class, I_("stack" )); |
907 | gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_GROUP); |
908 | } |
909 | |
910 | /** |
911 | * gtk_stack_new: |
912 | * |
913 | * Creates a new `GtkStack`. |
914 | * |
915 | * Returns: a new `GtkStack` |
916 | */ |
917 | GtkWidget * |
918 | gtk_stack_new (void) |
919 | { |
920 | return g_object_new (GTK_TYPE_STACK, NULL); |
921 | } |
922 | |
923 | static GtkStackPage * |
924 | find_child_info_for_widget (GtkStack *stack, |
925 | GtkWidget *child) |
926 | { |
927 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
928 | GtkStackPage *info; |
929 | GList *l; |
930 | |
931 | for (l = priv->children; l != NULL; l = l->next) |
932 | { |
933 | info = l->data; |
934 | if (info->widget == child) |
935 | return info; |
936 | } |
937 | |
938 | return NULL; |
939 | } |
940 | |
941 | static inline gboolean |
942 | is_left_transition (GtkStackTransitionType transition_type) |
943 | { |
944 | return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT || |
945 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_LEFT); |
946 | } |
947 | |
948 | static inline gboolean |
949 | is_right_transition (GtkStackTransitionType transition_type) |
950 | { |
951 | return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT || |
952 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_RIGHT); |
953 | } |
954 | |
955 | static inline gboolean |
956 | is_up_transition (GtkStackTransitionType transition_type) |
957 | { |
958 | return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_UP || |
959 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_UP); |
960 | } |
961 | |
962 | static inline gboolean |
963 | is_down_transition (GtkStackTransitionType transition_type) |
964 | { |
965 | return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN || |
966 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_DOWN); |
967 | } |
968 | |
969 | /* Transitions that cause the bin window to move */ |
970 | static inline gboolean |
971 | is_window_moving_transition (GtkStackTransitionType transition_type) |
972 | { |
973 | return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT || |
974 | transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT || |
975 | transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_UP || |
976 | transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN || |
977 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_UP || |
978 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_DOWN || |
979 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_LEFT || |
980 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_RIGHT); |
981 | } |
982 | |
983 | /* Transitions that change direction depending on the relative order of the |
984 | old and new child */ |
985 | static inline gboolean |
986 | is_direction_dependent_transition (GtkStackTransitionType transition_type) |
987 | { |
988 | return (transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT || |
989 | transition_type == GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN || |
990 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN || |
991 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP || |
992 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT || |
993 | transition_type == GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT || |
994 | transition_type == GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT); |
995 | } |
996 | |
997 | /* Returns simple transition type for a direction dependent transition, given |
998 | whether the new child (the one being switched to) is first in the stacking order |
999 | (added earlier). */ |
1000 | static inline GtkStackTransitionType |
1001 | get_simple_transition_type (gboolean new_child_first, |
1002 | GtkStackTransitionType transition_type) |
1003 | { |
1004 | switch (transition_type) |
1005 | { |
1006 | case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT: |
1007 | return new_child_first ? GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT : GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT; |
1008 | case GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT: |
1009 | return new_child_first ? GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT : GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT; |
1010 | case GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN: |
1011 | return new_child_first ? GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN : GTK_STACK_TRANSITION_TYPE_SLIDE_UP; |
1012 | case GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN: |
1013 | return new_child_first ? GTK_STACK_TRANSITION_TYPE_UNDER_DOWN : GTK_STACK_TRANSITION_TYPE_OVER_UP; |
1014 | case GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP: |
1015 | return new_child_first ? GTK_STACK_TRANSITION_TYPE_UNDER_UP : GTK_STACK_TRANSITION_TYPE_OVER_DOWN; |
1016 | case GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT: |
1017 | return new_child_first ? GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT : GTK_STACK_TRANSITION_TYPE_OVER_LEFT; |
1018 | case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT: |
1019 | return new_child_first ? GTK_STACK_TRANSITION_TYPE_UNDER_LEFT : GTK_STACK_TRANSITION_TYPE_OVER_RIGHT; |
1020 | case GTK_STACK_TRANSITION_TYPE_SLIDE_UP: |
1021 | case GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN: |
1022 | case GTK_STACK_TRANSITION_TYPE_OVER_UP: |
1023 | case GTK_STACK_TRANSITION_TYPE_OVER_DOWN: |
1024 | case GTK_STACK_TRANSITION_TYPE_UNDER_UP: |
1025 | case GTK_STACK_TRANSITION_TYPE_UNDER_DOWN: |
1026 | case GTK_STACK_TRANSITION_TYPE_NONE: |
1027 | case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT: |
1028 | case GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT: |
1029 | case GTK_STACK_TRANSITION_TYPE_OVER_LEFT: |
1030 | case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT: |
1031 | case GTK_STACK_TRANSITION_TYPE_UNDER_LEFT: |
1032 | case GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT: |
1033 | case GTK_STACK_TRANSITION_TYPE_CROSSFADE: |
1034 | case GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT: |
1035 | case GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT: |
1036 | default: |
1037 | return transition_type; |
1038 | } |
1039 | } |
1040 | |
1041 | static int |
1042 | get_bin_window_x (GtkStack *stack) |
1043 | { |
1044 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1045 | int width; |
1046 | int x = 0; |
1047 | |
1048 | width = gtk_widget_get_width (GTK_WIDGET (stack)); |
1049 | |
1050 | if (gtk_progress_tracker_get_state (tracker: &priv->tracker) != GTK_PROGRESS_STATE_AFTER) |
1051 | { |
1052 | if (is_left_transition (transition_type: priv->active_transition_type)) |
1053 | x = width * (1 - gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE)); |
1054 | if (is_right_transition (transition_type: priv->active_transition_type)) |
1055 | x = -width * (1 - gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE)); |
1056 | } |
1057 | |
1058 | return x; |
1059 | } |
1060 | |
1061 | static int |
1062 | get_bin_window_y (GtkStack *stack) |
1063 | { |
1064 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1065 | int height; |
1066 | int y = 0; |
1067 | |
1068 | height = gtk_widget_get_height (GTK_WIDGET (stack)); |
1069 | |
1070 | if (gtk_progress_tracker_get_state (tracker: &priv->tracker) != GTK_PROGRESS_STATE_AFTER) |
1071 | { |
1072 | if (is_up_transition (transition_type: priv->active_transition_type)) |
1073 | y = height * (1 - gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE)); |
1074 | if (is_down_transition(transition_type: priv->active_transition_type)) |
1075 | y = -height * (1 - gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE)); |
1076 | } |
1077 | |
1078 | return y; |
1079 | } |
1080 | |
1081 | static void |
1082 | gtk_stack_progress_updated (GtkStack *stack) |
1083 | { |
1084 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1085 | |
1086 | if (!priv->homogeneous[GTK_ORIENTATION_VERTICAL] || !priv->homogeneous[GTK_ORIENTATION_HORIZONTAL]) |
1087 | gtk_widget_queue_resize (GTK_WIDGET (stack)); |
1088 | else if (is_window_moving_transition (transition_type: priv->active_transition_type)) |
1089 | gtk_widget_queue_allocate (GTK_WIDGET (stack)); |
1090 | else |
1091 | gtk_widget_queue_draw (GTK_WIDGET (stack)); |
1092 | |
1093 | if (gtk_progress_tracker_get_state (tracker: &priv->tracker) == GTK_PROGRESS_STATE_AFTER && |
1094 | priv->last_visible_child != NULL) |
1095 | { |
1096 | gtk_widget_set_child_visible (widget: priv->last_visible_child->widget, FALSE); |
1097 | priv->last_visible_child = NULL; |
1098 | } |
1099 | } |
1100 | |
1101 | static gboolean |
1102 | gtk_stack_transition_cb (GtkWidget *widget, |
1103 | GdkFrameClock *frame_clock, |
1104 | gpointer user_data) |
1105 | { |
1106 | GtkStack *stack = GTK_STACK (widget); |
1107 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1108 | |
1109 | if (priv->first_frame_skipped) |
1110 | gtk_progress_tracker_advance_frame (tracker: &priv->tracker, |
1111 | frame_time: gdk_frame_clock_get_frame_time (frame_clock)); |
1112 | else |
1113 | priv->first_frame_skipped = TRUE; |
1114 | |
1115 | /* Finish animation early if not mapped anymore */ |
1116 | if (!gtk_widget_get_mapped (widget)) |
1117 | gtk_progress_tracker_finish (tracker: &priv->tracker); |
1118 | |
1119 | gtk_stack_progress_updated (GTK_STACK (widget)); |
1120 | |
1121 | if (gtk_progress_tracker_get_state (tracker: &priv->tracker) == GTK_PROGRESS_STATE_AFTER) |
1122 | { |
1123 | priv->tick_id = 0; |
1124 | g_object_notify_by_pspec (G_OBJECT (stack), pspec: stack_props[PROP_TRANSITION_RUNNING]); |
1125 | |
1126 | return FALSE; |
1127 | } |
1128 | |
1129 | return TRUE; |
1130 | } |
1131 | |
1132 | static void |
1133 | gtk_stack_schedule_ticks (GtkStack *stack) |
1134 | { |
1135 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1136 | |
1137 | if (priv->tick_id == 0) |
1138 | { |
1139 | priv->tick_id = |
1140 | gtk_widget_add_tick_callback (GTK_WIDGET (stack), callback: gtk_stack_transition_cb, user_data: stack, NULL); |
1141 | g_object_notify_by_pspec (G_OBJECT (stack), pspec: stack_props[PROP_TRANSITION_RUNNING]); |
1142 | } |
1143 | } |
1144 | |
1145 | static void |
1146 | gtk_stack_unschedule_ticks (GtkStack *stack) |
1147 | { |
1148 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1149 | |
1150 | if (priv->tick_id != 0) |
1151 | { |
1152 | gtk_widget_remove_tick_callback (GTK_WIDGET (stack), id: priv->tick_id); |
1153 | priv->tick_id = 0; |
1154 | g_object_notify_by_pspec (G_OBJECT (stack), pspec: stack_props[PROP_TRANSITION_RUNNING]); |
1155 | } |
1156 | } |
1157 | |
1158 | static GtkStackTransitionType |
1159 | effective_transition_type (GtkStack *stack, |
1160 | GtkStackTransitionType transition_type) |
1161 | { |
1162 | if (_gtk_widget_get_direction (GTK_WIDGET (stack)) == GTK_TEXT_DIR_RTL) |
1163 | { |
1164 | switch (transition_type) |
1165 | { |
1166 | case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT: |
1167 | return GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT; |
1168 | case GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT: |
1169 | return GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT; |
1170 | case GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT: |
1171 | return GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT; |
1172 | case GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT: |
1173 | return GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT; |
1174 | case GTK_STACK_TRANSITION_TYPE_OVER_LEFT: |
1175 | return GTK_STACK_TRANSITION_TYPE_OVER_RIGHT; |
1176 | case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT: |
1177 | return GTK_STACK_TRANSITION_TYPE_OVER_LEFT; |
1178 | case GTK_STACK_TRANSITION_TYPE_UNDER_LEFT: |
1179 | return GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT; |
1180 | case GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT: |
1181 | return GTK_STACK_TRANSITION_TYPE_UNDER_LEFT; |
1182 | case GTK_STACK_TRANSITION_TYPE_SLIDE_UP: |
1183 | case GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN: |
1184 | case GTK_STACK_TRANSITION_TYPE_OVER_UP: |
1185 | case GTK_STACK_TRANSITION_TYPE_OVER_DOWN: |
1186 | case GTK_STACK_TRANSITION_TYPE_UNDER_UP: |
1187 | case GTK_STACK_TRANSITION_TYPE_UNDER_DOWN: |
1188 | case GTK_STACK_TRANSITION_TYPE_NONE: |
1189 | case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT: |
1190 | case GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN: |
1191 | case GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN: |
1192 | case GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP: |
1193 | case GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT: |
1194 | case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT: |
1195 | case GTK_STACK_TRANSITION_TYPE_CROSSFADE: |
1196 | case GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT: |
1197 | default: |
1198 | return transition_type; |
1199 | } |
1200 | } |
1201 | else |
1202 | { |
1203 | return transition_type; |
1204 | } |
1205 | } |
1206 | |
1207 | static void |
1208 | gtk_stack_start_transition (GtkStack *stack, |
1209 | GtkStackTransitionType transition_type, |
1210 | guint transition_duration) |
1211 | { |
1212 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1213 | GtkWidget *widget = GTK_WIDGET (stack); |
1214 | |
1215 | if (gtk_widget_get_mapped (widget) && |
1216 | gtk_settings_get_enable_animations (settings: gtk_widget_get_settings (widget)) && |
1217 | transition_type != GTK_STACK_TRANSITION_TYPE_NONE && |
1218 | transition_duration != 0 && |
1219 | priv->last_visible_child != NULL) |
1220 | { |
1221 | priv->active_transition_type = effective_transition_type (stack, transition_type); |
1222 | priv->first_frame_skipped = FALSE; |
1223 | gtk_stack_schedule_ticks (stack); |
1224 | gtk_progress_tracker_start (tracker: &priv->tracker, |
1225 | duration: priv->transition_duration * 1000, |
1226 | delay: 0, |
1227 | iteration_count: 1.0); |
1228 | } |
1229 | else |
1230 | { |
1231 | gtk_stack_unschedule_ticks (stack); |
1232 | priv->active_transition_type = GTK_STACK_TRANSITION_TYPE_NONE; |
1233 | gtk_progress_tracker_finish (tracker: &priv->tracker); |
1234 | } |
1235 | |
1236 | gtk_stack_progress_updated (GTK_STACK (widget)); |
1237 | } |
1238 | |
1239 | static void |
1240 | set_visible_child (GtkStack *stack, |
1241 | GtkStackPage *child_info, |
1242 | GtkStackTransitionType transition_type, |
1243 | guint transition_duration) |
1244 | { |
1245 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1246 | GtkStackPage *info; |
1247 | GtkWidget *widget = GTK_WIDGET (stack); |
1248 | GList *l; |
1249 | GtkWidget *focus; |
1250 | gboolean contains_focus = FALSE; |
1251 | guint old_pos = GTK_INVALID_LIST_POSITION; |
1252 | guint new_pos = GTK_INVALID_LIST_POSITION; |
1253 | |
1254 | /* if we are being destroyed, do not bother with transitions |
1255 | * and notifications |
1256 | */ |
1257 | if (gtk_widget_in_destruction (widget)) |
1258 | return; |
1259 | |
1260 | /* If none, pick first visible */ |
1261 | if (child_info == NULL) |
1262 | { |
1263 | for (l = priv->children; l != NULL; l = l->next) |
1264 | { |
1265 | info = l->data; |
1266 | if (gtk_widget_get_visible (widget: info->widget)) |
1267 | { |
1268 | child_info = info; |
1269 | break; |
1270 | } |
1271 | } |
1272 | } |
1273 | |
1274 | if (child_info == priv->visible_child) |
1275 | return; |
1276 | |
1277 | if (priv->pages) |
1278 | { |
1279 | guint position; |
1280 | for (l = priv->children, position = 0; l != NULL; l = l->next, position++) |
1281 | { |
1282 | info = l->data; |
1283 | if (info == priv->visible_child) |
1284 | old_pos = position; |
1285 | else if (info == child_info) |
1286 | new_pos = position; |
1287 | } |
1288 | } |
1289 | |
1290 | if (gtk_widget_get_root (widget)) |
1291 | focus = gtk_root_get_focus (self: gtk_widget_get_root (widget)); |
1292 | else |
1293 | focus = NULL; |
1294 | if (focus && |
1295 | priv->visible_child && |
1296 | priv->visible_child->widget && |
1297 | gtk_widget_is_ancestor (widget: focus, ancestor: priv->visible_child->widget)) |
1298 | { |
1299 | contains_focus = TRUE; |
1300 | |
1301 | if (priv->visible_child->last_focus) |
1302 | g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus), |
1303 | weak_pointer_location: (gpointer *)&priv->visible_child->last_focus); |
1304 | priv->visible_child->last_focus = focus; |
1305 | g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus), |
1306 | weak_pointer_location: (gpointer *)&priv->visible_child->last_focus); |
1307 | } |
1308 | |
1309 | if (priv->last_visible_child) |
1310 | gtk_widget_set_child_visible (widget: priv->last_visible_child->widget, FALSE); |
1311 | priv->last_visible_child = NULL; |
1312 | |
1313 | if (priv->visible_child && priv->visible_child->widget) |
1314 | { |
1315 | if (gtk_widget_is_visible (widget)) |
1316 | { |
1317 | priv->last_visible_child = priv->visible_child; |
1318 | priv->last_visible_widget_width = gtk_widget_get_width (widget); |
1319 | priv->last_visible_widget_height = gtk_widget_get_height (widget); |
1320 | } |
1321 | else |
1322 | { |
1323 | gtk_widget_set_child_visible (widget: priv->visible_child->widget, FALSE); |
1324 | } |
1325 | } |
1326 | |
1327 | priv->visible_child = child_info; |
1328 | |
1329 | if (child_info) |
1330 | { |
1331 | gtk_widget_set_child_visible (widget: child_info->widget, TRUE); |
1332 | |
1333 | if (contains_focus) |
1334 | { |
1335 | if (child_info->last_focus) |
1336 | gtk_widget_grab_focus (widget: child_info->last_focus); |
1337 | else |
1338 | gtk_widget_child_focus (widget: child_info->widget, direction: GTK_DIR_TAB_FORWARD); |
1339 | } |
1340 | } |
1341 | |
1342 | if ((child_info == NULL || priv->last_visible_child == NULL) && |
1343 | is_direction_dependent_transition (transition_type)) |
1344 | { |
1345 | transition_type = GTK_STACK_TRANSITION_TYPE_NONE; |
1346 | } |
1347 | else if (is_direction_dependent_transition (transition_type)) |
1348 | { |
1349 | gboolean i_first = FALSE; |
1350 | for (l = priv->children; l != NULL; l = l->next) |
1351 | { |
1352 | if (child_info == l->data) |
1353 | { |
1354 | i_first = TRUE; |
1355 | break; |
1356 | } |
1357 | if (priv->last_visible_child == l->data) |
1358 | break; |
1359 | } |
1360 | |
1361 | transition_type = get_simple_transition_type (new_child_first: i_first, transition_type); |
1362 | } |
1363 | |
1364 | if (priv->homogeneous[GTK_ORIENTATION_HORIZONTAL] && priv->homogeneous[GTK_ORIENTATION_VERTICAL]) |
1365 | gtk_widget_queue_allocate (widget); |
1366 | else |
1367 | gtk_widget_queue_resize (widget); |
1368 | |
1369 | g_object_notify_by_pspec (G_OBJECT (stack), pspec: stack_props[PROP_VISIBLE_CHILD]); |
1370 | g_object_notify_by_pspec (G_OBJECT (stack), |
1371 | pspec: stack_props[PROP_VISIBLE_CHILD_NAME]); |
1372 | |
1373 | if (priv->pages) |
1374 | { |
1375 | if (old_pos == GTK_INVALID_LIST_POSITION && new_pos == GTK_INVALID_LIST_POSITION) |
1376 | ; /* nothing to do */ |
1377 | else if (old_pos == GTK_INVALID_LIST_POSITION) |
1378 | gtk_selection_model_selection_changed (model: priv->pages, position: new_pos, n_items: 1); |
1379 | else if (new_pos == GTK_INVALID_LIST_POSITION) |
1380 | gtk_selection_model_selection_changed (model: priv->pages, position: old_pos, n_items: 1); |
1381 | else |
1382 | gtk_selection_model_selection_changed (model: priv->pages, |
1383 | MIN (old_pos, new_pos), |
1384 | MAX (old_pos, new_pos) - MIN (old_pos, new_pos) + 1); |
1385 | } |
1386 | |
1387 | gtk_stack_start_transition (stack, transition_type, transition_duration); |
1388 | } |
1389 | |
1390 | static void |
1391 | update_child_visible (GtkStack *stack, |
1392 | GtkStackPage *child_info) |
1393 | { |
1394 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1395 | gboolean visible; |
1396 | |
1397 | visible = child_info->visible && gtk_widget_get_visible (widget: child_info->widget); |
1398 | |
1399 | if (priv->visible_child == NULL && visible) |
1400 | set_visible_child (stack, child_info, transition_type: priv->transition_type, transition_duration: priv->transition_duration); |
1401 | else if (priv->visible_child == child_info && !visible) |
1402 | set_visible_child (stack, NULL, transition_type: priv->transition_type, transition_duration: priv->transition_duration); |
1403 | |
1404 | if (child_info == priv->last_visible_child) |
1405 | { |
1406 | gtk_widget_set_child_visible (widget: priv->last_visible_child->widget, FALSE); |
1407 | priv->last_visible_child = NULL; |
1408 | } |
1409 | |
1410 | gtk_accessible_update_state (self: GTK_ACCESSIBLE (ptr: child_info), |
1411 | first_state: GTK_ACCESSIBLE_STATE_HIDDEN, !visible, |
1412 | -1); |
1413 | } |
1414 | |
1415 | static void |
1416 | stack_child_visibility_notify_cb (GObject *obj, |
1417 | GParamSpec *pspec, |
1418 | gpointer user_data) |
1419 | { |
1420 | GtkStack *stack = GTK_STACK (user_data); |
1421 | GtkStackPage *child_info; |
1422 | |
1423 | child_info = find_child_info_for_widget (stack, GTK_WIDGET (obj)); |
1424 | g_return_if_fail (child_info != NULL); |
1425 | |
1426 | update_child_visible (stack, child_info); |
1427 | } |
1428 | |
1429 | /** |
1430 | * gtk_stack_add_titled: |
1431 | * @stack: a `GtkStack` |
1432 | * @child: the widget to add |
1433 | * @name: (nullable): the name for @child |
1434 | * @title: a human-readable title for @child |
1435 | * |
1436 | * Adds a child to @stack. |
1437 | * |
1438 | * The child is identified by the @name. The @title |
1439 | * will be used by `GtkStackSwitcher` to represent |
1440 | * @child in a tab bar, so it should be short. |
1441 | * |
1442 | * Returns: (transfer none): the `GtkStackPage` for @child |
1443 | */ |
1444 | GtkStackPage * |
1445 | gtk_stack_add_titled (GtkStack *stack, |
1446 | GtkWidget *child, |
1447 | const char *name, |
1448 | const char *title) |
1449 | { |
1450 | g_return_val_if_fail (GTK_IS_STACK (stack), NULL); |
1451 | g_return_val_if_fail (GTK_IS_WIDGET (child), NULL); |
1452 | |
1453 | return gtk_stack_add_internal (stack, child, name, title); |
1454 | } |
1455 | |
1456 | /** |
1457 | * gtk_stack_add_child: |
1458 | * @stack: a `GtkStack` |
1459 | * @child: the widget to add |
1460 | * |
1461 | * Adds a child to @stack. |
1462 | * |
1463 | * Returns: (transfer none): the `GtkStackPage` for @child |
1464 | */ |
1465 | GtkStackPage * |
1466 | gtk_stack_add_child (GtkStack *stack, |
1467 | GtkWidget *child) |
1468 | { |
1469 | g_return_val_if_fail (GTK_IS_STACK (stack), NULL); |
1470 | g_return_val_if_fail (GTK_IS_WIDGET (child), NULL); |
1471 | |
1472 | return gtk_stack_add_internal (stack, child, NULL, NULL); |
1473 | } |
1474 | |
1475 | /** |
1476 | * gtk_stack_add_named: |
1477 | * @stack: a `GtkStack` |
1478 | * @child: the widget to add |
1479 | * @name: (nullable): the name for @child |
1480 | * |
1481 | * Adds a child to @stack. |
1482 | * |
1483 | * The child is identified by the @name. |
1484 | * |
1485 | * Returns: (transfer none): the `GtkStackPage` for @child |
1486 | */ |
1487 | GtkStackPage * |
1488 | gtk_stack_add_named (GtkStack *stack, |
1489 | GtkWidget *child, |
1490 | const char *name) |
1491 | { |
1492 | g_return_val_if_fail (GTK_IS_STACK (stack), NULL); |
1493 | g_return_val_if_fail (GTK_IS_WIDGET (child), NULL); |
1494 | |
1495 | return gtk_stack_add_internal (stack, child, name, NULL); |
1496 | } |
1497 | |
1498 | static GtkStackPage * |
1499 | gtk_stack_add_internal (GtkStack *stack, |
1500 | GtkWidget *child, |
1501 | const char *name, |
1502 | const char *title) |
1503 | { |
1504 | GtkStackPage *child_info; |
1505 | |
1506 | g_return_val_if_fail (child != NULL, NULL); |
1507 | |
1508 | child_info = g_object_new (GTK_TYPE_STACK_PAGE, NULL); |
1509 | child_info->widget = g_object_ref (child); |
1510 | child_info->name = g_strdup (str: name); |
1511 | child_info->title = g_strdup (str: title); |
1512 | child_info->icon_name = NULL; |
1513 | child_info->needs_attention = FALSE; |
1514 | child_info->last_focus = NULL; |
1515 | |
1516 | gtk_stack_add_page (stack, page: child_info); |
1517 | |
1518 | g_object_unref (object: child_info); |
1519 | |
1520 | return child_info; |
1521 | } |
1522 | |
1523 | static void |
1524 | gtk_stack_add_page (GtkStack *stack, |
1525 | GtkStackPage *child_info) |
1526 | { |
1527 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1528 | GList *l; |
1529 | |
1530 | g_return_if_fail (child_info->widget != NULL); |
1531 | |
1532 | if (child_info->name) |
1533 | { |
1534 | for (l = priv->children; l != NULL; l = l->next) |
1535 | { |
1536 | GtkStackPage *info = l->data; |
1537 | if (info->name && |
1538 | g_strcmp0 (str1: info->name, str2: child_info->name) == 0) |
1539 | { |
1540 | g_warning ("While adding page: duplicate child name in GtkStack: %s" , child_info->name); |
1541 | break; |
1542 | } |
1543 | } |
1544 | } |
1545 | |
1546 | priv->children = g_list_append (list: priv->children, g_object_ref (child_info)); |
1547 | |
1548 | gtk_widget_set_child_visible (widget: child_info->widget, FALSE); |
1549 | gtk_widget_set_parent (widget: child_info->widget, GTK_WIDGET (stack)); |
1550 | |
1551 | if (priv->pages) |
1552 | g_list_model_items_changed (list: G_LIST_MODEL (ptr: priv->pages), position: g_list_length (list: priv->children) - 1, removed: 0, added: 1); |
1553 | |
1554 | g_signal_connect (child_info->widget, "notify::visible" , |
1555 | G_CALLBACK (stack_child_visibility_notify_cb), stack); |
1556 | |
1557 | if (priv->visible_child == NULL && |
1558 | gtk_widget_get_visible (widget: child_info->widget)) |
1559 | set_visible_child (stack, child_info, transition_type: priv->transition_type, transition_duration: priv->transition_duration); |
1560 | |
1561 | if (priv->homogeneous[GTK_ORIENTATION_HORIZONTAL] || priv->homogeneous[GTK_ORIENTATION_VERTICAL] || priv->visible_child == child_info) |
1562 | gtk_widget_queue_resize (GTK_WIDGET (stack)); |
1563 | } |
1564 | |
1565 | static void |
1566 | stack_remove (GtkStack *stack, |
1567 | GtkWidget *child, |
1568 | gboolean in_dispose) |
1569 | { |
1570 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1571 | GtkStackPage *child_info; |
1572 | gboolean was_visible; |
1573 | |
1574 | child_info = find_child_info_for_widget (stack, child); |
1575 | if (child_info == NULL) |
1576 | return; |
1577 | |
1578 | g_signal_handlers_disconnect_by_func (child, |
1579 | stack_child_visibility_notify_cb, |
1580 | stack); |
1581 | |
1582 | was_visible = gtk_widget_get_visible (widget: child); |
1583 | |
1584 | if (priv->visible_child == child_info) |
1585 | priv->visible_child = NULL; |
1586 | |
1587 | if (priv->last_visible_child == child_info) |
1588 | priv->last_visible_child = NULL; |
1589 | |
1590 | gtk_widget_unparent (widget: child); |
1591 | |
1592 | g_clear_object (&child_info->widget); |
1593 | |
1594 | priv->children = g_list_remove (list: priv->children, data: child_info); |
1595 | |
1596 | g_object_unref (object: child_info); |
1597 | |
1598 | if (!in_dispose && |
1599 | (priv->homogeneous[GTK_ORIENTATION_HORIZONTAL] || priv->homogeneous[GTK_ORIENTATION_VERTICAL]) && |
1600 | was_visible) |
1601 | gtk_widget_queue_resize (GTK_WIDGET (stack)); |
1602 | } |
1603 | |
1604 | /** |
1605 | * gtk_stack_remove: |
1606 | * @stack: a `GtkStack` |
1607 | * @child: the child to remove |
1608 | * |
1609 | * Removes a child widget from @stack. |
1610 | */ |
1611 | void |
1612 | gtk_stack_remove (GtkStack *stack, |
1613 | GtkWidget *child) |
1614 | { |
1615 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1616 | GList *l; |
1617 | guint position; |
1618 | |
1619 | g_return_if_fail (GTK_IS_STACK (stack)); |
1620 | g_return_if_fail (GTK_IS_WIDGET (child)); |
1621 | g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (stack)); |
1622 | |
1623 | for (l = priv->children, position = 0; l; l = l->next, position++) |
1624 | { |
1625 | GtkStackPage *page = l->data; |
1626 | if (page->widget == child) |
1627 | break; |
1628 | } |
1629 | |
1630 | stack_remove (stack, child, FALSE); |
1631 | |
1632 | if (priv->pages) |
1633 | g_list_model_items_changed (list: G_LIST_MODEL (ptr: priv->pages), position, removed: 1, added: 0); |
1634 | } |
1635 | |
1636 | /** |
1637 | * gtk_stack_get_page: |
1638 | * @stack: a `GtkStack` |
1639 | * @child: a child of @stack |
1640 | * |
1641 | * Retrieves the stack page for the given @child. |
1642 | * |
1643 | * If the given @child is not a child widget of the stack, this function will return `NULL`. |
1644 | * |
1645 | * Returns: (transfer none) (nullable): the stack page object |
1646 | */ |
1647 | GtkStackPage * |
1648 | gtk_stack_get_page (GtkStack *stack, |
1649 | GtkWidget *child) |
1650 | { |
1651 | return find_child_info_for_widget (stack, child); |
1652 | } |
1653 | |
1654 | /** |
1655 | * gtk_stack_get_child_by_name: |
1656 | * @stack: a `GtkStack` |
1657 | * @name: the name of the child to find |
1658 | * |
1659 | * Finds the child with the name given as the argument. |
1660 | * |
1661 | * Returns %NULL if there is no child with this name. |
1662 | * |
1663 | * Returns: (transfer none) (nullable): the requested child |
1664 | * of the `GtkStack` |
1665 | */ |
1666 | GtkWidget * |
1667 | gtk_stack_get_child_by_name (GtkStack *stack, |
1668 | const char *name) |
1669 | { |
1670 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1671 | GtkStackPage *info; |
1672 | GList *l; |
1673 | |
1674 | g_return_val_if_fail (GTK_IS_STACK (stack), NULL); |
1675 | g_return_val_if_fail (name != NULL, NULL); |
1676 | |
1677 | for (l = priv->children; l != NULL; l = l->next) |
1678 | { |
1679 | info = l->data; |
1680 | if (info->name && strcmp (s1: info->name, s2: name) == 0) |
1681 | return info->widget; |
1682 | } |
1683 | |
1684 | return NULL; |
1685 | } |
1686 | |
1687 | /** |
1688 | * gtk_stack_page_get_child: (attributes org.gtk.Method.get_property=child) |
1689 | * @self: a `GtkStackPage` |
1690 | * |
1691 | * Returns the stack child to which @self belongs. |
1692 | * |
1693 | * Returns: (transfer none): the child to which @self belongs |
1694 | */ |
1695 | GtkWidget * |
1696 | gtk_stack_page_get_child (GtkStackPage *self) |
1697 | { |
1698 | return self->widget; |
1699 | } |
1700 | |
1701 | /** |
1702 | * gtk_stack_set_hhomogeneous: (attributes org.gtk.Method.set_property=hhomogeneous) |
1703 | * @stack: a `GtkStack` |
1704 | * @hhomogeneous: %TRUE to make @stack horizontally homogeneous |
1705 | * |
1706 | * Sets the `GtkStack` to be horizontally homogeneous or not. |
1707 | * |
1708 | * If it is homogeneous, the `GtkStack` will request the same |
1709 | * width for all its children. If it isn't, the stack |
1710 | * may change width when a different child becomes visible. |
1711 | */ |
1712 | void |
1713 | gtk_stack_set_hhomogeneous (GtkStack *stack, |
1714 | gboolean hhomogeneous) |
1715 | { |
1716 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1717 | |
1718 | g_return_if_fail (GTK_IS_STACK (stack)); |
1719 | |
1720 | hhomogeneous = !!hhomogeneous; |
1721 | |
1722 | if (priv->homogeneous[GTK_ORIENTATION_HORIZONTAL] == hhomogeneous) |
1723 | return; |
1724 | |
1725 | priv->homogeneous[GTK_ORIENTATION_HORIZONTAL] = hhomogeneous; |
1726 | |
1727 | if (gtk_widget_get_visible (GTK_WIDGET(stack))) |
1728 | gtk_widget_queue_resize (GTK_WIDGET (stack)); |
1729 | |
1730 | g_object_notify_by_pspec (G_OBJECT (stack), pspec: stack_props[PROP_HHOMOGENEOUS]); |
1731 | } |
1732 | |
1733 | /** |
1734 | * gtk_stack_get_hhomogeneous: (attributes org.gtk.Method.get_property=hhomogeneous) |
1735 | * @stack: a `GtkStack` |
1736 | * |
1737 | * Gets whether @stack is horizontally homogeneous. |
1738 | * |
1739 | * Returns: whether @stack is horizontally homogeneous. |
1740 | */ |
1741 | gboolean |
1742 | gtk_stack_get_hhomogeneous (GtkStack *stack) |
1743 | { |
1744 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1745 | |
1746 | g_return_val_if_fail (GTK_IS_STACK (stack), FALSE); |
1747 | |
1748 | return priv->homogeneous[GTK_ORIENTATION_HORIZONTAL]; |
1749 | } |
1750 | |
1751 | /** |
1752 | * gtk_stack_set_vhomogeneous: (attributes org.gtk.Method.set_property=vhomogeneous) |
1753 | * @stack: a `GtkStack` |
1754 | * @vhomogeneous: %TRUE to make @stack vertically homogeneous |
1755 | * |
1756 | * Sets the `GtkStack` to be vertically homogeneous or not. |
1757 | * |
1758 | * If it is homogeneous, the `GtkStack` will request the same |
1759 | * height for all its children. If it isn't, the stack |
1760 | * may change height when a different child becomes visible. |
1761 | */ |
1762 | void |
1763 | gtk_stack_set_vhomogeneous (GtkStack *stack, |
1764 | gboolean vhomogeneous) |
1765 | { |
1766 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1767 | |
1768 | g_return_if_fail (GTK_IS_STACK (stack)); |
1769 | |
1770 | vhomogeneous = !!vhomogeneous; |
1771 | |
1772 | if (priv->homogeneous[GTK_ORIENTATION_VERTICAL] == vhomogeneous) |
1773 | return; |
1774 | |
1775 | priv->homogeneous[GTK_ORIENTATION_VERTICAL] = vhomogeneous; |
1776 | |
1777 | if (gtk_widget_get_visible (GTK_WIDGET(stack))) |
1778 | gtk_widget_queue_resize (GTK_WIDGET (stack)); |
1779 | |
1780 | g_object_notify_by_pspec (G_OBJECT (stack), pspec: stack_props[PROP_VHOMOGENEOUS]); |
1781 | } |
1782 | |
1783 | /** |
1784 | * gtk_stack_get_vhomogeneous: (attributes org.gtk.Method.get_property=vhomogeneous) |
1785 | * @stack: a `GtkStack` |
1786 | * |
1787 | * Gets whether @stack is vertically homogeneous. |
1788 | * |
1789 | * Returns: whether @stack is vertically homogeneous. |
1790 | */ |
1791 | gboolean |
1792 | gtk_stack_get_vhomogeneous (GtkStack *stack) |
1793 | { |
1794 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1795 | |
1796 | g_return_val_if_fail (GTK_IS_STACK (stack), FALSE); |
1797 | |
1798 | return priv->homogeneous[GTK_ORIENTATION_VERTICAL]; |
1799 | } |
1800 | |
1801 | /** |
1802 | * gtk_stack_get_transition_duration: (attributes org.gtk.Method.get_property=transition-duration) |
1803 | * @stack: a `GtkStack` |
1804 | * |
1805 | * Returns the amount of time (in milliseconds) that |
1806 | * transitions between pages in @stack will take. |
1807 | * |
1808 | * Returns: the transition duration |
1809 | */ |
1810 | guint |
1811 | gtk_stack_get_transition_duration (GtkStack *stack) |
1812 | { |
1813 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1814 | |
1815 | g_return_val_if_fail (GTK_IS_STACK (stack), 0); |
1816 | |
1817 | return priv->transition_duration; |
1818 | } |
1819 | |
1820 | /** |
1821 | * gtk_stack_set_transition_duration: (attributes org.gtk.Method.set_property=transition-duration) |
1822 | * @stack: a `GtkStack` |
1823 | * @duration: the new duration, in milliseconds |
1824 | * |
1825 | * Sets the duration that transitions between pages in @stack |
1826 | * will take. |
1827 | */ |
1828 | void |
1829 | gtk_stack_set_transition_duration (GtkStack *stack, |
1830 | guint duration) |
1831 | { |
1832 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1833 | |
1834 | g_return_if_fail (GTK_IS_STACK (stack)); |
1835 | |
1836 | if (priv->transition_duration == duration) |
1837 | return; |
1838 | |
1839 | priv->transition_duration = duration; |
1840 | g_object_notify_by_pspec (G_OBJECT (stack), |
1841 | pspec: stack_props[PROP_TRANSITION_DURATION]); |
1842 | } |
1843 | |
1844 | /** |
1845 | * gtk_stack_get_transition_type: (attributes org.gtk.Method.get_property=transition-type) |
1846 | * @stack: a `GtkStack` |
1847 | * |
1848 | * Gets the type of animation that will be used |
1849 | * for transitions between pages in @stack. |
1850 | * |
1851 | * Returns: the current transition type of @stack |
1852 | */ |
1853 | GtkStackTransitionType |
1854 | gtk_stack_get_transition_type (GtkStack *stack) |
1855 | { |
1856 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1857 | |
1858 | g_return_val_if_fail (GTK_IS_STACK (stack), GTK_STACK_TRANSITION_TYPE_NONE); |
1859 | |
1860 | return priv->transition_type; |
1861 | } |
1862 | |
1863 | /** |
1864 | * gtk_stack_set_transition_type: (attributes org.gtk.Method.set_property=transition-type) |
1865 | * @stack: a `GtkStack` |
1866 | * @transition: the new transition type |
1867 | * |
1868 | * Sets the type of animation that will be used for |
1869 | * transitions between pages in @stack. |
1870 | * |
1871 | * Available types include various kinds of fades and slides. |
1872 | * |
1873 | * The transition type can be changed without problems |
1874 | * at runtime, so it is possible to change the animation |
1875 | * based on the page that is about to become current. |
1876 | */ |
1877 | void |
1878 | gtk_stack_set_transition_type (GtkStack *stack, |
1879 | GtkStackTransitionType transition) |
1880 | { |
1881 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1882 | |
1883 | g_return_if_fail (GTK_IS_STACK (stack)); |
1884 | |
1885 | if (priv->transition_type == transition) |
1886 | return; |
1887 | |
1888 | priv->transition_type = transition; |
1889 | g_object_notify_by_pspec (G_OBJECT (stack), |
1890 | pspec: stack_props[PROP_TRANSITION_TYPE]); |
1891 | } |
1892 | |
1893 | /** |
1894 | * gtk_stack_get_transition_running: (attributes org.gtk.Method.get_property=transition-running) |
1895 | * @stack: a `GtkStack` |
1896 | * |
1897 | * Returns whether the @stack is currently in a transition from one page to |
1898 | * another. |
1899 | * |
1900 | * Returns: %TRUE if the transition is currently running, %FALSE otherwise. |
1901 | */ |
1902 | gboolean |
1903 | gtk_stack_get_transition_running (GtkStack *stack) |
1904 | { |
1905 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1906 | |
1907 | g_return_val_if_fail (GTK_IS_STACK (stack), FALSE); |
1908 | |
1909 | return (priv->tick_id != 0); |
1910 | } |
1911 | |
1912 | /** |
1913 | * gtk_stack_set_interpolate_size: (attributes org.gtk.Method.set_property=interpolate-size) |
1914 | * @stack: A `GtkStack` |
1915 | * @interpolate_size: the new value |
1916 | * |
1917 | * Sets whether or not @stack will interpolate its size when |
1918 | * changing the visible child. |
1919 | * |
1920 | * If the [property@Gtk.Stack:interpolate-size] property is set |
1921 | * to %TRUE, @stack will interpolate its size between the current |
1922 | * one and the one it'll take after changing the visible child, |
1923 | * according to the set transition duration. |
1924 | */ |
1925 | void |
1926 | gtk_stack_set_interpolate_size (GtkStack *stack, |
1927 | gboolean interpolate_size) |
1928 | { |
1929 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1930 | g_return_if_fail (GTK_IS_STACK (stack)); |
1931 | |
1932 | interpolate_size = !!interpolate_size; |
1933 | |
1934 | if (priv->interpolate_size == interpolate_size) |
1935 | return; |
1936 | |
1937 | priv->interpolate_size = interpolate_size; |
1938 | g_object_notify_by_pspec (G_OBJECT (stack), |
1939 | pspec: stack_props[PROP_INTERPOLATE_SIZE]); |
1940 | } |
1941 | |
1942 | /** |
1943 | * gtk_stack_get_interpolate_size: (attributes org.gtk.Method.get_property=interpolate-size) |
1944 | * @stack: A `GtkStack` |
1945 | * |
1946 | * Returns whether the `GtkStack` is set up to interpolate between |
1947 | * the sizes of children on page switch. |
1948 | * |
1949 | * Returns: %TRUE if child sizes are interpolated |
1950 | */ |
1951 | gboolean |
1952 | gtk_stack_get_interpolate_size (GtkStack *stack) |
1953 | { |
1954 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1955 | g_return_val_if_fail (GTK_IS_STACK (stack), FALSE); |
1956 | |
1957 | return priv->interpolate_size; |
1958 | } |
1959 | |
1960 | |
1961 | |
1962 | /** |
1963 | * gtk_stack_get_visible_child: (attributes org.gtk.Method.get_property=visible-child) |
1964 | * @stack: a `GtkStack` |
1965 | * |
1966 | * Gets the currently visible child of @stack. |
1967 | * |
1968 | * Returns %NULL if there are no visible children. |
1969 | * |
1970 | * Returns: (transfer none) (nullable): the visible child of the `GtkStack` |
1971 | */ |
1972 | GtkWidget * |
1973 | gtk_stack_get_visible_child (GtkStack *stack) |
1974 | { |
1975 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1976 | |
1977 | g_return_val_if_fail (GTK_IS_STACK (stack), NULL); |
1978 | |
1979 | return priv->visible_child ? priv->visible_child->widget : NULL; |
1980 | } |
1981 | |
1982 | /** |
1983 | * gtk_stack_get_visible_child_name: (attributes org.gtk.Method.get_property=visible-child-name) |
1984 | * @stack: a `GtkStack` |
1985 | * |
1986 | * Returns the name of the currently visible child of @stack. |
1987 | * |
1988 | * Returns %NULL if there is no visible child. |
1989 | * |
1990 | * Returns: (transfer none) (nullable): the name of the visible child |
1991 | * of the `GtkStack` |
1992 | */ |
1993 | const char * |
1994 | gtk_stack_get_visible_child_name (GtkStack *stack) |
1995 | { |
1996 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
1997 | |
1998 | g_return_val_if_fail (GTK_IS_STACK (stack), NULL); |
1999 | |
2000 | if (priv->visible_child) |
2001 | return priv->visible_child->name; |
2002 | |
2003 | return NULL; |
2004 | } |
2005 | |
2006 | /** |
2007 | * gtk_stack_set_visible_child: (attributes org.gtk.Method.set_property=visible-child) |
2008 | * @stack: a `GtkStack` |
2009 | * @child: a child of @stack |
2010 | * |
2011 | * Makes @child the visible child of @stack. |
2012 | * |
2013 | * If @child is different from the currently visible child, |
2014 | * the transition between the two will be animated with the |
2015 | * current transition type of @stack. |
2016 | * |
2017 | * Note that the @child widget has to be visible itself |
2018 | * (see [method@Gtk.Widget.show]) in order to become the visible |
2019 | * child of @stack. |
2020 | */ |
2021 | void |
2022 | gtk_stack_set_visible_child (GtkStack *stack, |
2023 | GtkWidget *child) |
2024 | { |
2025 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2026 | GtkStackPage *child_info; |
2027 | |
2028 | g_return_if_fail (GTK_IS_STACK (stack)); |
2029 | g_return_if_fail (GTK_IS_WIDGET (child)); |
2030 | |
2031 | child_info = find_child_info_for_widget (stack, child); |
2032 | if (child_info == NULL) |
2033 | { |
2034 | g_warning ("Given child of type '%s' not found in GtkStack" , |
2035 | G_OBJECT_TYPE_NAME (child)); |
2036 | return; |
2037 | } |
2038 | |
2039 | if (gtk_widget_get_visible (widget: child_info->widget)) |
2040 | set_visible_child (stack, child_info, |
2041 | transition_type: priv->transition_type, |
2042 | transition_duration: priv->transition_duration); |
2043 | } |
2044 | |
2045 | /** |
2046 | * gtk_stack_set_visible_child_name: (attributes org.gtk.Method.set_property=visible-child-name) |
2047 | * @stack: a `GtkStack` |
2048 | * @name: the name of the child to make visible |
2049 | * |
2050 | * Makes the child with the given name visible. |
2051 | * |
2052 | * If @child is different from the currently visible child, |
2053 | * the transition between the two will be animated with the |
2054 | * current transition type of @stack. |
2055 | * |
2056 | * Note that the child widget has to be visible itself |
2057 | * (see [method@Gtk.Widget.show]) in order to become the visible |
2058 | * child of @stack. |
2059 | */ |
2060 | void |
2061 | gtk_stack_set_visible_child_name (GtkStack *stack, |
2062 | const char *name) |
2063 | { |
2064 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2065 | |
2066 | g_return_if_fail (GTK_IS_STACK (stack)); |
2067 | |
2068 | gtk_stack_set_visible_child_full (stack, name, transition: priv->transition_type); |
2069 | } |
2070 | |
2071 | /** |
2072 | * gtk_stack_set_visible_child_full: |
2073 | * @stack: a `GtkStack` |
2074 | * @name: the name of the child to make visible |
2075 | * @transition: the transition type to use |
2076 | * |
2077 | * Makes the child with the given name visible. |
2078 | * |
2079 | * Note that the child widget has to be visible itself |
2080 | * (see [method@Gtk.Widget.show]) in order to become the visible |
2081 | * child of @stack. |
2082 | */ |
2083 | void |
2084 | gtk_stack_set_visible_child_full (GtkStack *stack, |
2085 | const char *name, |
2086 | GtkStackTransitionType transition) |
2087 | { |
2088 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2089 | GtkStackPage *child_info, *info; |
2090 | GList *l; |
2091 | |
2092 | g_return_if_fail (GTK_IS_STACK (stack)); |
2093 | |
2094 | if (name == NULL) |
2095 | return; |
2096 | |
2097 | child_info = NULL; |
2098 | for (l = priv->children; l != NULL; l = l->next) |
2099 | { |
2100 | info = l->data; |
2101 | if (info->name != NULL && |
2102 | strcmp (s1: info->name, s2: name) == 0) |
2103 | { |
2104 | child_info = info; |
2105 | break; |
2106 | } |
2107 | } |
2108 | |
2109 | if (child_info == NULL) |
2110 | { |
2111 | g_warning ("Child name '%s' not found in GtkStack" , name); |
2112 | return; |
2113 | } |
2114 | |
2115 | if (gtk_widget_get_visible (widget: child_info->widget)) |
2116 | set_visible_child (stack, child_info, transition_type: transition, transition_duration: priv->transition_duration); |
2117 | } |
2118 | |
2119 | static void |
2120 | gtk_stack_compute_expand (GtkWidget *widget, |
2121 | gboolean *hexpand_p, |
2122 | gboolean *vexpand_p) |
2123 | { |
2124 | GtkStack *stack = GTK_STACK (widget); |
2125 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2126 | gboolean hexpand, vexpand; |
2127 | GtkStackPage *child_info; |
2128 | GtkWidget *child; |
2129 | GList *l; |
2130 | |
2131 | hexpand = FALSE; |
2132 | vexpand = FALSE; |
2133 | for (l = priv->children; l != NULL; l = l->next) |
2134 | { |
2135 | child_info = l->data; |
2136 | child = child_info->widget; |
2137 | |
2138 | if (!hexpand && |
2139 | gtk_widget_compute_expand (widget: child, orientation: GTK_ORIENTATION_HORIZONTAL)) |
2140 | hexpand = TRUE; |
2141 | |
2142 | if (!vexpand && |
2143 | gtk_widget_compute_expand (widget: child, orientation: GTK_ORIENTATION_VERTICAL)) |
2144 | vexpand = TRUE; |
2145 | |
2146 | if (hexpand && vexpand) |
2147 | break; |
2148 | } |
2149 | |
2150 | *hexpand_p = hexpand; |
2151 | *vexpand_p = vexpand; |
2152 | } |
2153 | |
2154 | static GtkSizeRequestMode |
2155 | gtk_stack_get_request_mode (GtkWidget *widget) |
2156 | { |
2157 | GtkWidget *w; |
2158 | int wfh = 0, hfw = 0; |
2159 | |
2160 | for (w = gtk_widget_get_first_child (widget); |
2161 | w != NULL; |
2162 | w = gtk_widget_get_next_sibling (widget: w)) |
2163 | { |
2164 | GtkSizeRequestMode mode = gtk_widget_get_request_mode (widget: w); |
2165 | |
2166 | switch (mode) |
2167 | { |
2168 | case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH: |
2169 | hfw ++; |
2170 | break; |
2171 | case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT: |
2172 | wfh ++; |
2173 | break; |
2174 | case GTK_SIZE_REQUEST_CONSTANT_SIZE: |
2175 | default: |
2176 | break; |
2177 | } |
2178 | } |
2179 | |
2180 | if (hfw == 0 && wfh == 0) |
2181 | return GTK_SIZE_REQUEST_CONSTANT_SIZE; |
2182 | else |
2183 | return wfh > hfw ? |
2184 | GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT : |
2185 | GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; |
2186 | } |
2187 | |
2188 | static void |
2189 | gtk_stack_snapshot_crossfade (GtkWidget *widget, |
2190 | GtkSnapshot *snapshot) |
2191 | { |
2192 | GtkStack *stack = GTK_STACK (widget); |
2193 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2194 | double progress = gtk_progress_tracker_get_progress (tracker: &priv->tracker, FALSE); |
2195 | |
2196 | gtk_snapshot_push_cross_fade (snapshot, progress); |
2197 | |
2198 | if (priv->last_visible_child) |
2199 | { |
2200 | gtk_widget_snapshot_child (widget, |
2201 | child: priv->last_visible_child->widget, |
2202 | snapshot); |
2203 | } |
2204 | gtk_snapshot_pop (snapshot); |
2205 | |
2206 | gtk_widget_snapshot_child (widget, |
2207 | child: priv->visible_child->widget, |
2208 | snapshot); |
2209 | gtk_snapshot_pop (snapshot); |
2210 | } |
2211 | |
2212 | static void |
2213 | gtk_stack_snapshot_under (GtkWidget *widget, |
2214 | GtkSnapshot *snapshot) |
2215 | { |
2216 | GtkStack *stack = GTK_STACK (widget); |
2217 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2218 | int widget_width, widget_height; |
2219 | int x, y, width, height, pos_x, pos_y; |
2220 | |
2221 | |
2222 | x = y = 0; |
2223 | width = widget_width = gtk_widget_get_width (widget); |
2224 | height = widget_height = gtk_widget_get_height (widget); |
2225 | |
2226 | pos_x = pos_y = 0; |
2227 | |
2228 | switch ((guint) priv->active_transition_type) |
2229 | { |
2230 | case GTK_STACK_TRANSITION_TYPE_UNDER_DOWN: |
2231 | y = 0; |
2232 | height = widget_height * (gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE)); |
2233 | pos_y = height; |
2234 | break; |
2235 | case GTK_STACK_TRANSITION_TYPE_UNDER_UP: |
2236 | y = widget_height * (1 - gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE)); |
2237 | height = widget_height - y; |
2238 | pos_y = y - widget_height; |
2239 | break; |
2240 | case GTK_STACK_TRANSITION_TYPE_UNDER_LEFT: |
2241 | x = widget_width * (1 - gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE)); |
2242 | width = widget_width - x; |
2243 | pos_x = x - widget_width; |
2244 | break; |
2245 | case GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT: |
2246 | x = 0; |
2247 | width = widget_width * (gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE)); |
2248 | pos_x = width; |
2249 | break; |
2250 | default: |
2251 | g_assert_not_reached (); |
2252 | } |
2253 | |
2254 | gtk_snapshot_push_clip (snapshot, bounds: &GRAPHENE_RECT_INIT(x, y, width, height)); |
2255 | |
2256 | gtk_widget_snapshot_child (widget, |
2257 | child: priv->visible_child->widget, |
2258 | snapshot); |
2259 | |
2260 | gtk_snapshot_pop (snapshot); |
2261 | |
2262 | if (priv->last_visible_child) |
2263 | { |
2264 | gtk_snapshot_save (snapshot); |
2265 | gtk_snapshot_translate (snapshot, point: &GRAPHENE_POINT_INIT (pos_x, pos_y)); |
2266 | gtk_widget_snapshot_child (widget, child: priv->last_visible_child->widget, snapshot); |
2267 | gtk_snapshot_restore (snapshot); |
2268 | } |
2269 | } |
2270 | |
2271 | static void |
2272 | gtk_stack_snapshot_cube (GtkWidget *widget, |
2273 | GtkSnapshot *snapshot) |
2274 | { |
2275 | GtkStack *stack = GTK_STACK (widget); |
2276 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2277 | double progress = gtk_progress_tracker_get_progress (tracker: &priv->tracker, FALSE); |
2278 | |
2279 | g_assert (priv->active_transition_type == GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT || |
2280 | priv->active_transition_type == GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT); |
2281 | |
2282 | if (priv->active_transition_type == GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT) |
2283 | progress = 1 - progress; |
2284 | |
2285 | if (priv->last_visible_child && progress > 0.5) |
2286 | { |
2287 | gtk_snapshot_save (snapshot); |
2288 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2289 | gtk_widget_get_width (widget) / 2.f, |
2290 | gtk_widget_get_height (widget) / 2.f, |
2291 | 0)); |
2292 | gtk_snapshot_perspective (snapshot, depth: 2 * gtk_widget_get_width (widget) / 1.f); |
2293 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2294 | 0, 0, |
2295 | - gtk_widget_get_width (widget) / 2.f)); |
2296 | gtk_snapshot_rotate_3d (snapshot, angle: -90 * progress, axis: graphene_vec3_y_axis()); |
2297 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2298 | - gtk_widget_get_width (widget) / 2.f, |
2299 | - gtk_widget_get_height (widget) / 2.f, |
2300 | gtk_widget_get_width (widget) / 2.f)); |
2301 | if (priv->active_transition_type == GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT) |
2302 | gtk_widget_snapshot_child (widget, child: priv->last_visible_child->widget, snapshot); |
2303 | else |
2304 | gtk_widget_snapshot_child (widget, child: priv->visible_child->widget, snapshot); |
2305 | gtk_snapshot_restore (snapshot); |
2306 | } |
2307 | |
2308 | gtk_snapshot_save (snapshot); |
2309 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2310 | gtk_widget_get_width (widget) / 2.f, |
2311 | gtk_widget_get_height (widget) / 2.f, |
2312 | 0)); |
2313 | gtk_snapshot_perspective (snapshot, depth: 2 * gtk_widget_get_width (widget) / 1.f); |
2314 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2315 | 0, 0, |
2316 | - gtk_widget_get_width (widget) / 2.f)); |
2317 | gtk_snapshot_rotate_3d (snapshot, angle: 90 * (1.0 - progress), axis: graphene_vec3_y_axis()); |
2318 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2319 | - gtk_widget_get_width (widget) / 2.f, |
2320 | - gtk_widget_get_height (widget) / 2.f, |
2321 | gtk_widget_get_width (widget) / 2.f)); |
2322 | |
2323 | if (priv->active_transition_type == GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT) |
2324 | gtk_widget_snapshot_child (widget, child: priv->visible_child->widget, snapshot); |
2325 | else if (priv->last_visible_child) |
2326 | gtk_widget_snapshot_child (widget, child: priv->last_visible_child->widget, snapshot); |
2327 | gtk_snapshot_restore (snapshot); |
2328 | |
2329 | if (priv->last_visible_child && progress <= 0.5) |
2330 | { |
2331 | gtk_snapshot_save (snapshot); |
2332 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2333 | gtk_widget_get_width (widget) / 2.f, |
2334 | gtk_widget_get_height (widget) / 2.f, |
2335 | 0)); |
2336 | gtk_snapshot_perspective (snapshot, depth: 2 * gtk_widget_get_width (widget) / 1.f); |
2337 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2338 | 0, 0, |
2339 | - gtk_widget_get_width (widget) / 2.f)); |
2340 | gtk_snapshot_rotate_3d (snapshot, angle: -90 * progress, axis: graphene_vec3_y_axis()); |
2341 | gtk_snapshot_translate_3d (snapshot, point: &GRAPHENE_POINT3D_INIT ( |
2342 | - gtk_widget_get_width (widget) / 2.f, |
2343 | - gtk_widget_get_height (widget) / 2.f, |
2344 | gtk_widget_get_width (widget) / 2.f)); |
2345 | if (priv->active_transition_type == GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT) |
2346 | gtk_widget_snapshot_child (widget, child: priv->last_visible_child->widget, snapshot); |
2347 | else |
2348 | gtk_widget_snapshot_child (widget, child: priv->visible_child->widget, snapshot); |
2349 | gtk_snapshot_restore (snapshot); |
2350 | } |
2351 | } |
2352 | |
2353 | static void |
2354 | gtk_stack_snapshot_slide (GtkWidget *widget, |
2355 | GtkSnapshot *snapshot) |
2356 | { |
2357 | GtkStack *stack = GTK_STACK (widget); |
2358 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2359 | |
2360 | if (priv->last_visible_child) |
2361 | { |
2362 | int x, y; |
2363 | int width, height; |
2364 | |
2365 | width = gtk_widget_get_width (widget); |
2366 | height = gtk_widget_get_height (widget); |
2367 | |
2368 | x = get_bin_window_x (stack); |
2369 | y = get_bin_window_y (stack); |
2370 | |
2371 | switch ((guint) priv->active_transition_type) |
2372 | { |
2373 | case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT: |
2374 | x -= width; |
2375 | break; |
2376 | case GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT: |
2377 | x += width; |
2378 | break; |
2379 | case GTK_STACK_TRANSITION_TYPE_SLIDE_UP: |
2380 | y -= height; |
2381 | break; |
2382 | case GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN: |
2383 | y += height; |
2384 | break; |
2385 | case GTK_STACK_TRANSITION_TYPE_OVER_UP: |
2386 | case GTK_STACK_TRANSITION_TYPE_OVER_DOWN: |
2387 | y = 0; |
2388 | break; |
2389 | case GTK_STACK_TRANSITION_TYPE_OVER_LEFT: |
2390 | case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT: |
2391 | x = 0; |
2392 | break; |
2393 | default: |
2394 | g_assert_not_reached (); |
2395 | break; |
2396 | } |
2397 | |
2398 | if (priv->last_visible_child != NULL) |
2399 | { |
2400 | if (gtk_widget_get_valign (widget: priv->last_visible_child->widget) == GTK_ALIGN_END && |
2401 | priv->last_visible_widget_height > height) |
2402 | y -= priv->last_visible_widget_height - height; |
2403 | else if (gtk_widget_get_valign (widget: priv->last_visible_child->widget) == GTK_ALIGN_CENTER) |
2404 | y -= (priv->last_visible_widget_height - height) / 2; |
2405 | } |
2406 | |
2407 | gtk_snapshot_save (snapshot); |
2408 | gtk_snapshot_translate (snapshot, point: &GRAPHENE_POINT_INIT (x, y)); |
2409 | gtk_widget_snapshot_child (widget, child: priv->last_visible_child->widget, snapshot); |
2410 | gtk_snapshot_restore (snapshot); |
2411 | } |
2412 | |
2413 | gtk_widget_snapshot_child (widget, |
2414 | child: priv->visible_child->widget, |
2415 | snapshot); |
2416 | } |
2417 | |
2418 | static void |
2419 | gtk_stack_snapshot (GtkWidget *widget, |
2420 | GtkSnapshot *snapshot) |
2421 | { |
2422 | GtkStack *stack = GTK_STACK (widget); |
2423 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2424 | |
2425 | if (priv->visible_child) |
2426 | { |
2427 | if (gtk_progress_tracker_get_state (tracker: &priv->tracker) != GTK_PROGRESS_STATE_AFTER) |
2428 | { |
2429 | gtk_snapshot_push_clip (snapshot, |
2430 | bounds: &GRAPHENE_RECT_INIT( |
2431 | 0, 0, |
2432 | gtk_widget_get_width (widget), |
2433 | gtk_widget_get_height (widget) |
2434 | )); |
2435 | |
2436 | switch (priv->active_transition_type) |
2437 | { |
2438 | case GTK_STACK_TRANSITION_TYPE_CROSSFADE: |
2439 | gtk_stack_snapshot_crossfade (widget, snapshot); |
2440 | break; |
2441 | case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT: |
2442 | case GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT: |
2443 | case GTK_STACK_TRANSITION_TYPE_SLIDE_UP: |
2444 | case GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN: |
2445 | case GTK_STACK_TRANSITION_TYPE_OVER_UP: |
2446 | case GTK_STACK_TRANSITION_TYPE_OVER_DOWN: |
2447 | case GTK_STACK_TRANSITION_TYPE_OVER_LEFT: |
2448 | case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT: |
2449 | gtk_stack_snapshot_slide (widget, snapshot); |
2450 | break; |
2451 | case GTK_STACK_TRANSITION_TYPE_UNDER_UP: |
2452 | case GTK_STACK_TRANSITION_TYPE_UNDER_DOWN: |
2453 | case GTK_STACK_TRANSITION_TYPE_UNDER_LEFT: |
2454 | case GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT: |
2455 | gtk_stack_snapshot_under (widget, snapshot); |
2456 | break; |
2457 | case GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT: |
2458 | case GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT: |
2459 | gtk_stack_snapshot_cube (widget, snapshot); |
2460 | break; |
2461 | case GTK_STACK_TRANSITION_TYPE_NONE: |
2462 | case GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT: |
2463 | case GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN: |
2464 | case GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN: |
2465 | case GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP: |
2466 | case GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT: |
2467 | case GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT: |
2468 | case GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT: |
2469 | default: |
2470 | g_assert_not_reached (); |
2471 | } |
2472 | |
2473 | gtk_snapshot_pop (snapshot); |
2474 | } |
2475 | else |
2476 | gtk_widget_snapshot_child (widget, |
2477 | child: priv->visible_child->widget, |
2478 | snapshot); |
2479 | } |
2480 | } |
2481 | |
2482 | static void |
2483 | gtk_stack_size_allocate (GtkWidget *widget, |
2484 | int width, |
2485 | int height, |
2486 | int baseline) |
2487 | { |
2488 | GtkStack *stack = GTK_STACK (widget); |
2489 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2490 | GtkAllocation child_allocation; |
2491 | |
2492 | if (priv->last_visible_child) |
2493 | { |
2494 | int child_width, child_height; |
2495 | int min, nat; |
2496 | |
2497 | gtk_widget_measure (widget: priv->last_visible_child->widget, orientation: GTK_ORIENTATION_HORIZONTAL, |
2498 | for_size: -1, |
2499 | minimum: &min, natural: &nat, NULL, NULL); |
2500 | child_width = MAX (min, width); |
2501 | gtk_widget_measure (widget: priv->last_visible_child->widget, orientation: GTK_ORIENTATION_VERTICAL, |
2502 | for_size: child_width, |
2503 | minimum: &min, natural: &nat, NULL, NULL); |
2504 | child_height = MAX (min, height); |
2505 | |
2506 | gtk_widget_size_allocate (widget: priv->last_visible_child->widget, |
2507 | allocation: &(GtkAllocation) { 0, 0, child_width, child_height }, baseline: -1); |
2508 | } |
2509 | |
2510 | child_allocation.x = get_bin_window_x (stack); |
2511 | child_allocation.y = get_bin_window_y (stack); |
2512 | child_allocation.width = width; |
2513 | child_allocation.height = height; |
2514 | |
2515 | if (priv->visible_child) |
2516 | { |
2517 | int min_width; |
2518 | int min_height; |
2519 | |
2520 | gtk_widget_measure (widget: priv->visible_child->widget, orientation: GTK_ORIENTATION_HORIZONTAL, |
2521 | for_size: height, minimum: &min_width, NULL, NULL, NULL); |
2522 | child_allocation.width = MAX (child_allocation.width, min_width); |
2523 | |
2524 | gtk_widget_measure (widget: priv->visible_child->widget, orientation: GTK_ORIENTATION_VERTICAL, |
2525 | for_size: child_allocation.width, minimum: &min_height, NULL, NULL, NULL); |
2526 | child_allocation.height = MAX (child_allocation.height, min_height); |
2527 | |
2528 | if (child_allocation.width > width) |
2529 | { |
2530 | GtkAlign halign = gtk_widget_get_halign (widget: priv->visible_child->widget); |
2531 | |
2532 | if (halign == GTK_ALIGN_CENTER || halign == GTK_ALIGN_FILL) |
2533 | child_allocation.x = (width - child_allocation.width) / 2; |
2534 | else if (halign == GTK_ALIGN_END) |
2535 | child_allocation.x = (width - child_allocation.width); |
2536 | } |
2537 | |
2538 | if (child_allocation.height > height) |
2539 | { |
2540 | GtkAlign valign = gtk_widget_get_valign (widget: priv->visible_child->widget); |
2541 | |
2542 | if (valign == GTK_ALIGN_CENTER || valign == GTK_ALIGN_FILL) |
2543 | child_allocation.y = (height - child_allocation.height) / 2; |
2544 | else if (valign == GTK_ALIGN_END) |
2545 | child_allocation.y = (height - child_allocation.height); |
2546 | } |
2547 | |
2548 | gtk_widget_size_allocate (widget: priv->visible_child->widget, allocation: &child_allocation, baseline: -1); |
2549 | } |
2550 | } |
2551 | |
2552 | #define LERP(a, b, t) ((a) + (((b) - (a)) * (1.0 - (t)))) |
2553 | static void |
2554 | gtk_stack_measure (GtkWidget *widget, |
2555 | GtkOrientation orientation, |
2556 | int for_size, |
2557 | int *minimum, |
2558 | int *natural, |
2559 | int *minimum_baseline, |
2560 | int *natural_baseline) |
2561 | { |
2562 | GtkStack *stack = GTK_STACK (widget); |
2563 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2564 | GtkStackPage *child_info; |
2565 | GtkWidget *child; |
2566 | int child_min, child_nat; |
2567 | GList *l; |
2568 | |
2569 | *minimum = 0; |
2570 | *natural = 0; |
2571 | |
2572 | for (l = priv->children; l != NULL; l = l->next) |
2573 | { |
2574 | child_info = l->data; |
2575 | child = child_info->widget; |
2576 | |
2577 | if (!priv->homogeneous[orientation] && |
2578 | priv->visible_child != child_info) |
2579 | continue; |
2580 | |
2581 | if (gtk_widget_get_visible (widget: child)) |
2582 | { |
2583 | if (!priv->homogeneous[OPPOSITE_ORIENTATION(orientation)] && priv->visible_child != child_info) |
2584 | { |
2585 | int min_for_size; |
2586 | |
2587 | gtk_widget_measure (widget: child, OPPOSITE_ORIENTATION (orientation), for_size: -1, minimum: &min_for_size, NULL, NULL, NULL); |
2588 | |
2589 | gtk_widget_measure (widget: child, orientation, MAX (min_for_size, for_size), minimum: &child_min, natural: &child_nat, NULL, NULL); |
2590 | } |
2591 | else |
2592 | gtk_widget_measure (widget: child, orientation, for_size, minimum: &child_min, natural: &child_nat, NULL, NULL); |
2593 | |
2594 | *minimum = MAX (*minimum, child_min); |
2595 | *natural = MAX (*natural, child_nat); |
2596 | } |
2597 | } |
2598 | |
2599 | if (priv->last_visible_child != NULL && !priv->homogeneous[orientation]) |
2600 | { |
2601 | double t = priv->interpolate_size ? gtk_progress_tracker_get_ease_out_cubic (tracker: &priv->tracker, FALSE) : 1.0; |
2602 | int last_size; |
2603 | |
2604 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
2605 | last_size = priv->last_visible_widget_width; |
2606 | else |
2607 | last_size = priv->last_visible_widget_height; |
2608 | |
2609 | *minimum = LERP (*minimum, last_size, t); |
2610 | *natural = LERP (*natural, last_size, t); |
2611 | } |
2612 | } |
2613 | |
2614 | static void |
2615 | gtk_stack_init (GtkStack *stack) |
2616 | { |
2617 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2618 | |
2619 | priv->homogeneous[GTK_ORIENTATION_VERTICAL] = TRUE; |
2620 | priv->homogeneous[GTK_ORIENTATION_HORIZONTAL] = TRUE; |
2621 | priv->transition_duration = 200; |
2622 | priv->transition_type = GTK_STACK_TRANSITION_TYPE_NONE; |
2623 | } |
2624 | |
2625 | /** |
2626 | * gtk_stack_get_pages: (attributes org.gtk.Method.get_property=pages) |
2627 | * @stack: a `GtkStack` |
2628 | * |
2629 | * Returns a `GListModel` that contains the pages of the stack. |
2630 | * |
2631 | * This can be used to keep an up-to-date view. The model also |
2632 | * implements [iface@Gtk.SelectionModel] and can be used to track |
2633 | * and modify the visible page. |
2634 | * |
2635 | * Returns: (transfer full): a `GtkSelectionModel` for the stack's children |
2636 | */ |
2637 | GtkSelectionModel * |
2638 | gtk_stack_get_pages (GtkStack *stack) |
2639 | { |
2640 | GtkStackPrivate *priv = gtk_stack_get_instance_private (self: stack); |
2641 | |
2642 | g_return_val_if_fail (GTK_IS_STACK (stack), NULL); |
2643 | |
2644 | if (priv->pages) |
2645 | return g_object_ref (priv->pages); |
2646 | |
2647 | priv->pages = GTK_SELECTION_MODEL (ptr: gtk_stack_pages_new (stack)); |
2648 | g_object_add_weak_pointer (G_OBJECT (priv->pages), weak_pointer_location: (gpointer *)&priv->pages); |
2649 | |
2650 | return priv->pages; |
2651 | } |
2652 | |
2653 | /** |
2654 | * gtk_stack_page_get_visible: (attributes org.gtk.Method.get_property=visible) |
2655 | * @self: a `GtkStackPage` |
2656 | * |
2657 | * Returns whether @page is visible in its `GtkStack`. |
2658 | * |
2659 | * This is independent from the [property@Gtk.Widget:visible] |
2660 | * property of its widget. |
2661 | * |
2662 | * Returns: %TRUE if @page is visible |
2663 | */ |
2664 | gboolean |
2665 | gtk_stack_page_get_visible (GtkStackPage *self) |
2666 | { |
2667 | g_return_val_if_fail (GTK_IS_STACK_PAGE (self), FALSE); |
2668 | |
2669 | return self->visible; |
2670 | } |
2671 | |
2672 | /** |
2673 | * gtk_stack_page_set_visible: (attributes org.gtk.Method.set_property=visible) |
2674 | * @self: a `GtkStackPage` |
2675 | * @visible: The new property value |
2676 | * |
2677 | * Sets whether @page is visible in its `GtkStack`. |
2678 | */ |
2679 | void |
2680 | gtk_stack_page_set_visible (GtkStackPage *self, |
2681 | gboolean visible) |
2682 | { |
2683 | g_return_if_fail (GTK_IS_STACK_PAGE (self)); |
2684 | |
2685 | visible = !!visible; |
2686 | |
2687 | if (visible == self->visible) |
2688 | return; |
2689 | |
2690 | self->visible = visible; |
2691 | |
2692 | if (self->widget && gtk_widget_get_parent (widget: self->widget)) |
2693 | update_child_visible (GTK_STACK (gtk_widget_get_parent (self->widget)), child_info: self); |
2694 | |
2695 | g_object_notify_by_pspec (G_OBJECT (self), pspec: stack_page_props[CHILD_PROP_VISIBLE]); |
2696 | } |
2697 | |
2698 | /** |
2699 | * gtk_stack_page_get_needs_attention: (attributes org.gtk.Method.get_property=needs-attention) |
2700 | * @self: a `GtkStackPage` |
2701 | * |
2702 | * Returns whether the page is marked as “needs attention”. |
2703 | * |
2704 | * Returns: The value of the [property@Gtk.StackPage:needs-attention] |
2705 | * property. |
2706 | */ |
2707 | gboolean |
2708 | gtk_stack_page_get_needs_attention (GtkStackPage *self) |
2709 | { |
2710 | return self->needs_attention; |
2711 | } |
2712 | |
2713 | /** |
2714 | * gtk_stack_page_set_needs_attention: (attributes org.gtk.Method.set_property=needs-attention) |
2715 | * @self: a `GtkStackPage` |
2716 | * @setting: the new value to set |
2717 | * |
2718 | * Sets whether the page is marked as “needs attention”. |
2719 | */ |
2720 | void |
2721 | gtk_stack_page_set_needs_attention (GtkStackPage *self, |
2722 | gboolean setting) |
2723 | { |
2724 | setting = !!setting; |
2725 | |
2726 | if (setting == self->needs_attention) |
2727 | return; |
2728 | |
2729 | self->needs_attention = setting; |
2730 | g_object_notify_by_pspec (G_OBJECT (self), pspec: stack_page_props[CHILD_PROP_NEEDS_ATTENTION]); |
2731 | } |
2732 | |
2733 | /** |
2734 | * gtk_stack_page_get_use_underline: (attributes org.gtk.Method.get_property=use-underline) |
2735 | * @self: a `GtkStackPage` |
2736 | * |
2737 | * Gets whether underlines in the page title indicate mnemonics. |
2738 | * |
2739 | * Returns: The value of the [property@Gtk.StackPage:use-underline] property |
2740 | */ |
2741 | gboolean |
2742 | gtk_stack_page_get_use_underline (GtkStackPage *self) |
2743 | { |
2744 | return self->use_underline; |
2745 | } |
2746 | |
2747 | /** |
2748 | * gtk_stack_page_set_use_underline: (attributes org.gtk.Method.set_property=use-underline) |
2749 | * @self: a `GtkStackPage` |
2750 | * @setting: the new value to set |
2751 | * |
2752 | * Sets whether underlines in the page title indicate mnemonics. |
2753 | */ |
2754 | void |
2755 | gtk_stack_page_set_use_underline (GtkStackPage *self, |
2756 | gboolean setting) |
2757 | { |
2758 | setting = !!setting; |
2759 | |
2760 | if (setting == self->use_underline) |
2761 | return; |
2762 | |
2763 | self->use_underline = setting; |
2764 | g_object_notify_by_pspec (G_OBJECT (self), pspec: stack_page_props[CHILD_PROP_USE_UNDERLINE]); |
2765 | } |
2766 | |
2767 | |
2768 | /** |
2769 | * gtk_stack_page_get_name: (attributes org.gtk.Method.get_property=name) |
2770 | * @self: a `GtkStackPage` |
2771 | * |
2772 | * Returns the name of the page. |
2773 | * |
2774 | * Returns: (nullable): The value of the [property@Gtk.StackPage:name] property |
2775 | */ |
2776 | const char * |
2777 | gtk_stack_page_get_name (GtkStackPage *self) |
2778 | { |
2779 | g_return_val_if_fail (GTK_IS_STACK_PAGE (self), NULL); |
2780 | |
2781 | return self->name; |
2782 | } |
2783 | |
2784 | /** |
2785 | * gtk_stack_page_set_name: (attributes org.gtk.Method.set_property=name) |
2786 | * @self: a `GtkStackPage` |
2787 | * @setting: (transfer none): the new value to set |
2788 | * |
2789 | * Sets the name of the page. |
2790 | */ |
2791 | void |
2792 | gtk_stack_page_set_name (GtkStackPage *self, |
2793 | const char *setting) |
2794 | { |
2795 | GtkStack *stack = NULL; |
2796 | GtkStackPrivate *priv = NULL; |
2797 | |
2798 | g_return_if_fail (GTK_IS_STACK_PAGE (self)); |
2799 | |
2800 | if (self->widget && |
2801 | gtk_widget_get_parent (widget: self->widget) && |
2802 | GTK_IS_STACK (gtk_widget_get_parent (self->widget))) |
2803 | { |
2804 | GList *l; |
2805 | |
2806 | stack = GTK_STACK (gtk_widget_get_parent (self->widget)); |
2807 | priv = gtk_stack_get_instance_private (self: stack); |
2808 | |
2809 | for (l = priv->children; l != NULL; l = l->next) |
2810 | { |
2811 | GtkStackPage *info2 = l->data; |
2812 | if (self == info2) |
2813 | continue; |
2814 | |
2815 | if (g_strcmp0 (str1: info2->name, str2: setting) == 0) |
2816 | { |
2817 | g_warning ("Duplicate child name in GtkStack: %s" , setting); |
2818 | break; |
2819 | } |
2820 | } |
2821 | } |
2822 | |
2823 | if (setting == self->name) |
2824 | return; |
2825 | |
2826 | g_free (mem: self->name); |
2827 | self->name = g_strdup (str: setting); |
2828 | g_object_notify_by_pspec (G_OBJECT (self), pspec: stack_page_props[CHILD_PROP_NAME]); |
2829 | |
2830 | if (priv && priv->visible_child == self) |
2831 | g_object_notify_by_pspec (G_OBJECT (stack), |
2832 | pspec: stack_props[PROP_VISIBLE_CHILD_NAME]); |
2833 | } |
2834 | |
2835 | /** |
2836 | * gtk_stack_page_get_title: (attributes org.gtk.Method.get_property=title) |
2837 | * @self: a `GtkStackPage` |
2838 | * |
2839 | * Gets the page title. |
2840 | * |
2841 | * Returns: (nullable): The value of the [property@Gtk.StackPage:title] property |
2842 | */ |
2843 | const char * |
2844 | gtk_stack_page_get_title (GtkStackPage *self) |
2845 | { |
2846 | g_return_val_if_fail (GTK_IS_STACK_PAGE (self), NULL); |
2847 | |
2848 | return self->title; |
2849 | } |
2850 | |
2851 | /** |
2852 | * gtk_stack_page_set_title: (attributes org.gtk.Method.set_property=title) |
2853 | * @self: a `GtkStackPage` |
2854 | * @setting: (transfer none): the new value to set |
2855 | * |
2856 | * Sets the page title. |
2857 | */ |
2858 | void |
2859 | gtk_stack_page_set_title (GtkStackPage *self, |
2860 | const char *setting) |
2861 | { |
2862 | g_return_if_fail (GTK_IS_STACK_PAGE (self)); |
2863 | |
2864 | if (setting == self->title) |
2865 | return; |
2866 | |
2867 | g_free (mem: self->title); |
2868 | self->title = g_strdup (str: setting); |
2869 | g_object_notify_by_pspec (G_OBJECT (self), pspec: stack_page_props[CHILD_PROP_TITLE]); |
2870 | |
2871 | gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: self), |
2872 | first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, self->title, |
2873 | -1); |
2874 | } |
2875 | |
2876 | /** |
2877 | * gtk_stack_page_get_icon_name: (attributes org.gtk.Method.get_property=icon-name) |
2878 | * @self: a `GtkStackPage` |
2879 | * |
2880 | * Returns the icon name of the page. |
2881 | * |
2882 | * Returns: (nullable): The value of the [property@Gtk.StackPage:icon-name] property |
2883 | */ |
2884 | const char * |
2885 | gtk_stack_page_get_icon_name (GtkStackPage *self) |
2886 | { |
2887 | g_return_val_if_fail (GTK_IS_STACK_PAGE (self), NULL); |
2888 | |
2889 | return self->icon_name; |
2890 | } |
2891 | |
2892 | /** |
2893 | * gtk_stack_page_set_icon_name: (attributes org.gtk.Method.set_property=icon-name) |
2894 | * @self: a `GtkStackPage` |
2895 | * @setting: (transfer none): the new value to set |
2896 | * |
2897 | * Sets the icon name of the page. |
2898 | */ |
2899 | void |
2900 | gtk_stack_page_set_icon_name (GtkStackPage *self, |
2901 | const char *setting) |
2902 | { |
2903 | g_return_if_fail (GTK_IS_STACK_PAGE (self)); |
2904 | |
2905 | if (setting == self->icon_name) |
2906 | return; |
2907 | |
2908 | g_free (mem: self->icon_name); |
2909 | self->icon_name = g_strdup (str: setting); |
2910 | g_object_notify_by_pspec (G_OBJECT (self), pspec: stack_page_props[CHILD_PROP_ICON_NAME]); |
2911 | } |
2912 | |