Constraints

last time, I described the Effects in Clutter as anything that should affect the way an actor paints itself without sub-classing.

this kind of modification of the behaviour of an Actor can also be applied to other areas, and leads to defining three different kinds of modifiers:

  • Effects, which modify the way an actor paints itself
  • Actions, which modify the way an actor responds to user events
  • Constraints, which modify the way an actor is positioned or sized

being capable of being dragged using the pointer can be defined as an action: it changes the way an Actor responds to user events (button press, motion, button release). in the same way, update the X position using the X position of another actor is a constraint placed on an Actor.

Clutter already has signals, properties and virtual functions for dealing with these modifiers, and they can be seen as the building blocks. What’s missing is a set of classes that wrap those building blocks into something that leads to run-time composition of effects, actions and constraints on top of an existing Actor class.

last week I started working on the implementation of these modifiers, and now I’m pretty close to a finalized API for Clutter 1.4. the usage of an action is pretty simple:

  ClutterAction *action = clutter_drag_action_new ();
  g_signal_connect (action, "drag-motion",
                    G_CALLBACK (on_drag_motion),
                    NULL);

  clutter_actor_add_action (some_actor, action)
  clutter_actor_set_reactive (actor, TRUE);

the simplest implementation of a ClutterDragAction::drag-motion signal handler is:

  static void
  on_drag_motion (ClutterDragAction *action,
                  ClutterActor      *actor,
                  float              delta_x,
                  float              delta_y)
  {
    clutter_actor_move_by (actor, delta_x, delta_y);
  }

there’s no fundamental difference for the constraints API; for instance, this positions an actor in the middle of another:

  ClutterConstraint *constraint;

  constraint = clutter_align_constraint_new (parent, CLUTTER_ALIGN_X_AXIS, 0.5);
  clutter_actor_add_constraint (some_actor, constraint);

  constraint = clutter_align_constraint_new (parent, CLUTTER_ALIGN_Y_AXIS, 0.5);
  clutter_actor_add_constraint (some_actor, constraint);

it’s important to note that parent in this case can be any actor, and not necessarily the scene graph parent of some_actor.

this example binds the X coordinate of an actor to the X coordinate of another one, while keeping it Y aligned at the center of its parent:

  ClutterConstraint *constraint;

  constraint = clutter_bind_constraint_new (source, CLUTTER_BIND_X, 0.0);
  clutter_actor_add_constraint_with_name (some_actor, "x-bind", source);

  constraint = clutter_align_constraint_new (parent, CLUTTER_ALIGN_Y_AXIS, 0.5);
  clutter_actor_add_constraint_with_name (some_actor, "y-bind", constraint);

Update: I’ve updated the example to use the newly added add_constraint_with_name() instead of set_name()+add_constraint(); I also changed the constraint names.

now, I’ve added a call to clutter_actor_meta_set_name() in the mix because I might decide to animate the offset property of the ClutterBindConstraint using a specific syntax for ClutterAnimation ((the change is contained in ClutterActor so I’m not over-complicating the parsing code in the animation class itself; which also means that ClutterAnimator and the upcoming ClutterState classes will be able to use the same syntax to address action, effect and constraint properties)):

  float new_offset = clutter_actor_get_width (source) + h_padding;
  clutter_actor_animate (some_actor, CLUTTER_EASE_OUT_CUBIC, 500,
                         "@constraints.x-bind.offset", new_offset,
                         "opacity", 128,
                         NULL);

the new syntax is still type checked, so you’ll get warnings if you’re using the wrong type or the wrong interval ((I’m still considering using a dotted notation; I might switch to a colon notation, or a slash notation. and yes: I know that it looks like the syntax for key access in CoreAnimation; it turns out that there aren’t many new ways to access an item in a path)).

caveat: obviously, a constraint only applies to an actor that is in a fixed positioning layout manager.

the new classes and API are available in the wip/actor-actions branch here and wip/constraints branch here; there are also a couple of interactive tests that show you how actions and constraints can be used with existing actors. if all goes according to plan, I’ll merge them by the end of the week — following Frederic’s blog post, I want to make a 1.3 snapshot release in time for GNOME 2.31.2.

6 Replies to “Constraints”

  1. The Action usage could be way simplified. Why should the user be responsible for figuring out which signals to connect, for example? That’s an implementation detail of the Action. Making the user be responsible just makes the API require more effort to use, increases the likelihood of mistakes, and makes changing parts of the implementation of an action essentially impossible.

    Likewise, for constraints, wouldn’t it be better to just automatically add the bind-x meta to the actor when the BIND_X constraint is applied?

    APIs should generally aim to be low level or high level. This one is kind of mid level, and hence it’s hard to figure out the target user of the API. It’s got too many steps and exposes too much implementation to be a good candidate for high level scripting and UI designer interaction, but it’s too specific for someone who’s trying to build a general widget library on top of the core Clutter functionality.

  2. @Iain: I’m tempted by the snowman. or I could use the ♥.

    @Sean: your arguments make no sense whatsoever. the signals are there to allow you to change the implementation. and adding a custom name when there’s the risk of collision is stupid and dangerous — especially if you’re using a UI builder, where these things should be automatically filled in by the builder itself, and not by the library.

    and yes: Clutter is a mid-level toolkit. it’s like that by design.

    by the way, and this fires me up all the time, so I’ll just go ahead and say it once and for all — the user is responsible for learning an API: it’s not the API designer’s place to write applications for the user. if I create an API that solves one user’s problem invariably I will a) solve just one class of problems, b) make it harder for anyone that doesn’t have that same class of problems to solve them and finally c) force everyone to shoehorn every single problem into the same class because that’s easily solved.

    if you want me to write applications for you then I’m sorry, but there’s no amount of API I can possibly write to make that easier.

    have you even looked at the code in the examples? the staggering low amount of code that’s needed? yes: you have to learn how to use an API. big. fucking. whoop.

  3. Emmanuele thank you very much for the nice ClutterAction implementation.

    Every day, I literally swim in “events” in several platforms including JS,Actionscript and iPhone ObjC.

    For that, I think I can see Sean’s motivation. Action API could be simpler. And I don’t think he is talking about not learning APIs. As a high level framework, even Flash AS3 Event API is pretty complicated and has a learning curve.

    I find Flash Display and Event model very smart and versatile. This is actually the reason I love Clutter. It resembles Flash in many ways and frankly it even offers higher level functionality in “C”, like the box containers.

    All in all, Clutter is great, and even greater with the ClutterAction implementation. Thanks again for all the hard work.

Comments are closed.