ImplicitAnimationController

Since: BlackBerry 10.0.0

#include <bb/cascades/ImplicitAnimationController>

Controls implicit (automatic) property animations.

Warning!

Instances of this class must be created and handled on the application thread or a runtime error will occur.

Implicit animations are animations performed by the framework when a value of property changes to a new value. When a property changes, the actual value of property is not animated, it's only the visual representation that is animated. For example, if the application changes the VisualNode::translationX property of an object, the property value is updated instantly but the visual representation of the affected control will be animated into the new position.

Many of the core controls in the framework contain visual properties that are implicitly animated, however only the following properties are allowed to be controlled using ImplicitAnimationController class:

This class allows for controlling either a single property of a VisualNode or all implicitly animated properties as a whole, if no property name is specified during the creation of a controller.

User-defined properties (those which not defined by the framework in built-in controls) are not implicitly animated and can not be controlled with this class.

For layout-related properties, like positioning, alignment, and preferred width and height, you can disable implicit animations using the Control::implicitLayoutAnimationsEnabled flag. Once disabled, all layout-related property changes will not be implicitly animated (i.e. changes happen immediately).

The scope of the effect that an ImplicitAnimationController has on implicit animations is determined by the lifetime of the ImplicitAnimationController instance.

When an ImplicitAnimationController instance is created using ImplicitAnimationController::create(), the implicit animation state change immediately takes effect. When this instance is destroyed the setting is reset to the original value.

Instances of this class are implicitly shared, so the referred setting will only be reset once all controller instances sharing the same setting are destroyed. Assigning a controller to another controller creates another instance referring to the original setting.

Here's how you can control the scope of implicit animation changes using ImplicitAnimationController. First, a basic button is created and added to the scene.

mButton1 = Button::create()
    .text("Disable");
connect(mButton1, SIGNAL(clicked()), this,
        SLOT(onClicked1()));
When clicked, implicit animations are disabled on the button, but only within the scope of the method. When this method is complete, the controller is destroyed and the animation state is restored.
void ImplicitAnimationExamples::onClicked1() {
    // Implicit animations are disabled for "node" within the scope
    // of this method.
    ImplicitAnimationController allDisabled =
            ImplicitAnimationController::create(mButton1)
                    .enabled(false);

    mButton1->setTranslationX(100.0f);
    mButton1->setRotationZ(45.0f);
    mButton1->setOpacity(0.5f);
    // When this method is complete, the controller is destroyed and the
    // animation state is restored.
}
Nested state scopes are also supported. In the following example, all animations are initially disabled. Within the method, the disabled setting is overridden to allow for animations just on the node's rotationZ property.
void ImplicitAnimationExamples::onClicked2() {
    // All implicit animations are disabled for the node for the scope
    // of this method.
    ImplicitAnimationController allDisabled =
            ImplicitAnimationController::create(mButton2).enabled(false);

    mButton2->setTranslationX(100.0f); // Setting is not animated
    {
        // Overrides the setting in this scope for the "rotationZ" property.
        ImplicitAnimationController enableRotation =
                ImplicitAnimationController::create(mButton2, "rotationZ")
                        .enabled(true);
        mButton2->setRotationZ(45.0f); // Animated
    }
    mButton2->setOpacity(0.5f); // Setting is not animated
}
For cases where implicit animations need to be controlled for a scope wider than the scope of single method, one can extend the lifetime of the corresponding controller. Here's a basic button that gets added to the scene graph:
mButton3 = Button::create().text("Disable");
connect(mButton3, SIGNAL(clicked()), this,
        SLOT(toggleImplicitAnimations()));
