Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,387 @@
// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#include "animations.h"
#include "../cview.h"
#include "../cframe.h"
#include "../controls/ccontrol.h"
#include <cassert>
#include <cmath>
namespace VSTGUI {
namespace Animation {
//------------------------------------------------------------------------
/*! @defgroup AnimationTargets Animation Targets
* @ingroup animation
*/
//------------------------------------------------------------------------
/** @class AlphaValueAnimation
see @ref page_animation Support */
//-----------------------------------------------------------------------------
AlphaValueAnimation::AlphaValueAnimation (float endValue, bool forceEndValueOnFinish)
: startValue (0.f)
, endValue (endValue)
, forceEndValueOnFinish (forceEndValueOnFinish)
{
}
//-----------------------------------------------------------------------------
void AlphaValueAnimation::animationStart (CView* view, IdStringPtr name)
{
startValue = view->getAlphaValue ();
}
//-----------------------------------------------------------------------------
void AlphaValueAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
float alpha = startValue + (endValue - startValue) * pos;
view->setAlphaValue (alpha);
}
//-----------------------------------------------------------------------------
void AlphaValueAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
if (!wasCanceled || forceEndValueOnFinish)
view->setAlphaValue (endValue);
}
//-----------------------------------------------------------------------------
/** @class ViewSizeAnimation
see @ref page_animation Support */
//-----------------------------------------------------------------------------
ViewSizeAnimation::ViewSizeAnimation (const CRect& inNewRect, bool forceEndValueOnFinish)
: newRect (inNewRect)
, forceEndValueOnFinish (forceEndValueOnFinish)
{
}
//-----------------------------------------------------------------------------
void ViewSizeAnimation::animationStart (CView* view, IdStringPtr name)
{
startRect = view->getViewSize ();
}
//-----------------------------------------------------------------------------
void ViewSizeAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
if (!wasCanceled || forceEndValueOnFinish)
{
if (view->getViewSize () != newRect)
{
view->invalid ();
view->setViewSize (newRect);
view->setMouseableArea (view->getViewSize ());
view->invalid ();
}
}
}
//-----------------------------------------------------------------------------
void ViewSizeAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
CRect r;
r.left = (int32_t)(startRect.left + ((newRect.left - startRect.left) * pos));
r.right = (int32_t)(startRect.right + ((newRect.right - startRect.right) * pos));
r.top = (int32_t)(startRect.top + ((newRect.top - startRect.top) * pos));
r.bottom = (int32_t)(startRect.bottom + ((newRect.bottom - startRect.bottom) * pos));
if (view->getViewSize () != r)
{
view->invalid ();
view->setViewSize (r);
view->setMouseableArea (view->getViewSize ());
view->invalid ();
}
}
//-----------------------------------------------------------------------------
/** @class ExchangeViewAnimation
see @ref page_animation Support */
//-----------------------------------------------------------------------------
ExchangeViewAnimation::ExchangeViewAnimation (CView* oldView, CView* newView, AnimationStyle style)
: newView (newView)
, viewToRemove (oldView)
, style (style)
{
vstgui_assert (newView->isAttached () == false);
vstgui_assert (viewToRemove->isAttached ());
if (auto parent = viewToRemove->getParentView ()->asViewContainer ())
parent->addView (newView);
init ();
}
//-----------------------------------------------------------------------------
ExchangeViewAnimation::~ExchangeViewAnimation () noexcept
{
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::updateViewSize (CView* view, const CRect& rect)
{
view->invalid ();
view->setViewSize (rect);
view->setMouseableArea (rect);
view->invalid ();
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::init ()
{
if (style == kAlphaValueFade)
{
oldViewAlphaValueStart = viewToRemove->getAlphaValue ();
newViewAlphaValueEnd = newView->getAlphaValue ();
newView->setAlphaValue (0.f);
}
else
{
destinationRect = viewToRemove->getViewSize ();
switch (style)
{
case kAlphaValueFade: break;
case kPushInFromLeft:
{
doPushInFromLeft (0.f);
break;
}
case kPushInFromRight:
{
doPushInFromRight (0.f);
break;
}
case kPushInFromTop:
{
doPushInFromTop (0.f);
break;
}
case kPushInFromBottom:
{
doPushInFromBottom (0.f);
break;
}
case kPushInOutFromLeft:
{
doPushInOutFromLeft (0.f);
break;
}
case kPushInOutFromRight:
{
doPushInOutFromRight (0.f);
break;
}
}
}
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doAlphaFade (float pos)
{
float alpha = oldViewAlphaValueStart - (oldViewAlphaValueStart * pos);
viewToRemove->setAlphaValue (alpha);
alpha = newViewAlphaValueEnd * pos;
newView->setAlphaValue (alpha);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInFromLeft (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord leftOrigin = destinationRect.left;
CCoord offset = viewSize.getWidth () * (1.f - pos);
viewSize.offset (-viewSize.left, 0);
viewSize.offset (leftOrigin - offset, 0);
updateViewSize (newView, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInFromRight (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord rightOrigin = destinationRect.left + destinationRect.getWidth ();
CCoord offset = viewSize.getWidth () * pos;
viewSize.offset (-viewSize.left, 0);
viewSize.offset (rightOrigin - offset, 0);
updateViewSize (newView, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInFromTop (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord topOrigin = destinationRect.top;
CCoord offset = viewSize.getHeight () * (1.f - pos);
viewSize.offset (0, -viewSize.top);
viewSize.offset (0, topOrigin - offset);
updateViewSize (newView, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInFromBottom (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord bottomOrigin = destinationRect.top + destinationRect.getHeight ();
CCoord offset = viewSize.getHeight () * pos;
viewSize.offset (0, -viewSize.top);
viewSize.offset (0, bottomOrigin - offset);
updateViewSize (newView, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInOutFromLeft (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord offset = viewSize.getWidth () * (1.f - pos);
viewSize.offset (-viewSize.left, 0);
viewSize.offset (destinationRect.left - offset, 0);
updateViewSize (newView, viewSize);
offset = viewToRemove->getWidth () * pos;
viewSize = destinationRect;
viewSize.offset (offset, 0);
updateViewSize (viewToRemove, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::doPushInOutFromRight (float pos)
{
CRect viewSize (newView->getViewSize ());
CCoord offset = viewSize.getWidth () * pos;
viewSize.offset (-viewSize.left, 0);
viewSize.offset ((destinationRect.left + destinationRect.getWidth ()) - offset, 0);
updateViewSize (newView, viewSize);
offset = viewToRemove->getWidth () * pos;
viewSize = destinationRect;
viewSize.offset (-offset, 0);
updateViewSize (viewToRemove, viewSize);
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::animationStart (CView* view, IdStringPtr name)
{
#if DEBUG
CViewContainer* parent = viewToRemove->getParentView ()->asViewContainer ();
vstgui_assert (view == parent);
#endif
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
switch (style)
{
case kAlphaValueFade:
{
doAlphaFade (pos);
break;
}
case kPushInFromLeft:
{
doPushInFromLeft (pos);
break;
}
case kPushInFromRight:
{
doPushInFromRight (pos);
break;
}
case kPushInFromTop:
{
doPushInFromTop (pos);
break;
}
case kPushInFromBottom:
{
doPushInFromBottom (pos);
break;
}
case kPushInOutFromLeft:
{
doPushInOutFromLeft (pos);
break;
}
case kPushInOutFromRight:
{
doPushInOutFromRight (pos);
break;
}
}
}
//-----------------------------------------------------------------------------
void ExchangeViewAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
animationTick (nullptr, nullptr, 1.f);
if (auto viewContainer = viewToRemove->getParentView ()->asViewContainer ())
{
viewContainer->removeView (viewToRemove);
}
}
//-----------------------------------------------------------------------------
/** @class ControlValueAnimation
see @ref page_animation Support */
//-----------------------------------------------------------------------------
ControlValueAnimation::ControlValueAnimation (float endValue, bool forceEndValueOnFinish)
: startValue (0.f)
, endValue (endValue)
, forceEndValueOnFinish (forceEndValueOnFinish)
{
}
//-----------------------------------------------------------------------------
void ControlValueAnimation::animationStart (CView* view, IdStringPtr name)
{
auto* control = dynamic_cast<CControl*> (view);
if (control)
startValue = control->getValue ();
}
//-----------------------------------------------------------------------------
void ControlValueAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
auto* control = dynamic_cast<CControl*> (view);
if (control)
{
float value = startValue + (endValue - startValue) * pos;
control->setValue (value);
if (control->isDirty ())
control->invalid ();
}
}
//-----------------------------------------------------------------------------
void ControlValueAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
auto* control = dynamic_cast<CControl*> (view);
if (control)
{
if (!wasCanceled || forceEndValueOnFinish)
control->setValue (endValue);
}
}
//------------------------------------------------------------------------
FuncAnimation::FuncAnimation (StartFunc&& start, TickFunc&& tick, FinishedFunc&& finished)
: start (std::move (start)), tick (std::move (tick)), finished (std::move (finished))
{
}
//------------------------------------------------------------------------
void FuncAnimation::animationStart (CView* view, IdStringPtr name) { start (view, name); }
//------------------------------------------------------------------------
void FuncAnimation::animationTick (CView* view, IdStringPtr name, float pos)
{
tick (view, name, pos);
}
//------------------------------------------------------------------------
void FuncAnimation::animationFinished (CView* view, IdStringPtr name, bool wasCanceled)
{
finished (view, name, wasCanceled);
}
}} // namespaces
@@ -0,0 +1,144 @@
// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#pragma once
#include "../vstguifwd.h"
#include "ianimationtarget.h"
#include "../crect.h"
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @brief animates the alpha value of the view
/// @ingroup AnimationTargets
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class AlphaValueAnimation : public IAnimationTarget, public NonAtomicReferenceCounted
{
public:
AlphaValueAnimation (float endValue, bool forceEndValueOnFinish = false);
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
protected:
float startValue;
float endValue;
bool forceEndValueOnFinish;
};
//-----------------------------------------------------------------------------
/// @brief animates the view size of the view
/// @ingroup AnimationTargets
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class ViewSizeAnimation : public IAnimationTarget, public NonAtomicReferenceCounted
{
public:
ViewSizeAnimation (const CRect& newRect, bool forceEndValueOnFinish = false);
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
protected:
CRect startRect;
CRect newRect;
bool forceEndValueOnFinish;
};
//-----------------------------------------------------------------------------
/// @brief exchange a view by another view with an animation
/// @ingroup AnimationTargets
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class ExchangeViewAnimation : public IAnimationTarget, public NonAtomicReferenceCounted
{
public:
enum AnimationStyle {
kAlphaValueFade = 0,
kPushInFromLeft,
kPushInFromRight,
kPushInFromTop,
kPushInFromBottom,
kPushInOutFromLeft,
kPushInOutFromRight
};
/** oldView must be a subview of the animation view */
ExchangeViewAnimation (CView* oldView, CView* newView, AnimationStyle style = kAlphaValueFade);
~ExchangeViewAnimation () noexcept override;
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
protected:
void init ();
void doAlphaFade (float pos);
void doPushInFromLeft (float pos);
void doPushInFromRight (float pos);
void doPushInFromTop (float pos);
void doPushInFromBottom (float pos);
void doPushInOutFromLeft (float pos);
void doPushInOutFromRight (float pos);
void updateViewSize (CView* view, const CRect& rect);
SharedPointer<CView> newView;
SharedPointer<CView> viewToRemove;
AnimationStyle style;
float newViewAlphaValueEnd;
float oldViewAlphaValueStart;
CRect destinationRect;
};
//-----------------------------------------------------------------------------
/// @brief animates the value of a CControl
/// @ingroup AnimationTargets
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class ControlValueAnimation : public IAnimationTarget, public NonAtomicReferenceCounted
{
public:
ControlValueAnimation (float endValue, bool forceEndValueOnFinish = false);
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
protected:
float startValue;
float endValue;
bool forceEndValueOnFinish;
};
//------------------------------------------------------------------------
/// @brief animation via custom functions
/// @ingroup AnimationTargets
/// @ingroup new_in_4_14
//------------------------------------------------------------------------
class FuncAnimation : public IAnimationTarget,
public NonAtomicReferenceCounted
{
public:
using StartFunc = std::function<void (CView*, IdStringPtr)>;
using TickFunc = std::function<void (CView*, IdStringPtr, float)>;
using FinishedFunc = std::function<void (CView*, IdStringPtr, bool)>;
FuncAnimation (StartFunc&& start, TickFunc&& tick, FinishedFunc&& finished);
void animationStart (CView* view, IdStringPtr name) override;
void animationTick (CView* view, IdStringPtr name, float pos) override;
void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) override;
private:
StartFunc start;
TickFunc tick;
FinishedFunc finished;
};
} // Animation
} // VSTGUI
@@ -0,0 +1,374 @@
// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
/**
@page page_animation Animations
VSTGUI version 4 adds simple to use view animation support.
The source can be found under /lib/animation/
@section the_animator The Animator
Every @link VSTGUI::CFrame::getAnimator CFrame @endlink object can have one @link VSTGUI::Animation::Animator Animator @endlink object which runs animations at 60 Hz.
The animator is responsible for running animations.
You can add and remove animations.
Animations are identified by a view and a name.
To add an animation you just call @link VSTGUI::CView::addAnimation CView::addAnimation (name, target, timing)@endlink.
The animation will start immediately and will automatically be removed if it has finished.
If you want to stop it before it has finished you can use @link VSTGUI::CView::removeAnimation CView::removeAnimation (name)@endlink.
You can also stop all animations for a view with @link VSTGUI::CView::removeAllAnimations CView::removeAllAnimations ()@endlink.
The animator is the owner of the target and timing function objects and will destroy these objects when the animation has finished.
This means that the animator will call delete on these objects or if they are inherited from CBaseObject it will call forget() on them.
@section the_animation The Animation
An animation is made up by an @link VSTGUI::Animation::IAnimationTarget IAnimationTarget @endlink and an @link VSTGUI::Animation::ITimingFunction ITimingFunction @endlink object.
@subsection animation_target The Animation Target
The animation target is responsible for changing the view from one state to another state.
The animation target interface consists of 3 methods:
- @link VSTGUI::Animation::IAnimationTarget::animationStart animationStart (view, name) @endlink
- @link VSTGUI::Animation::IAnimationTarget::animationTick animationTick (view, name, pos) @endlink
- @link VSTGUI::Animation::IAnimationTarget::animationFinished animationFinished (view, name, wasCanceled) @endlink
All these methods have the view and the animation name as arguments to identify the animation within the target.
The animationTick method in addition has the normalized animation position as argument and the animationFinished method has a bool argument indicating if the animation was canceled.
see @link AnimationTargets included animation target classes @endlink
@subsection animation_timing The Animation Timing Function
the animation timing function maps elapsed time to a normalized position.
see @link AnimationTimingFunctions included animation timing function classes @endlink
@section simple_example Simple Usage Example
In this example the custom view animates it's alpha value when the mouse moves inside or outside the view.
@code
using namespace VSTGUI::Animation;
class MyView : public CView
{
public:
MyView (const CRect& r) : CView (r) { setAlphaValue (0.5f); }
CMouseEventResult onMouseEntered (CPoint &where, const CButtonState& buttons)
{
// this adds an animation which takes 200 ms to make a linear alpha fade from the current value to 1
addAnimation ("AlphaValueAnimation", new AlphaValueAnimation (1.f), new LinearTimingFunction (200));
return kMouseEventHandled;
}
CMouseEventResult onMouseExited (CPoint &where, const CButtonState& buttons)
{
// this adds an animation which takes 200 ms to make a linear alpha fade from the current value to 0.5
addAnimation ("AlphaValueAnimation", new AlphaValueAnimation (0.5f), new LinearTimingFunction (200));
return kMouseEventHandled;
}
void draw (CDrawContext* context)
{
// ... any drawing code here
}
};
@endcode
*/
//------------------------------------------------------------------------
/*! @defgroup animation Animation
see @ref page_animation
*/
//-----------------------------------------------------------------------------
#include "animator.h"
#include "ianimationtarget.h"
#include "itimingfunction.h"
#include "../cvstguitimer.h"
#include "../cview.h"
#include "../dispatchlist.h"
#include "../platform/platformfactory.h"
#include <list>
#define DEBUG_LOG 0 // DEBUG
namespace VSTGUI {
namespace Animation {
///@cond ignore
namespace Detail {
//-----------------------------------------------------------------------------
class Timer : public NonAtomicReferenceCounted
{
public:
static void addAnimator (Animator* animator)
{
getInstance ()->animators.emplace_back (animator);
#if DEBUG_LOG
DebugPrint ("Animator added: %p\n", animator);
#endif
}
static void removeAnimator (Animator* animator)
{
if (gInstance)
{
if (getInstance ()->inTimer)
{
gInstance->toRemove.emplace_back (animator);
}
else
{
#if DEBUG_LOG
DebugPrint ("Animator removed: %p\n", animator);
#endif
gInstance->animators.remove (animator);
if (gInstance->animators.empty ())
{
gInstance->forget ();
gInstance = nullptr;
}
}
}
}
protected:
static Timer* getInstance ()
{
if (gInstance == nullptr)
gInstance = new Timer;
return gInstance;
}
Timer ()
: inTimer (false)
{
#if DEBUG_LOG
DebugPrint ("Animation timer started\n");
#endif
timer = new CVSTGUITimer ([this] (CVSTGUITimer*) {
onTimer ();
}, 1000/60); // 60 Hz
}
~Timer () noexcept override
{
#if DEBUG_LOG
DebugPrint ("Animation timer stopped\n");
#endif
timer->forget ();
gInstance = nullptr;
}
void onTimer ()
{
inTimer = true;
auto guard = shared (this);
#if DEBUG_LOG
DebugPrint ("Current Animators : %d\n", animators.size ());
#endif
for (auto& animator : animators)
animator->onTimer ();
inTimer = false;
for (auto& animator : toRemove)
removeAnimator (animator);
toRemove.clear ();
}
CVSTGUITimer* timer;
using Animators = std::list<Animator*>;
Animators animators;
Animators toRemove;
bool inTimer;
static Timer* gInstance;
};
Timer* Timer::gInstance = nullptr;
//-----------------------------------------------------------------------------
class Animation : public NonAtomicReferenceCounted
{
public:
Animation (CView* view, const std::string& name, IAnimationTarget* at, ITimingFunction* t,
DoneFunction&& notification, bool notifyOnCancel);
~Animation () noexcept override;
std::string name;
SharedPointer<CView> view;
IAnimationTarget* animationTarget;
ITimingFunction* timingFunction;
DoneFunction notification;
uint64_t startTime {0};
float lastPos {-1.};
bool done {false};
bool notifyOnCancel;
};
//-----------------------------------------------------------------------------
Animation::Animation (CView* view, const std::string& name, IAnimationTarget* at,
ITimingFunction* t, DoneFunction&& notification, bool notifyOnCancel)
: name (name)
, view (view)
, animationTarget (at)
, timingFunction (t)
, notification (std::move (notification))
, notifyOnCancel (notifyOnCancel)
{
}
//-----------------------------------------------------------------------------
Animation::~Animation () noexcept
{
if (notification)
notification (view, name.c_str (), animationTarget);
if (auto obj = dynamic_cast<IReference*> (animationTarget))
obj->forget ();
else
delete animationTarget;
if (auto obj = dynamic_cast<IReference*> (timingFunction))
obj->forget ();
else
delete timingFunction;
}
} // Detail
//-----------------------------------------------------------------------------
struct Animator::Impl
{
DispatchList<SharedPointer<Detail::Animation>> animations;
};
///@endcond
//-----------------------------------------------------------------------------
Animator::Animator ()
{
pImpl = std::unique_ptr<Impl> (new Impl ());
}
//-----------------------------------------------------------------------------
Animator::~Animator () noexcept
{
Detail::Timer::removeAnimator (this);
}
//-----------------------------------------------------------------------------
void Animator::addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target,
ITimingFunction* timingFunction, DoneFunction notification,
bool notifyOnCancel)
{
if (pImpl->animations.empty ())
Detail::Timer::addAnimator (this);
removeAnimation (view, name);
pImpl->animations.add (makeOwned<Detail::Animation> (view, name, target, timingFunction,
std::move (notification), notifyOnCancel));
#if DEBUG_LOG
DebugPrint ("new animation added: %p - %s\n", view, name);
#endif
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
//-----------------------------------------------------------------------------
void Animator::addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target, ITimingFunction* timingFunction, CBaseObject* notificationObject)
{
DoneFunction notification;
if (notificationObject)
{
SharedPointer<CBaseObject> nObj (notificationObject);
notification = [nObj] (CView* view, const IdStringPtr name, IAnimationTarget* target) {
FinishedMessage fmsg (view, name, target);
nObj->notify (&fmsg, kMsgAnimationFinished);
};
}
addAnimation (view, name, target, timingFunction, std::move (notification));
}
#endif
//-----------------------------------------------------------------------------
void Animator::removeAnimation (CView* view, IdStringPtr name)
{
pImpl->animations.forEach ([&] (const SharedPointer<Detail::Animation>& animation) {
if (animation->view == view && animation->name == name)
{
#if DEBUG_LOG
DebugPrint ("animation removed: %p - %s\n", view, name);
#endif
if (animation->done == false)
{
animation->done = true;
animation->animationTarget->animationFinished (view, name, true);
}
if (!animation->notifyOnCancel)
animation->notification = nullptr;
pImpl->animations.remove (animation);
}
});
}
//-----------------------------------------------------------------------------
void Animator::removeAnimations (CView* view)
{
pImpl->animations.forEach ([&] (const SharedPointer<Detail::Animation>& animation) {
if (animation->view == view)
{
#if DEBUG_LOG
DebugPrint ("animation removed: %p - %s\n", view, animation->name.data ());
#endif
if (animation->done == false)
{
animation->done = true;
animation->animationTarget->animationFinished (view, animation->name.data (), true);
}
pImpl->animations.remove (animation);
}
});
}
//-----------------------------------------------------------------------------
void Animator::onTimer ()
{
auto selfGuard = shared (this);
auto currentTicks = getPlatformFactory ().getTicks ();
pImpl->animations.forEach ([&] (SharedPointer<Detail::Animation>& animation) {
if (animation->startTime == 0)
{
#if DEBUG_LOG
DebugPrint ("animation start: %p - %s\n", animation->view.cast<CView>(), animation->name.data ());
#endif
animation->animationTarget->animationStart (animation->view, animation->name.data ());
animation->startTime = currentTicks;
}
uint32_t time = static_cast<uint32_t> (currentTicks - animation->startTime);
float pos = animation->timingFunction->getPosition (time);
if (pos != animation->lastPos)
{
animation->animationTarget->animationTick (animation->view, animation->name.data (), pos);
animation->lastPos = pos;
}
if (animation->timingFunction->isDone (time))
{
animation->done = true;
animation->animationTarget->animationFinished (animation->view, animation->name.data (), false);
#if DEBUG_LOG
DebugPrint ("animation finished: %p - %s\n", animation->view.cast<CView>(), animation->name.data ());
#endif
pImpl->animations.remove (animation);
}
});
if (pImpl->animations.empty ())
Detail::Timer::removeAnimator (this);
}
#if VSTGUI_ENABLE_DEPRECATED_METHODS
IdStringPtr kMsgAnimationFinished = "kMsgAnimationFinished";
#endif
}} // namespaces
@@ -0,0 +1,95 @@
// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#pragma once
#include "../vstguifwd.h"
#include <string>
#include <functional>
#include <memory>
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @brief Animation runner
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class Animator : public NonAtomicReferenceCounted
{
public:
//-----------------------------------------------------------------------------
/// @name Adding and removing Animations
//-----------------------------------------------------------------------------
//@{
VSTGUI_DEPRECATED(
/** adds an animation.
Animation and timingFunction is now owned by the animator.
An already running animation for view with name will be canceled.
If a notificationObject is supplied, it will be notified when the animation has finished @see FinishedMessage.
*/
void addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target, ITimingFunction* timingFunction, CBaseObject* notificationObject);)
/** adds an animation.
Animation and timingFunction is now owned by the animator.
An already running animation for view with name will be canceled.
The notification function will be called when the animation has finished or on cancelation
of the animation if notifyOnCancel is true (new in 4.11)
*/
void addAnimation (CView* view, IdStringPtr name, IAnimationTarget* target,
ITimingFunction* timingFunction, DoneFunction notification = nullptr,
bool notifyOnCancel = false);
/** removes an animation.
If animation has the IReference interface forget() will be called otherwise it is deleted.
The same will be done with the timingFunction.
*/
void removeAnimation (CView* view, IdStringPtr name);
/** removes all animations for view */
void removeAnimations (CView* view);
//@}
/// @cond ignore
Animator (); // do not use this, instead use CFrame::getAnimator()
void onTimer ();
protected:
~Animator () noexcept override;
struct Impl;
std::unique_ptr<Impl> pImpl;
/// @endcond
};
#if VSTGUI_ENABLE_DEPRECATED_METHODS
/** message sent to the notificationObject when the animation has finished, the sender parameter will be a FinishedMessage object. */
extern IdStringPtr kMsgAnimationFinished;
//-----------------------------------------------------------------------------
/// @brief Animation Finished Message Object
///
/// The FinishedMessage will be sent to the notificationObject when the animation has finished
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class FinishedMessage : public CBaseObject
{
public:
FinishedMessage (CView* view, const std::string& name, IAnimationTarget* target) : view (view), name (name), target (target) {}
CView* getView () const { return view; }
IdStringPtr getName () const { return name.c_str (); }
IAnimationTarget* getTarget () const { return target; }
CLASS_METHODS_NOCOPY(FinishedMessage, CBaseObject)
protected:
CView* view;
const std::string& name;
IAnimationTarget* target;
};
#endif
} // Animation
} // VSTGUI
@@ -0,0 +1,30 @@
// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#pragma once
#include "../vstguifwd.h"
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @brief Animation target interface
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class IAnimationTarget
{
public:
virtual ~IAnimationTarget () noexcept = default;
/** animation starts */
virtual void animationStart (CView* view, IdStringPtr name) = 0;
/** pos is a normalized value between zero and one */
virtual void animationTick (CView* view, IdStringPtr name, float pos) = 0;
/** animation ended */
virtual void animationFinished (CView* view, IdStringPtr name, bool wasCanceled) = 0;
};
} // Animation
} // VSTGUI
@@ -0,0 +1,26 @@
// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#pragma once
#include "../vstguibase.h"
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @brief Animation timing function interface
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class ITimingFunction
{
public:
virtual ~ITimingFunction () noexcept = default;
virtual float getPosition (uint32_t milliseconds) = 0;
virtual bool isDone (uint32_t milliseconds) = 0;
};
} // Animation
} // VSTGUI
@@ -0,0 +1,219 @@
// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#include "timingfunctions.h"
#include "../vstguibase.h"
#include <cmath>
namespace VSTGUI {
namespace Animation {
//------------------------------------------------------------------------
/*! @defgroup AnimationTimingFunctions Animation Timing Functions
* @ingroup animation
*/
//------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
LinearTimingFunction::LinearTimingFunction (uint32_t length)
: TimingFunctionBase (length)
{
}
//-----------------------------------------------------------------------------
float LinearTimingFunction::getPosition (uint32_t milliseconds)
{
float pos = ((float)milliseconds) / ((float)length);
if (pos > 1.f)
pos = 1.f;
else if (pos < 0.f)
pos = 0.f;
return pos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
PowerTimingFunction::PowerTimingFunction (uint32_t length, float factor)
: TimingFunctionBase (length)
, factor (factor)
{
}
//-----------------------------------------------------------------------------
float PowerTimingFunction::getPosition (uint32_t milliseconds)
{
float pos = ((float)milliseconds) / ((float)length);
pos = std::pow (pos, factor);
if (pos > 1.f)
pos = 1.f;
else if (pos < 0.f)
pos = 0.f;
return pos;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
InterpolationTimingFunction::InterpolationTimingFunction (uint32_t length, float startPos, float endPos)
: TimingFunctionBase (length)
{
addPoint (0.f, startPos);
addPoint (1.f, endPos);
}
//-----------------------------------------------------------------------------
void InterpolationTimingFunction::addPoint (float time, float pos)
{
points.emplace ((uint32_t)((float)getLength () * time), pos);
}
//-----------------------------------------------------------------------------
float InterpolationTimingFunction::getPosition (uint32_t milliseconds)
{
uint32_t prevTime = getLength ();
float prevPos = points[prevTime];
PointMap::reverse_iterator it = points.rbegin ();
while (it != points.rend ())
{
uint32_t time = it->first;
float pos = it->second;
if (time == milliseconds)
return pos;
else if (time <= milliseconds && prevTime > milliseconds)
{
double timePos = (double)(milliseconds - time) / (double)(prevTime - time);
return static_cast<float> (static_cast<double> (pos) + ((static_cast<double> (prevPos) - static_cast<double> (pos)) * timePos));
}
prevTime = time;
prevPos = pos;
++it;
}
return 1.f;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CubicBezierTimingFunction::CubicBezierTimingFunction (uint32_t milliseconds, CPoint p1, CPoint p2)
: TimingFunctionBase (milliseconds), p1 (p1), p2 (p2)
{
}
//-----------------------------------------------------------------------------
CPoint CubicBezierTimingFunction::lerp (CPoint p1, CPoint p2, float pos)
{
return p1 * (1.f - pos) + p2 * pos;
}
//-----------------------------------------------------------------------------
float CubicBezierTimingFunction::getPosition (uint32_t milliseconds)
{
constexpr CPoint p0 (0, 0);
constexpr CPoint p3 (1, 1);
auto t = static_cast<float> (milliseconds) / static_cast<float> (length);
auto a = lerp (p0, p1, t);
auto b = lerp (p1, p2, t);
auto c = lerp (p2, p3, t);
auto d = lerp (a, b, t);
auto e = lerp (b, c, t);
auto result = lerp (d, e, t).y;
return static_cast<float> (result);
}
//-----------------------------------------------------------------------------
CubicBezierTimingFunction CubicBezierTimingFunction::easy (uint32_t time)
{
return CubicBezierTimingFunction (time, CPoint (0.25, 0.1), CPoint (0.25, 1.));
}
//-----------------------------------------------------------------------------
CubicBezierTimingFunction CubicBezierTimingFunction::easyIn (uint32_t time)
{
return CubicBezierTimingFunction (time, CPoint (0.42, 0.), CPoint (1., 1.));
}
//-----------------------------------------------------------------------------
CubicBezierTimingFunction CubicBezierTimingFunction::easyOut (uint32_t time)
{
return CubicBezierTimingFunction (time, CPoint (0., 0.), CPoint (0.58, 1.));
}
//-----------------------------------------------------------------------------
CubicBezierTimingFunction CubicBezierTimingFunction::easyInOut (uint32_t time)
{
return CubicBezierTimingFunction (time, CPoint (0.42, 0.), CPoint (0.58, 1.));
}
//------------------------------------------------------------------------
CubicBezierTimingFunction* CubicBezierTimingFunction::make (Style style, uint32_t time)
{
using Func = CubicBezierTimingFunction;
switch (style)
{
case Easy:
return new CubicBezierTimingFunction (Func::easy (time));
case EasyIn:
return new CubicBezierTimingFunction (Func::easyIn (time));
case EasyOut:
return new CubicBezierTimingFunction (Func::easyOut (time));
case EasyInOut:
return new CubicBezierTimingFunction (Func::easyInOut (time));
}
return nullptr;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
RepeatTimingFunction::RepeatTimingFunction (TimingFunctionBase* tf, int32_t repeatCount, bool autoReverse)
: tf (tf)
, repeatCount (repeatCount)
, runCounter (0)
, autoReverse (autoReverse)
, isReverse (false)
{
}
//-----------------------------------------------------------------------------
RepeatTimingFunction::~RepeatTimingFunction () noexcept
{
auto obj = dynamic_cast<IReference*> (tf);
if (obj)
obj->forget ();
else
delete tf;
}
//-----------------------------------------------------------------------------
float RepeatTimingFunction::getPosition (uint32_t milliseconds)
{
if (runCounter > 0)
milliseconds -= tf->getLength () * runCounter;
float pos = tf->getPosition (milliseconds);
return isReverse ? 1.f - pos : pos;
}
//-----------------------------------------------------------------------------
bool RepeatTimingFunction::isDone (uint32_t milliseconds)
{
if (runCounter > 0)
milliseconds -= tf->getLength () * runCounter;
if (tf->isDone (milliseconds))
{
runCounter++;
if (autoReverse)
isReverse = !isReverse;
if ((uint64_t)runCounter >= (uint64_t)repeatCount)
return true;
}
return false;
}
}} // namespaces
@@ -0,0 +1,144 @@
// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#pragma once
#include "animator.h"
#include "itimingfunction.h"
#include "../cpoint.h"
#include <map>
namespace VSTGUI {
namespace Animation {
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class TimingFunctionBase : public ITimingFunction
{
public:
explicit TimingFunctionBase (uint32_t length) : length (length) {}
TimingFunctionBase (const TimingFunctionBase&) = default;
TimingFunctionBase& operator= (const TimingFunctionBase&) = default;
uint32_t getLength () const { return length; }
bool isDone (uint32_t milliseconds) override { return milliseconds >= length; }
protected:
uint32_t length; // in milliseconds
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class LinearTimingFunction : public TimingFunctionBase
{
public:
explicit LinearTimingFunction (uint32_t length);
LinearTimingFunction (const LinearTimingFunction&) = default;
LinearTimingFunction& operator= (const LinearTimingFunction&) = default;
float getPosition (uint32_t milliseconds) override;
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class PowerTimingFunction : public TimingFunctionBase
{
public:
PowerTimingFunction (uint32_t length, float factor);
PowerTimingFunction (const PowerTimingFunction&) = default;
PowerTimingFunction& operator= (const PowerTimingFunction&) = default;
float getPosition (uint32_t milliseconds) override;
protected:
float factor;
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class InterpolationTimingFunction : public TimingFunctionBase
{
public:
InterpolationTimingFunction (uint32_t length, float startPos = 0.f, float endPos = 1.f);
InterpolationTimingFunction (const InterpolationTimingFunction&) = default;
InterpolationTimingFunction& operator= (const InterpolationTimingFunction&) = default;
/** both values are normalized ones */
void addPoint (float time, float pos);
float getPosition (uint32_t milliseconds) override;
protected:
using PointMap = std::map<uint32_t, float>;
PointMap points;
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_7
//-----------------------------------------------------------------------------
class CubicBezierTimingFunction : public TimingFunctionBase
{
public:
CubicBezierTimingFunction (uint32_t milliseconds, CPoint p1, CPoint p2);
CubicBezierTimingFunction (const CubicBezierTimingFunction&) = default;
CubicBezierTimingFunction (CubicBezierTimingFunction&&) = default;
CubicBezierTimingFunction& operator= (const CubicBezierTimingFunction&) = default;
CubicBezierTimingFunction& operator= (CubicBezierTimingFunction&&) = default;
float getPosition (uint32_t milliseconds) override;
// some common timings
static CubicBezierTimingFunction easy (uint32_t time);
static CubicBezierTimingFunction easyIn (uint32_t time);
static CubicBezierTimingFunction easyOut (uint32_t time);
static CubicBezierTimingFunction easyInOut (uint32_t time);
enum Style
{
Easy,
EasyIn,
EasyOut,
EasyInOut
};
static CubicBezierTimingFunction* make (Style style, uint32_t time);
private:
static CPoint lerp (CPoint p1, CPoint p2, float pos);
CPoint p1;
CPoint p2;
};
//-----------------------------------------------------------------------------
/// @ingroup AnimationTimingFunctions
/// @ingroup new_in_4_0
//-----------------------------------------------------------------------------
class RepeatTimingFunction : public ITimingFunction
{
public:
RepeatTimingFunction (TimingFunctionBase* tf, int32_t repeatCount, bool autoReverse = true);
~RepeatTimingFunction () noexcept override;
float getPosition (uint32_t milliseconds) override;
bool isDone (uint32_t milliseconds) override;
protected:
TimingFunctionBase* tf;
int32_t repeatCount;
uint32_t runCounter;
bool autoReverse;
bool isReverse;
};
} // Animation
} // VSTGUI