No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /**
4 * SECTION:shell-stack
5 * @short_description: Pure "Z-axis" container class
6 *
7 * A #ShellStack draws its children on top of each other,
8 * aligned to the top left. It will be sized in width/height
9 * according to the largest such dimension of its children, and
10 * all children will be allocated that size. This differs
11 * from #ClutterGroup which allocates its children their natural
12 * size, even if that would overflow the size allocated to the stack.
13 */
14
15 #include "config.h"
16
17 #include "shell-stack.h"
18
19 G_DEFINE_TYPE (ShellStack,
20 shell_stack,
21 ST_TYPE_WIDGET);
22
23 static void
24 shell_stack_allocate (ClutterActor *self,
25 const ClutterActorBox *box,
26 ClutterAllocationFlags flags)
27 {
28 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
29 ClutterActorBox content_box;
30 ClutterActor *child;
31
32 clutter_actor_set_allocation (self, box, flags);
33
34 st_theme_node_get_content_box (theme_node, box, &content_box);
35
36 for (child = clutter_actor_get_first_child (self);
37 child != NULL;
38 child = clutter_actor_get_next_sibling (child))
39 {
40 ClutterActorBox child_box = content_box;
41 clutter_actor_allocate (child, &child_box, flags);
42 }
43 }
44
45 static void
46 shell_stack_get_preferred_height (ClutterActor *actor,
47 gfloat for_width,
48 gfloat *min_height_p,
49 gfloat *natural_height_p)
50 {
51 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
52 gboolean first = TRUE;
53 float min = 0, natural = 0;
54 ClutterActor *child;
55
56 st_theme_node_adjust_for_width (theme_node, &for_width);
57
58 for (child = clutter_actor_get_first_child (actor);
59 child != NULL;
60 child = clutter_actor_get_next_sibling (child))
61 {
62 float child_min, child_natural;
63
64 clutter_actor_get_preferred_height (child,
65 for_width,
66 &child_min,
67 &child_natural);
68
69 if (first)
70 {
71 first = FALSE;
72 min = child_min;
73 natural = child_natural;
74 }
75 else
76 {
77 if (child_min > min)
78 min = child_min;
79
80 if (child_natural > natural)
81 natural = child_natural;
82 }
83 }
84
85 if (min_height_p)
86 *min_height_p = min;
87
88 if (natural_height_p)
89 *natural_height_p = natural;
90
91 st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
92 }
93
94 static void
95 shell_stack_get_preferred_width (ClutterActor *actor,
96 gfloat for_height,
97 gfloat *min_width_p,
98 gfloat *natural_width_p)
99 {
100 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
101 gboolean first = TRUE;
102 float min = 0, natural = 0;
103 ClutterActor *child;
104
105 st_theme_node_adjust_for_height (theme_node, &for_height);
106
107 for (child = clutter_actor_get_first_child (actor);
108 child != NULL;
109 child = clutter_actor_get_next_sibling (child))
110 {
111 float child_min, child_natural;
112
113 clutter_actor_get_preferred_width (child,
114 for_height,
115 &child_min,
116 &child_natural);
117
118 if (first)
119 {
120 first = FALSE;
121 min = child_min;
122 natural = child_natural;
123 }
124 else
125 {
126 if (child_min > min)
127 min = child_min;
128
129 if (child_natural > natural)
130 natural = child_natural;
131 }
132 }
133
134 if (min_width_p)
135 *min_width_p = min;
136
137 if (natural_width_p)
138 *natural_width_p = natural;
139
140 st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
141 }
142
143 static gboolean
144 shell_stack_navigate_focus (StWidget *widget,
145 ClutterActor *from,
146 GtkDirectionType direction)
147 {
148 ClutterActor *top_actor;
149
150 /* If the stack is itself focusable, then focus into or out of
151 * it, as appropriate.
152 */
153 if (st_widget_get_can_focus (widget))
154 {
155 if (from && clutter_actor_contains (CLUTTER_ACTOR (widget), from))
156 return FALSE;
157
158 if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (widget)))
159 {
160 clutter_actor_grab_key_focus (CLUTTER_ACTOR (widget));
161 return TRUE;
162 }
163 else
164 {
165 return FALSE;
166 }
167 }
168
169 top_actor = clutter_actor_get_last_child (CLUTTER_ACTOR (widget));
170 if (ST_IS_WIDGET (top_actor))
171 return st_widget_navigate_focus (ST_WIDGET (top_actor), from, direction, FALSE);
172 else
173 return FALSE;
174 }
175
176 static void
177 shell_stack_class_init (ShellStackClass *klass)
178 {
179 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
180 StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
181
182 actor_class->get_preferred_width = shell_stack_get_preferred_width;
183 actor_class->get_preferred_height = shell_stack_get_preferred_height;
184 actor_class->allocate = shell_stack_allocate;
185
186 widget_class->navigate_focus = shell_stack_navigate_focus;
187 }
188
189 static void
190 shell_stack_init (ShellStack *actor)
191 {
192 }