When clicked, implicit animations are disabled within the entire scope of the class.
void ImplicitAnimationExamples::toggleImplicitAnimations() {
    // Important: first end previous transaction by assigning an
    // inactive controller to our controller.
    mButtonAnimationState = ImplicitAnimationController();

    if (mButton3->text().startsWith("Disable")) {
        mButton3->setText("Enable");
        // Disable implicit animations for m_button.
        mButtonAnimationState =
                ImplicitAnimationController::create(mButton3)
                        .enabled(false);
    } else {
        mButton3->setText("Disable");
        // Enable implicit animations for m_button.
        mButtonAnimationState =
                ImplicitAnimationController::create(mButton3)
                        .enabled(true);
    }
    // Whether the rotation is animated or not depends on the logic above
    mButton3->setRotationZ(mButton3->rotationZ() + 5);
}
To change the scope of the state that is currently in effect, before the corresponding controller is deleted due to being out of scope, you can assign a new controller to it:
void ImplicitAnimationExamples::onClicked3() {
    // All implicit animations are disabled for "node" within the scope
    // of this method.
    ImplicitAnimationController allDisabled =
            ImplicitAnimationController::create(mButton4)
                    .enabled(false);

    mButton4->setRotationZ(45.0f); // NOT animated

    // Assigns a new controller which causes the current controller to
    // be deleted and thus ends the scope of the current implicit
    // animation setting.
    allDisabled = ImplicitAnimationController();
    mButton4->setOpacity(0.5f); // Animated
}
You must take caution not to delete an outer scope controller before the inner scope controller. If nesting is broken the results are undefined. Here's an example of how deleting the outer scope controller first can cause undesired behavior.
void ImplicitAnimationExamples::onClicked4() {
    ImplicitAnimationController allDisabled =
            ImplicitAnimationController::create(mButton5)
                    .enabled(false);
    // Animations now disabled (outer scope)
    ImplicitAnimationController allEnabled =
            ImplicitAnimationController::create(mButton5)
                    .enabled(true);
    // Animations now enabled (inner scope)

    allDisabled = ImplicitAnimationController();
    // Error! outer scope is ended before the inner scope!
    // allEnabled exits the scope and restores saved state which was
    // "enabled=false"

    mButton5->setTranslationX(100);
}
After allEnabled goes out of scope the animations will remain disabled because the controller restores the state it was in hen it was created. The following transition will not be animated.
    mButton5->setTranslationX(100);

Controlling implicit animations in QML

Controlling implicit animations from QML uses a different approach more suited for the declarative nature of QML. The approach uses instances of ImplicitAnimationController attached to a controlled node. See UIObject::attachedObjects property for more information about attached objects.

In QML an ImplicitAnimationController can be attached to any UI node. The object has two properties:
  • QString propertyName: This represents a name of the property this instance of controller is used to control; if this property is not specified (or set to null) the controller affects all implicitly animatable properties. in the node the controller is attached to. Once declared this property cannot be changed; any changes to it after node is instantiated will be ignored and a warning will be printed out on console. The specified property name must be one of the implicitly animated properties mentioned above, otherwise the controller will have not effect and a warning will be printed out on a console during instantiation of the document's root node.

  • boolean enabled; which controls whether implicit animations are enabled or not.

A node cannot declare more than one controller with the same propertyName, only the first declared controller for particular property will have effect; the others will be not have any effect and will not represent the state of the implicit animation for the specified property. In addition a warning will be printed out during the creation of the document's root component. Same rule applies to controllers not defining propertyName: only one can be declared for a node.

A node can declare either a single controller with propertyName undeclared (meanning it will control all properties of the node it is attached to) or one or more per-property contorllers. If node does declare both "all-properties" and "single-property" controllers the "all-properties" controller will have effect, and the rest will be ignored and will not represent the state for their declared property correctly. In addition a warning will be printed out on the console when document's root component is created.

Here's an example of how to use an ImplicitAnimationController to control some of a node's properties, using QML.

