Magnum::Ui::TextLayerStyleAnimator class new in Git master

Text layer style animator.

Each animation is a transition between two TextLayer styles, with individual properties interpolated with an easing function. BaseLayerStyleAnimator is a matching animator for the BaseLayer.

Setting up an animator instance

The animator doesn't have any shared state or configuration, so it's just about constructing it from a fresh AbstractUserInterface::createAnimator() handle and passing it to setStyleAnimatorInstance().

Ui::TextLayerStyleAnimator& animator = ui.setStyleAnimatorInstance(
    Containers::pointer<Ui::TextLayerStyleAnimator>(ui.createAnimator()));

After that, the animator has to be registered with a concrete layer instance. The animations make use of dynamic styles, so the text layer is expected to have at least one dynamic style enabled with TextLayer::Shared::Configuration::setDynamicStyleCount(). The more dynamic styles are enabled, the more style animations can be running for given layer at the same time, but also more data need to get uploaded to the GPU every frame. Finally, call TextLayer::assignAnimator(TextLayerStyleAnimator&) to assign the animator to the layer instance. Then, assuming AbstractUserInterface::advanceAnimations() is called in an appropriate place, the animator is ready to use.

Ui::TextLayerGL::Shared textLayerShared{glyphCache,
    Ui::TextLayer::Shared::Configuration{}
        
        .setDynamicStyleCount(10) /* adjust as needed */
};
Ui::TextLayer& textLayer = ui.setLayerInstance(
    Containers::pointer<Ui::TextLayerGL>(ui.createLayer(), textLayerShared));



textLayer.assignAnimator(animator);

Unlike builtin layers or layouters, the default UserInterface implementation doesn't implicitly provide a TextLayerStyleAnimator instance.

Creating animations

Assuming an enum is used to index the styles defined in TextLayer::Shared of the associated layer instance, an animation is created by calling create() with the source and target style indices, an easing function from Animation::Easing or a custom one, time at which it's meant to start, its duration, and a DataHandle which the style animation should affect. Here, for example, to fade out a button hover style over half a second:

enum class TextLayerStyle {
    
    Button,
    ButtonHovered,
    
};

Ui::DataHandle buttonText = ;

animator.create(TextLayerStyle::ButtonHovered, TextLayerStyle::Button,
    Animation::Easing::cubicOut, now, 0.5_sec, buttonText);

Internally, once the animation starts playing, the animator allocates a new dynamic style index using TextLayer::allocateDynamicStyle() and switches the style index of given DataHandle to the allocated dynamic style with BaseLayer::setStyle(). During the animation the style data are updated to corresponding interpolation between the source and target styles, equivalent to calling TextLayer::setDynamicStyle(). When the animation stops, the data style index is switched to the target ID specified in create() and the dynamic style index is recycled with TextLayer::recycleDynamicStyle(). Animations that have AnimationFlag::Reverse set animate in the other direction and get switched to the source ID on animation stop instead.

If the animator runs out of dynamic styles, newly started animations are switched to the source style index until a dynamic style is recycled. If no dynamic style gets recycled until the animation stops, the data gets switched directly to the target style at the animation stop with no animation. Again, in case of AnimationFlag::Reverse animations the source and target style is swapped in this case.

The animation interpolates all properties of TextLayerStyleUniform as well as the style padding value. The font, alignment or text feature style properties cannot be animated and thus are set to properties of source style at the start (and target style for AnimationFlag::Reverse animations). If the styles reference a cursor or editing style, the corresponding TextLayerEditingStyleUniform including its padding value, and the selection TextLayerStyleUniform override is animated as well. All style data are fetched from TextLayer::Shared at animation start, meaning that you can reuse existing animations even after the style is updated.

At the moment, only animation between predefined styles is possible.

Resolving style conflicts

In case of styles transitioning in response to input events, it may often happen that a transition happens while another transition is still being animated. To avoid visual artifacts and seemingly random behavior, the animator updates the style assignment only if it didn't change since the animation started.

