No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /**
4 * SECTION:shell-generic-container
5 * @short_description: A container class with signals for allocation
6 *
7 * #ShellGenericContainer is mainly a workaround for the current
8 * lack of GObject subclassing + vfunc overrides in gjs. We
9 * implement the container interface, but proxy the virtual functions
10 * into signals, which gjs can catch.
11 *
12 * #ShellGenericContainer is an #StWidget, and automatically takes its
13 * borders and padding into account during size request and allocation.
14 */
15
16 #include "config.h"
17
18 #include "shell-generic-container.h"
19
20 #include <clutter/clutter.h>
21 #include <gtk/gtk.h>
22 #include <girepository.h>
23
24 static void shell_generic_container_iface_init (ClutterContainerIface *iface);
25
26 G_DEFINE_TYPE_WITH_CODE(ShellGenericContainer,
27 shell_generic_container,
28 ST_TYPE_WIDGET,
29 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
30 shell_generic_container_iface_init));
31
32 struct _ShellGenericContainerPrivate {
33 GHashTable *skip_paint;
34 };
35
36 /* Signals */
37 enum
38 {
39 GET_PREFERRED_WIDTH,
40 GET_PREFERRED_HEIGHT,
41 ALLOCATE,
42 LAST_SIGNAL
43 };
44
45 static guint shell_generic_container_signals [LAST_SIGNAL] = { 0 };
46
47 static gpointer
48 shell_generic_container_allocation_ref (ShellGenericContainerAllocation *alloc)
49 {
50 alloc->_refcount++;
51 return alloc;
52 }
53
54 static void
55 shell_generic_container_allocation_unref (ShellGenericContainerAllocation *alloc)
56 {
57 if (--alloc->_refcount == 0)
58 g_slice_free (ShellGenericContainerAllocation, alloc);
59 }
60
61 static void
62 shell_generic_container_allocate (ClutterActor *self,
63 const ClutterActorBox *box,
64 ClutterAllocationFlags flags)
65 {
66 StThemeNode *theme_node;
67 ClutterActorBox content_box;
68
69 clutter_actor_set_allocation (self, box, flags);
70
71 theme_node = st_widget_get_theme_node (ST_WIDGET (self));
72 st_theme_node_get_content_box (theme_node, box, &content_box);
73
74 g_signal_emit (G_OBJECT (self), shell_generic_container_signals[ALLOCATE], 0,
75 &content_box, flags);
76 }
77
78 static void
79 shell_generic_container_get_preferred_width (ClutterActor *actor,
80 gfloat for_height,
81 gfloat *min_width_p,
82 gfloat *natural_width_p)
83 {
84 ShellGenericContainerAllocation *alloc = g_slice_new0 (ShellGenericContainerAllocation);
85 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
86
87 st_theme_node_adjust_for_height (theme_node, &for_height);
88
89 alloc->_refcount = 1;
90 g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_WIDTH], 0,
91 for_height, alloc);
92 if (min_width_p)
93 *min_width_p = alloc->min_size;
94 if (natural_width_p)
95 *natural_width_p = alloc->natural_size;
96 shell_generic_container_allocation_unref (alloc);
97
98 st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
99 }
100
101 static void
102 shell_generic_container_get_preferred_height (ClutterActor *actor,
103 gfloat for_width,
104 gfloat *min_height_p,
105 gfloat *natural_height_p)
106 {
107 ShellGenericContainerAllocation *alloc = g_slice_new0 (ShellGenericContainerAllocation);
108 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
109
110 st_theme_node_adjust_for_width (theme_node, &for_width);
111
112 alloc->_refcount = 1;
113 g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_HEIGHT], 0,
114 for_width, alloc);
115 if (min_height_p)
116 *min_height_p = alloc->min_size;
117 if (natural_height_p)
118 *natural_height_p = alloc->natural_size;
119 shell_generic_container_allocation_unref (alloc);
120
121 st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
122 }
123
124 static void
125 shell_generic_container_paint (ClutterActor *actor)
126 {
127 ShellGenericContainer *self = (ShellGenericContainer*) actor;
128 ClutterActor *child;
129
130 st_widget_paint_background (ST_WIDGET (actor));
131
132 for (child = clutter_actor_get_first_child (actor);
133 child != NULL;
134 child = clutter_actor_get_next_sibling (child))
135 {
136 if (g_hash_table_lookup (self->priv->skip_paint, child))
137 continue;
138
139 clutter_actor_paint (child);
140 }
141 }
142
143 static void
144 shell_generic_container_pick (ClutterActor *actor,
145 const ClutterColor *color)
146 {
147 ShellGenericContainer *self = (ShellGenericContainer*) actor;
148 ClutterActor *child;
149
150 CLUTTER_ACTOR_CLASS (shell_generic_container_parent_class)->pick (actor, color);
151
152 for (child = clutter_actor_get_first_child (actor);
153 child != NULL;
154 child = clutter_actor_get_next_sibling (child))
155 {
156 if (g_hash_table_lookup (self->priv->skip_paint, child))
157 continue;
158
159 clutter_actor_paint (child);
160 }
161 }
162
163 static GList *
164 shell_generic_container_get_focus_chain (StWidget *widget)
165 {
166 ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (widget);
167 ClutterActor *child;
168 GList *focus_chain;
169
170 focus_chain = NULL;
171 for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
172 child != NULL;
173 child = clutter_actor_get_next_sibling (child))
174 {
175 if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
176 !shell_generic_container_get_skip_paint (self, child))
177 focus_chain = g_list_prepend (focus_chain, child);
178 }
179
180 return g_list_reverse (focus_chain);
181 }
182
183 /**
184 * shell_generic_container_get_n_skip_paint:
185 * @self: A #ShellGenericContainer
186 *
187 * Returns: Number of children which will not be painted.
188 */
189 guint
190 shell_generic_container_get_n_skip_paint (ShellGenericContainer *self)
191 {
192 return g_hash_table_size (self->priv->skip_paint);
193 }
194
195 /**
196 * shell_generic_container_get_skip_paint:
197 * @self: A #ShellGenericContainer
198 * @child: Child #ClutterActor
199 *
200 * Gets whether or not @actor is skipped when painting.
201 *
202 * Return value: %TRUE or %FALSE
203 */
204 gboolean
205 shell_generic_container_get_skip_paint (ShellGenericContainer *self,
206 ClutterActor *child)
207 {
208 return g_hash_table_lookup (self->priv->skip_paint, child) != NULL;
209 }
210
211 /**
212 * shell_generic_container_set_skip_paint:
213 * @self: A #ShellGenericContainer
214 * @child: Child #ClutterActor
215 * @skip: %TRUE if we should skip painting
216 *
217 * Set whether or not we should skip painting @actor. Workaround for
218 * lack of gjs ability to override _paint vfunc.
219 */
220 void
221 shell_generic_container_set_skip_paint (ShellGenericContainer *self,
222 ClutterActor *child,
223 gboolean skip)
224 {
225 gboolean currently_skipping;
226
227 currently_skipping = g_hash_table_lookup (self->priv->skip_paint, child) != NULL;
228 if (!!skip == currently_skipping)
229 return;
230
231 if (!skip)
232 g_hash_table_remove (self->priv->skip_paint, child);
233 else
234 g_hash_table_insert (self->priv->skip_paint, child, child);
235
236 clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
237 }
238
239 static void
240 shell_generic_container_finalize (GObject *object)
241 {
242 ShellGenericContainer *self = (ShellGenericContainer*) object;
243
244 g_hash_table_destroy (self->priv->skip_paint);
245
246 G_OBJECT_CLASS (shell_generic_container_parent_class)->finalize (object);
247 }
248
249 static void
250 shell_generic_container_class_init (ShellGenericContainerClass *klass)
251 {
252 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
253 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
254 StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
255
256 gobject_class->finalize = shell_generic_container_finalize;
257
258 actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
259 actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
260 actor_class->allocate = shell_generic_container_allocate;
261 actor_class->paint = shell_generic_container_paint;
262 actor_class->pick = shell_generic_container_pick;
263
264 widget_class->get_focus_chain = shell_generic_container_get_focus_chain;
265
266 /**
267 * ShellGenericContainer::get-preferred-width:
268 * @self: the #ShellGenericContainer
269 * @for_height: as in clutter_actor_get_preferred_width()
270 * @alloc: a #ShellGenericContainerAllocation to be filled in
271 *
272 * Emitted when clutter_actor_get_preferred_width() is called
273 * on @self. You should fill in the fields of @alloc with the
274 * your minimum and natural widths. #ShellGenericContainer
275 * will deal with taking its borders and padding into account
276 * for you.
277 *
278 * @alloc's fields are initialized to 0, so unless you have a fixed
279 * width specified (via #ClutterActor:width or CSS), you must
280 * connect to this signal and fill in the values.
281 */
282 shell_generic_container_signals[GET_PREFERRED_WIDTH] =
283 g_signal_new ("get-preferred-width",
284 G_TYPE_FROM_CLASS (klass),
285 G_SIGNAL_RUN_LAST,
286 0,
287 NULL, NULL, NULL,
288 G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
289
290 /**
291 * ShellGenericContainer::get-preferred-height:
292 * @self: the #ShellGenericContainer
293 * @for_width: as in clutter_actor_get_preferred_height()
294 * @alloc: a #ShellGenericContainerAllocation to be filled in
295 *
296 * Emitted when clutter_actor_get_preferred_height() is called
297 * on @self. You should fill in the fields of @alloc with the
298 * your minimum and natural heights. #ShellGenericContainer
299 * will deal with taking its borders and padding into account
300 * for you.
301 *
302 * @alloc's fields are initialized to 0, so unless you have a fixed
303 * height specified (via #ClutterActor:height or CSS), you must
304 * connect to this signal and fill in the values.
305 */
306 shell_generic_container_signals[GET_PREFERRED_HEIGHT] =
307 g_signal_new ("get-preferred-height",
308 G_TYPE_FROM_CLASS (klass),
309 G_SIGNAL_RUN_LAST,
310 0,
311 NULL, NULL, NULL,
312 G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
313
314 /**
315 * ShellGenericContainer::allocate:
316 * @self: the #ShellGenericContainer
317 * @box: @self's content box
318 * @flags: the allocation flags.
319 *
320 * Emitted when @self is allocated, after chaining up to the parent
321 * allocate method.
322 *
323 * Note that @box is @self's content box (qv
324 * st_theme_node_get_content_box()), NOT its allocation.
325 */
326 shell_generic_container_signals[ALLOCATE] =
327 g_signal_new ("allocate",
328 G_TYPE_FROM_CLASS (klass),
329 G_SIGNAL_RUN_LAST,
330 0,
331 NULL, NULL, NULL,
332 G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR_BOX, CLUTTER_TYPE_ALLOCATION_FLAGS);
333
334 g_type_class_add_private (gobject_class, sizeof (ShellGenericContainerPrivate));
335 }
336
337 static void
338 shell_generic_container_actor_removed (ClutterContainer *container,
339 ClutterActor *actor)
340 {
341 ShellGenericContainerPrivate *priv = SHELL_GENERIC_CONTAINER (container)->priv;
342
343 g_hash_table_remove (priv->skip_paint, actor);
344 }
345
346 static void
347 shell_generic_container_iface_init (ClutterContainerIface *iface)
348 {
349 iface->actor_removed = shell_generic_container_actor_removed;
350 }
351
352 static void
353 shell_generic_container_init (ShellGenericContainer *area)
354 {
355 area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_GENERIC_CONTAINER,
356 ShellGenericContainerPrivate);
357 area->priv->skip_paint = g_hash_table_new (NULL, NULL);
358 }
359
360 GType
361 shell_generic_container_allocation_get_type (void)
362 {
363 static GType gtype = G_TYPE_INVALID;
364 if (gtype == G_TYPE_INVALID)
365 {
366 gtype = g_boxed_type_register_static ("ShellGenericContainerAllocation",
367 (GBoxedCopyFunc)shell_generic_container_allocation_ref,
368 (GBoxedFreeFunc)shell_generic_container_allocation_unref);
369 }
370 return gtype;
371 }