Events and Animations

Create logo.vala

Copy/paste this into a file called logo.vala:

using Clutter;

void
main(string[] args)
{
    Clutter.init(ref args);
    
    var red = new Actor() {
        x = 0,
        y = 0,
        width = 200,
        height = 200,
        background_color = Color.from_string("red")};
    var green = new Actor() {
        x = 200,
        y = 0,
        width = 200,
        height = 200,
        background_color = Color.from_string("green")};
    var blue = new Actor() {
        x = 0,
        y = 200,
        width = 200,
        height = 200,
        background_color = Color.from_string("blue")};
    var yellow = new Actor() {
        x = 200,
        y = 200,
        width = 200,
        height = 200,
        background_color = Color.from_string("yellow")};
    
    var container = new Actor() {
        x = 0,
        y = 0,
        width = 400,
        height = 400};
    container.save_easing_state();
    container.set_easing_mode(AnimationMode.EASE_IN_OUT_CUBIC);
    container.set_easing_duration(400);
    
    foreach (var rect in new Actor[]{red, green, blue, yellow})
        container.add_child(rect);
    
    var stage = new Stage() {
        width = 200,
        height = 200};
    
    stage.add_child(container);
    
    stage.key_press_event.connect((event) =>
    {
        switch (event.keyval)
        {
        case Key.Up:
            container.y = 0;
            break;
        case Key.Down:
            container.y = -200;
            break;
        case Key.Left:
            container.x = 0;
            break;
        case Key.Right:
            container.x = -200;
            break;
        default:
            return false;
        }
        return true;
    });
    
    stage.show();
    stage.destroy.connect(Clutter.main_quit);
    Clutter.main();
}

Result

Compile/run as was done in Getting Started. We see this:

logo_result.png

You can pan around to one of 4 colored squares using the arrow keys. Recognize the logo?

Discussion

Actors

In this example, we created several actors. An actor is anything that appears on-stage. If you use the base Actor class directly, you'll get a rectangle, but subclasses of Actor can be used to render anything. For example, Text (as seen in Getting Started) subclasses Actor, and Stage itself is also an Actor subclass (albeit an unusual one).

The first actors we created were 200x200 colored rectangles like this one:

var red = new Actor() {
    x = 0,
    y = 0,
    width = 200,
    height = 200,
    background_color = Color.from_string("red")};

Each of these had different x and y coordinates.

We then created a larger 400x400 rectangle called container

var container = new Actor() {
    x = 0,
    y = 0,
    width = 400,
    height = 400};

and inserted each of the colored rectangles into it:

foreach (var rect in new Actor[]{red, green, blue, yellow})
    container.add_child(rect);

By inserting actors as children of other actors, you can create nested structures. Any actor can serve as a parent, and any other actor (except a Stage) can be added as a child. However, some actors (like Text) aren't intended to serve as containers and won't render their children.

With these rectangles placed inside it, container will look like this:

full_logo.png

However, you can only see 1/4 of it at a time, since the stage isn't large enough.

Events

To detect key input, we connected to the stage's key_press_event signal:

stage.key_press_event.connect((event) =>
{
...

The callback takes an argument called event which encodes information about the event. event.keyval indicates which key was pressed:

switch (event.keyval)
{
case Key.Up:
...

Animations

We moved the container to various positions on the stage by setting its x and y coordinates:

case Key.Up:
    container.y = 0;
...

Normally, this would just cause container to jump from position to position, but instead it animates smoothly. This is because we did some set-up beforehand:

container.save_easing_state();
container.set_easing_mode(AnimationMode.EASE_IN_OUT_CUBIC);
container.set_easing_duration(400);

save_easing_state() has no real purpose in this situation, but it's necessary to enable animations. set_easing_mode() affects how the speed of the animation changes as it runs; EASE_IN_OUT_CUBIC is a good default which causes animations to proceed slowly at first, then faster in the middle, then slowly again near the end. set_easing_duration sets the overall time that the animation will take.

The easing mode and duration affect all animations on an actor. To change them temporarily, you can use save_easing_state and restore_easing_state as follows:

my_actor.save_easing_state();
my_actor.set_easing_mode(AnimationMode.SOME_MODE);
my_actor.set_easing_duration(SOME_TIME);
... // do animation
my_actor.restore_easing_state();

Attic/Clutter/Tutorial/EventsAndAnimations (last edited 2024-04-11 15:39:37 by EmmanueleBassi)