For example, if a different style is assigned while an animation is playing — either by a different animation or directly with TextLayer::setStyle() — the allocated dynamic style still gets interpolated until the end, but it's no longer assigned to the original data, and once the animation stops, given data isn't switched to the target style either.

From the other side, if external code encounters a dynamic style being assigned to a particular data, it can query TextLayer::dynamicStyleAnimation() to get a corresponding AnimationHandle, and pass it to styles() to know which styles the animation is transitioning between.

The animator doesn't give any ordering guarantees for multiple style animations affecting the same data starting at the same time. Such cases should be rather rare in practice, however.

Animation lifetime and data attachment

As with all other animations, they're implicitly removed once they're played. Pass AnimationFlag::KeepOncePlayed to create() or addFlags() to disable this behavior.

Style animations are associated with data they animate, and thus as soon as the data or node the data is attached to is removed, the animation gets removed as well. If you want to preserve the animation when the data is removed, call attach(AnimationHandle, DataHandle) with DataHandle::Null to detach it from the data before removing.

Additionally the animation is removed also if a different style is assigned to data the animation is attached to while it's playing, and the animation doesn't have AnimationFlag::KeepOncePlayed set. This allows you to create & forget long-running and even infinitely repeating animations and they'll get cleaned up right once they stop affecting data they're attached to.

If you call create() with DataHandle::Null, the animation will still allocate and interpolate a dynamic style, but the style won't be used anywhere. You can then retrieve the dynamic style index with dynamicStyle() and use it for example to make the same style animation on multiple different data. Note that in this case you're also responsible also for switching to the target style once the animation finishes — once the dynamic style is recycled, the same index may get used for arbitrary other style either by this animator or any other code, causing visual bugs.

Base classes

class AbstractVisualLayerStyleAnimator new in Git master
Base for AbstractVisualLayer style animators.

Constructors, destructors, conversion operators

TextLayerStyleAnimator(AnimatorHandle handle) explicit
Constructor.
TextLayerStyleAnimator(const AbstractStyleAnimator&) deleted
Copying is not allowed.
TextLayerStyleAnimator(TextLayerStyleAnimator&&) noexcept
Move constructor.

Public functions

auto operator=(const TextLayerStyleAnimator&) -> TextLayerStyleAnimator& deleted
Copying is not allowed.
auto operator=(TextLayerStyleAnimator&&) -> TextLayerStyleAnimator& noexcept
Move assignment.
auto create(UnsignedInt sourceStyle, UnsignedInt targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, DataHandle data, UnsignedInt repeatCount = 1, AnimationFlags flags = {}) -> AnimationHandle
Create an animation.
template<class StyleIndex>
auto create(StyleIndex sourceStyle, StyleIndex targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, DataHandle data, UnsignedInt repeatCount = 1, AnimationFlags flags = {}) -> AnimationHandle
Create an animation with a style index in a concrete enum type.
auto create(UnsignedInt sourceStyle, UnsignedInt targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, DataHandle data, AnimationFlags flags) -> AnimationHandle
Create an animation.
template<class StyleIndex>
auto create(StyleIndex sourceStyle, StyleIndex targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, DataHandle data, AnimationFlags flags) -> AnimationHandle
Create an animation with a style index in a concrete enum type.
auto create(UnsignedInt sourceStyle, UnsignedInt targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, LayerDataHandle data, UnsignedInt repeatCount = 1, AnimationFlags flags = {}) -> AnimationHandle
Create an animation assuming the data it's attached to belongs to the layer the animator is registered with.
template<class StyleIndex>
auto create(StyleIndex sourceStyle, StyleIndex targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, LayerDataHandle data, UnsignedInt repeatCount = 1, AnimationFlags flags = {}) -> AnimationHandle
Create an animation with a style index in a concrete enum type assuming the data it's attached to belongs to the layer the animator is registered with.
auto create(UnsignedInt sourceStyle, UnsignedInt targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, LayerDataHandle data, AnimationFlags flags) -> AnimationHandle
Create an animation assuming the data it's attached to belongs to the layer the animator is registered with.
template<class StyleIndex>
auto create(StyleIndex sourceStyle, StyleIndex targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, LayerDataHandle data, AnimationFlags flags) -> AnimationHandle
Create an animation with a style index in a concrete enum type assuming the data it's attached to belongs to the layer the animator is registered with.
void remove(AnimationHandle handle)
Remove an animation.
void remove(AnimatorDataHandle handle)
Remove an animation assuming it belongs to this animator.
auto easing(AnimationHandle handle) -> auto
Animation easing function.
auto easing(AnimatorDataHandle handle) -> auto
Animation easing function assuming it belongs to this animator.
auto advance(Nanoseconds time, Containers::MutableBitArrayView activeStorage, Containers::MutableBitArrayView startedStorage, Containers::MutableBitArrayView stoppedStorage, const Containers::StridedArrayView1D<Float>& factorStorage, Containers::MutableBitArrayView removeStorage, Containers::ArrayView<TextLayerStyleUniform> dynamicStyleUniforms, Containers::MutableBitArrayView dynamicStyleCursorStyles, Containers::MutableBitArrayView dynamicStyleSelectionStyles, const Containers::StridedArrayView1D<Vector4>& dynamicStylePaddings, Containers::ArrayView<TextLayerEditingStyleUniform> dynamicEditingStyleUniforms, const Containers::StridedArrayView1D<Vector4>& dynamicEditingStylePaddings, const Containers::StridedArrayView1D<UnsignedInt>& dataStyles) -> TextLayerStyleAnimatorUpdates
Advance the animations.

