halting problem :: Crash Years

:: ~3 min read

to continue the series about actions and constraints, here’s an example showing how to use the ClutterDragAction to create a scrolling container that pans the contents using dragging.

we can start with the usual set up of the stage:

  clutter_init (&argc, &argv);

stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Scrolling"); clutter_actor_set_size (stage, 800, 600); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_actor_show (stage);

then we set up the group that will contain the visible portion of the panning content:
  scroll = clutter_group_new ();
  clutter_container_add_actor (CLUTTER_CONTAINER (stage), scroll);
  clutter_actor_set_size (scroll, RECT_WIDTH, RECT_HEIGHT);

constraint = clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5) clutter_actor_add_constraint (scroll, constraint); constraint = clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.5) clutter_actor_add_constraint (scroll, constraint); clutter_actor_set_clip_to_allocation (scroll, TRUE);

the RECT_WIDTH and RECT_HEIGHT are the two size constants for the each “page” of the content. we use ClutterAlignConstraints to keep the group centered on the stage ((this will work even if you set the stage as user resizable, as the constraints are recomputed at each allocation cycle)).

the important bit is clutter_actor_set_clip_to_allocation() which will use (and track) the actor’s allocation as the clipping area.

  viewport = clutter_box_new (clutter_box_layout_new ());
  clutter_container_add_actor (CLUTTER_CONTAINER (scroll), viewport);

for (i = 0; i < N_RECTS; i++) { ClutterColor color;

  clutter_color_from_string (&amp;color, rect_color[i]);

  rectangle[i] = clutter_rectangle_new_with_color (&amp;color);
  clutter_container_add_actor (CLUTTER_CONTAINER (viewport), rectangle[i]);
  clutter_actor_set_size (rectangle[i], RECT_WIDTH, RECT_HEIGHT);
}</pre>

this is the content area, composed of a ClutterBox using a ClutterBoxLayout to lay out a list of rectangles.

let’s start the main loop, and the result should look like this:

[caption id=”attachment_360” align=”aligncenter” width=”300” caption=”does not do much”]Panning Container[/caption]

now we need to enable the panning logic. to do so, we use the ClutterDragAction on the viewport actor, and we constrain it to the horizontal axis:

  action = clutter_drag_action_new ();
  clutter_actor_add_action (viewport, action);
  clutter_drag_action_set_drag_axis (CLUTTER_DRAG_ACTION (action), CLUTTER_DRAG_X_AXIS);
  clutter_actor_set_reactive (viewport, TRUE);
and that’s it:

[caption id=”attachment_361” align=”aligncenter” width=”300” caption=”it's done! ship it!”][/caption]

well, except that it isn’t really done; the behaviour at the edges of the viewport can lead to simply have no way to pan back, and the whole thing is a bit static. we should add some “kinetic-style” animation depending on the position of the content at the end of the panning action. to do so, we can use the ClutterDragAction::drag-end signal:

g_signal_connect (action, "drag-end", G_CALLBACK (on_drag_end), NULL);
and have our logic there; first, the edges:
  float viewport_x = clutter_actor_get_x (viewport);

/ check if we're at the viewport edges / if (viewport_x > 0) { clutter_actor_animate (viewport, CLUTTER_EASE_OUT_BOUNCE, 250, "x", 0.0, NULL); return; }

if (viewport_x < (-1.0f * (RECT_WIDTH * (N_RECTS - 1)))) { clutter_actor_animate (viewport, CLUTTER_EASE_OUT_BOUNCE, 250, "x", (-1.0f * (RECT_WIDTH * (N_RECTS - 1))), NULL); return; }

then the content:
  float offset_x;
  int child_visible;

/ animate the viewport to fully show the child once we pass * a certain threshold with the dragging action / offset_x = fabsf (viewport_x) / RECT_WIDTH + 0.5f; if (offset_x > (RECT_WIDTH * 0.33)) child_visible = (int) offset_x + 1; else child_visible = (int) offset_x;

/ sanity check on the children number / child_visible = CLAMP (child_visible, 0, N_RECTS);

clutter_actor_animate (viewport, CLUTTER_EASE_OUT_QUAD, 250, "x", (-1.0f * RECT_WIDTH * child_visible), NULL);

and here‘s the result:

that wasn’t very hard, was it?

I’m going to submit this as a recipe for the Clutter Cookbook; the reference source code is available here.

context-switch wordpress old-blog