ToggleButton {
    id: rotationControllerTB
    checked: true
}
Button {
    text: "Hit me!"
    onClicked: {
        translationController.enabled = false;
        // translation is not animated
        translationX = translationX + 10;
        translationController.enabled = true;

        // whether rotation is animated or not is controlled by
        // rotationController.enabled (which is bound to the the toggle button's
        // checked state)
        rotationZ = rotationZ + 10;
    }
    attachedObjects: [
        ImplicitAnimationController {
            id: rotationController
            propertyName: "rotationZ"
            enabled: rotationControllerTB.checked
        },
        ImplicitAnimationController {
            id: translationController
            propertyName: "translationX"
        }
    ]
}
And here's how you can use an ImplicitAnimationController to control all of a node's properties, using QML.
ToggleButton {
    id: allPropertiesTB
    checked: true
}
Button {
    text: "Hit me!"
    onClicked: {
        // whether any of these settings are animated or not is controlled
        // by allPropertiesController.enabled (which is bound to the the toggle
        // button's checked state)
        translationX = translationX + 10;
        rotationZ = rotationZ + 10;
        opacity = opacity * 0.9;
    }
    attachedObjects: [
        ImplicitAnimationController {
            id: allPropertiesController
            enabled: allPropertiesTB.checked
        }
    ]
}

Interaction between implicit animations state changes in C++ and QML

Per-property state changes made by scoped controllers in C++ code are reflected in attached controller objects in QML, unless the controller represents all the properties for the node, in which case, changes on native level aren't reflected.

So, if there's an "opacity" animation controller attached to a node and there's a C++ call that changes the implicit animation state of the "opacity" property, the QML controller's state will be updated (the "enabled" property will be updated accordingly and the enabledChanged signals will be fired).


Overview

Public Functions Index

ImplicitAnimationController ()
ImplicitAnimationController (const ImplicitAnimationController &aCopy)
~ImplicitAnimationController ()
ImplicitAnimationController &operator= (const ImplicitAnimationController &other)

Static Public Functions Index

Buildercreate (VisualNode *target, const QString &propertyName=QString::null)

Public Functions

ImplicitAnimationController ()

Creates a controller which doesn't change any implicit animation settings.

Warning!

Instances of this class must be created on the application thread or a runtime error will occur.

Since:

BlackBerry 10.0.0

ImplicitAnimationController (

A copy constructor for ImplicitAnimationController.

Since:

BlackBerry 10.0.0

~ImplicitAnimationController ()

Destructor.

If this instance was created to change implicit animation state the original state will be restored when the destructor is executed.

Since:

BlackBerry 10.0.0

ImplicitAnimationController & operator= (

Assignment operator for ImplicitAnimationController.

Note that ImplicitAnimationController class is implicitly shared.

Since:

BlackBerry 10.0.0

Static Public Functions

Builder create (

Creates a controller which will affect implicit animations for an object's specified property, or for all properties of the node if the property name isn't specified.

This controller will not have any effect and a warning will be printed out if
  • the specified property cannot be implicitly animated (it is not one of the supported properties specified by the ImplicitAnimationController class)

  • the specified property doesn't exist in the provided node

  • passed target node is null

Warning!

This method must be called from the application thread or a runtime error will occur.

Here's an example for how to control implicit animations for a specific property.
void ImplicitAnimationExamples::onClicked5() {
    ImplicitAnimationController disableOpacityAnimation =
            ImplicitAnimationController::create(mButton6, "opacity")
                    .enabled(false);
    // implicit animations are disabled for the provided node's opacity property for
    // the scope of this method

    mButton6->setTranslationX(100.0f); // animated
    mButton6->setOpacity(0.5f); // not animated
    mButton6->setRotationZ(45.0f); // animated
}
And here's how to control implicit animations for all properties:
void ImplicitAnimationExamples::onClicked6() {
    ImplicitAnimationController allDisabled =
            ImplicitAnimationController::create(mButton7)
                    .enabled(false);
    // all implicit animations are disabled for the scope of this method

    // none of these properties will be animated
    mButton7->setTranslationX(100.0f);
    mButton7->setRotationZ(45.0f);
    mButton7->setOpacity(0.5f);
}
Parameters
target

A non-null VisualNode to control implicit animations for.

propertyName

An optional string parameter specifying the name of the property to be affected or QString::null (the default) indicating that the controller will affect all implicitly animated properties properties for the specified node.

target

A non-null VisualNode to control implicit animations for.

Since:

BlackBerry 10.0.0

Last modified: 2014-03-13

comments powered by Disqus