Function documentation

Magnum::Ui::TextLayerStyleAnimator::TextLayerStyleAnimator(AnimatorHandle handle) explicit

Constructor.

Parameters
handle Handle returned by AbstractUserInterface::createAnimator()

Magnum::Ui::TextLayerStyleAnimator::TextLayerStyleAnimator(TextLayerStyleAnimator&&) noexcept

Move constructor.

Performs a destructive move, i.e. the original object isn't usable afterwards anymore.

AnimationHandle Magnum::Ui::TextLayerStyleAnimator::create(UnsignedInt sourceStyle, UnsignedInt targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, DataHandle data, UnsignedInt repeatCount = 1, AnimationFlags flags = {})

Create an animation.

Parameters
sourceStyle Style index to animate from
targetStyle Style index to animate to
easing Easing function between 0.0f and 1.0f, used for all style uniform values as well as the padding. Pick one from Animation::Easing or supply a custom one.
start Time at which the animation starts. Use Nanoseconds::max() for reserving an animation that doesn't get played until play() is called on it.
duration Duration of a single play of the animation
data Data the animation is attached to. Use DataHandle::Null to create an animation that isn't attached to any data.
repeatCount Repeat count. Use 0 for an indefinitely repeating animation.
flags Flags

Expects that TextLayer::assignAnimator(TextLayerStyleAnimator&) has been already called for this animator, that both sourceStyle and targetStyle are less than TextLayer::Shared::styleCount() (not TextLayer::Shared::totalStyleCount() — the style animation is not allowed to use the dynamic style indices) and that easing is not nullptr.

Delegates to AbstractAnimator::create(Nanoseconds, Nanoseconds, DataHandle, UnsignedInt, AnimationFlags), see its documentation for more information.

The animation affects the TextLayerStyleUniform and the padding value, if it differs between the styles. The animated dynamic style is initialized from font, alignment and features from styleSrc, if styleDst has them different, they don't affect the animation in any way. If given style references a cursor or editing style, it affects also the corresponding TextLayerEditingStyleUniform, the editing padding value, if it differs between the editing styles, and the TextLayerStyleUniform override for selected portions of the text, if referenced.

The actually animated style values are queried from TextLayer::Shared only when the animation starts, thus it isn't needed for TextLayer::Shared::setStyle() / setEditingStyle() to be called at animation creation time. Calling TextLayer::create() to have a data to attach to however requires a style to be set.

template<class StyleIndex>
AnimationHandle Magnum::Ui::TextLayerStyleAnimator::create(StyleIndex sourceStyle, StyleIndex targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, DataHandle data, UnsignedInt repeatCount = 1, AnimationFlags flags = {})

Create an animation with a style index in a concrete enum type.

Casts sourceStyle and targetStyle to UnsignedInt and delegates to create(UnsignedInt, UnsignedInt, Float(*)(Float), Nanoseconds, Nanoseconds, DataHandle, UnsignedInt, AnimationFlags).

AnimationHandle Magnum::Ui::TextLayerStyleAnimator::create(UnsignedInt sourceStyle, UnsignedInt targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, DataHandle data, AnimationFlags flags)

Create an animation.

Same as calling create(UnsignedInt, UnsignedInt, Float(*)(Float), Nanoseconds, Nanoseconds, DataHandle, UnsignedInt, AnimationFlags) with repeatCount set to 1.

template<class StyleIndex>
AnimationHandle Magnum::Ui::TextLayerStyleAnimator::create(StyleIndex sourceStyle, StyleIndex targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, DataHandle data, AnimationFlags flags)

Create an animation with a style index in a concrete enum type.

Casts sourceStyle and targetStyle to UnsignedInt and delegates to create(UnsignedInt, UnsignedInt, Float(*)(Float), Nanoseconds, Nanoseconds, DataHandle, AnimationFlags).

AnimationHandle Magnum::Ui::TextLayerStyleAnimator::create(UnsignedInt sourceStyle, UnsignedInt targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, LayerDataHandle data, UnsignedInt repeatCount = 1, AnimationFlags flags = {})

Create an animation assuming the data it's attached to belongs to the layer the animator is registered with.

Compared to create(UnsignedInt, UnsignedInt, Float(*)(Float), Nanoseconds, Nanoseconds, DataHandle, UnsignedInt, AnimationFlags) delegates to AbstractAnimator::create(Nanoseconds, Nanoseconds, LayerDataHandle, UnsignedInt, AnimationFlags) instead.

template<class StyleIndex>
AnimationHandle Magnum::Ui::TextLayerStyleAnimator::create(StyleIndex sourceStyle, StyleIndex targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, LayerDataHandle data, UnsignedInt repeatCount = 1, AnimationFlags flags = {})

Create an animation with a style index in a concrete enum type assuming the data it's attached to belongs to the layer the animator is registered with.

Casts sourceStyle and targetStyle to UnsignedInt and delegates to create(UnsignedInt, UnsignedInt, Float(*)(Float), Nanoseconds, Nanoseconds, LayerDataHandle, UnsignedInt, AnimationFlags).

AnimationHandle Magnum::Ui::TextLayerStyleAnimator::create(UnsignedInt sourceStyle, UnsignedInt targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, LayerDataHandle data, AnimationFlags flags)

Create an animation assuming the data it's attached to belongs to the layer the animator is registered with.

Same as calling create(UnsignedInt, UnsignedInt, Float(*)(Float), Nanoseconds, Nanoseconds, LayerDataHandle, UnsignedInt, AnimationFlags) with repeatCount set to 1.

template<class StyleIndex>
AnimationHandle Magnum::Ui::TextLayerStyleAnimator::create(StyleIndex sourceStyle, StyleIndex targetStyle, Float(*)(Float) easing, Nanoseconds start, Nanoseconds duration, LayerDataHandle data, AnimationFlags flags)

Create an animation with a style index in a concrete enum type assuming the data it's attached to belongs to the layer the animator is registered with.

Casts sourceStyle and targetStyle to UnsignedInt and delegates to create(UnsignedInt, UnsignedInt, Float(*)(Float), Nanoseconds, Nanoseconds, LayerDataHandle, UnsignedInt, AnimationFlags).

void Magnum::Ui::TextLayerStyleAnimator::remove(AnimationHandle handle)

Remove an animation.

Expects that handle is valid. Recycles a dynamic style used by given animation with TextLayer::recycleDynamicStyle() and delegates to AbstractAnimator::remove(AnimationHandle), see its documentation for more information.

void Magnum::Ui::TextLayerStyleAnimator::remove(AnimatorDataHandle handle)

Remove an animation assuming it belongs to this animator.

Compared to remove(AnimationHandle) delegates to AbstractAnimator::remove(AnimatorDataHandle) instead.

auto Magnum::Ui::TextLayerStyleAnimator::easing(AnimationHandle handle)

Animation easing function.

Expects that handle is valid. The returned pointer is never nullptr.

auto Magnum::Ui::TextLayerStyleAnimator::easing(AnimatorDataHandle handle)

Animation easing function assuming it belongs to this animator.

Like easing(AnimationHandle) const but without checking that handle indeed belongs to this animator. See its documentation for more information.

TextLayerStyleAnimatorUpdates Magnum::Ui::TextLayerStyleAnimator::advance(Nanoseconds time, Containers::MutableBitArrayView activeStorage, Containers::MutableBitArrayView startedStorage, Containers::MutableBitArrayView stoppedStorage, const Containers::StridedArrayView1D<Float>& factorStorage, Containers::MutableBitArrayView removeStorage, Containers::ArrayView<TextLayerStyleUniform> dynamicStyleUniforms, Containers::MutableBitArrayView dynamicStyleCursorStyles, Containers::MutableBitArrayView dynamicStyleSelectionStyles, const Containers::StridedArrayView1D<Vector4>& dynamicStylePaddings, Containers::ArrayView<TextLayerEditingStyleUniform> dynamicEditingStyleUniforms, const Containers::StridedArrayView1D<Vector4>& dynamicEditingStylePaddings, const Containers::StridedArrayView1D<UnsignedInt>& dataStyles)

Advance the animations.

Parameters
time in Time to which to advance
activeStorage in/out Storage for the animator to put a mask of active animations into
startedStorage in/out Storage for the animator to put a mask of started animations into
stoppedStorage in/out Storage for the animator to put a mask of stopped animations into
factorStorage in/out Storage for the animator to put animation interpolation factors into
removeStorage in/out Storage for the animator to put a mask of animations to remove into
dynamicStyleUniforms in/out Uniforms to animate indexed by dynamic style ID or dynamic editing style text uniform ID
dynamicStyleCursorStyles in/out Cursor style association to animate indexed by dynamic style ID
dynamicStyleSelectionStyles in/out Selection style association to animate indexed by dynamic style ID
dynamicStylePaddings in/out Paddings to animate indexed by dynamic style ID
dynamicEditingStyleUniforms in/out Editing uniforms to animate indexed by dynamic editing style ID
dynamicEditingStylePaddings in/out Editing paddings to animate indexed by dynamic editing style ID
dataStyles in/out Style assignments of all layer data indexed by data ID
Returns Style properties that were affected by the animation

Used internally from TextLayer::advanceAnimations(Nanoseconds, Containers::MutableBitArrayView, Containers::MutableBitArrayView, Containers::MutableBitArrayView, const Containers::StridedArrayView1D<Float>&, Containers::MutableBitArrayView, const Containers::Iterable<AbstractStyleAnimator>&), which is called from AbstractUserInterface::advanceAnimations(). Exposed just for testing purposes, there should be no need to call this function directly and doing so may cause internal AbstractUserInterface state update to misbehave.

Expects that size of activeStorage, startedStorage, stoppedStorage, factorStorage and removeStorage matches capacity(), their contents get filled by update() internally. If the layer the animator is associated with doesn't contain editing styles, the dynamicStyleUniforms, dynamicStyleCursorStyles, dynamicStyleSelectionStyles and dynamicStylePaddings, are expected to have a size of TextLayer::Shared::dynamicStyleCount() and the dynamicEditingStyleUniforms and dynamicEditingStylePaddings views are expected to be empty. If the layer contains editing styles, the dynamicStyleUniforms is expected to be three times as large as TextLayer::Shared::dynamicStyleCount(), and the dynamicEditingStyleUniforms and dynamicEditingStylePaddings views twice as large as TextLayer::Shared::dynamicStyleCount() instead. The dataStyles view should be large enough to contain any valid layer data ID. TextLayer::Shared::setStyle(), and also TextLayer::Shared::setEditingStyle() if editing styles are enabled, is expected to be already called for the layer this animator is assigned to.