mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-11 23:54:18 +00:00
244 lines
7.4 KiB
C++
244 lines
7.4 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
Copyright 2004-10 by Raw Material Software Ltd.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the GNU General
|
|
Public License (Version 2), as published by the Free Software Foundation.
|
|
A copy of the license is included in the JUCE distribution, or can be found
|
|
online at www.gnu.org/licenses.
|
|
|
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
To release a closed-source product which uses JUCE, commercial licenses are
|
|
available: visit www.rawmaterialsoftware.com/juce for more information.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include "../../../core/juce_StandardHeader.h"
|
|
|
|
BEGIN_JUCE_NAMESPACE
|
|
|
|
#include "juce_ComponentAnimator.h"
|
|
#include "../../../core/juce_Time.h"
|
|
|
|
|
|
//==============================================================================
|
|
class ComponentAnimator::AnimationTask
|
|
{
|
|
public:
|
|
AnimationTask (Component* const comp)
|
|
: component (comp)
|
|
{
|
|
}
|
|
|
|
Component::SafePointer<Component> component;
|
|
Rectangle<int> destination;
|
|
int msElapsed, msTotal;
|
|
double startSpeed, midSpeed, endSpeed, lastProgress;
|
|
double left, top, right, bottom;
|
|
|
|
bool useTimeslice (const int elapsed)
|
|
{
|
|
if (component == 0)
|
|
return false;
|
|
|
|
msElapsed += elapsed;
|
|
double newProgress = msElapsed / (double) msTotal;
|
|
|
|
if (newProgress >= 0 && newProgress < 1.0)
|
|
{
|
|
newProgress = timeToDistance (newProgress);
|
|
const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
|
|
jassert (newProgress >= lastProgress);
|
|
lastProgress = newProgress;
|
|
|
|
left += (destination.getX() - left) * delta;
|
|
top += (destination.getY() - top) * delta;
|
|
right += (destination.getRight() - right) * delta;
|
|
bottom += (destination.getBottom() - bottom) * delta;
|
|
|
|
if (delta < 1.0)
|
|
{
|
|
const Rectangle<int> newBounds (roundToInt (left),
|
|
roundToInt (top),
|
|
roundToInt (right - left),
|
|
roundToInt (bottom - top));
|
|
|
|
if (newBounds != destination)
|
|
{
|
|
component->setBounds (newBounds);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
component->setBounds (destination);
|
|
return false;
|
|
}
|
|
|
|
void moveToFinalDestination()
|
|
{
|
|
if (component != 0)
|
|
component->setBounds (destination);
|
|
}
|
|
|
|
private:
|
|
inline double timeToDistance (const double time) const
|
|
{
|
|
return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
|
|
: 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
|
|
+ (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
ComponentAnimator::ComponentAnimator()
|
|
: lastTime (0)
|
|
{
|
|
}
|
|
|
|
ComponentAnimator::~ComponentAnimator()
|
|
{
|
|
cancelAllAnimations (false);
|
|
jassert (tasks.size() == 0);
|
|
}
|
|
|
|
//==============================================================================
|
|
ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const
|
|
{
|
|
for (int i = tasks.size(); --i >= 0;)
|
|
if (component == tasks.getUnchecked(i)->component.getComponent())
|
|
return tasks.getUnchecked(i);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ComponentAnimator::animateComponent (Component* const component,
|
|
const Rectangle<int>& finalPosition,
|
|
const int millisecondsToSpendMoving,
|
|
const double startSpeed,
|
|
const double endSpeed)
|
|
{
|
|
if (component != 0)
|
|
{
|
|
AnimationTask* at = findTaskFor (component);
|
|
|
|
if (at == 0)
|
|
{
|
|
at = new AnimationTask (component);
|
|
tasks.add (at);
|
|
sendChangeMessage (this);
|
|
}
|
|
|
|
at->msElapsed = 0;
|
|
at->lastProgress = 0;
|
|
at->msTotal = jmax (1, millisecondsToSpendMoving);
|
|
at->destination = finalPosition;
|
|
|
|
// the speeds must be 0 or greater!
|
|
jassert (startSpeed >= 0 && endSpeed >= 0)
|
|
|
|
const double invTotalDistance = 4.0 / (startSpeed + endSpeed + 2.0);
|
|
at->startSpeed = jmax (0.0, startSpeed * invTotalDistance);
|
|
at->midSpeed = invTotalDistance;
|
|
at->endSpeed = jmax (0.0, endSpeed * invTotalDistance);
|
|
|
|
at->left = component->getX();
|
|
at->top = component->getY();
|
|
at->right = component->getRight();
|
|
at->bottom = component->getBottom();
|
|
|
|
if (! isTimerRunning())
|
|
{
|
|
lastTime = Time::getMillisecondCounter();
|
|
startTimer (1000 / 50);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
|
|
{
|
|
for (int i = tasks.size(); --i >= 0;)
|
|
{
|
|
AnimationTask* const at = tasks.getUnchecked(i);
|
|
|
|
if (moveComponentsToTheirFinalPositions)
|
|
at->moveToFinalDestination();
|
|
|
|
delete at;
|
|
tasks.remove (i);
|
|
sendChangeMessage (this);
|
|
}
|
|
}
|
|
|
|
void ComponentAnimator::cancelAnimation (Component* const component,
|
|
const bool moveComponentToItsFinalPosition)
|
|
{
|
|
AnimationTask* const at = findTaskFor (component);
|
|
|
|
if (at != 0)
|
|
{
|
|
if (moveComponentToItsFinalPosition)
|
|
at->moveToFinalDestination();
|
|
|
|
tasks.removeValue (at);
|
|
delete at;
|
|
sendChangeMessage (this);
|
|
}
|
|
}
|
|
|
|
const Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
|
|
{
|
|
AnimationTask* const at = findTaskFor (component);
|
|
|
|
if (at != 0)
|
|
return at->destination;
|
|
else if (component != 0)
|
|
return component->getBounds();
|
|
|
|
return Rectangle<int>();
|
|
}
|
|
|
|
bool ComponentAnimator::isAnimating (Component* component) const
|
|
{
|
|
return findTaskFor (component) != 0;
|
|
}
|
|
|
|
void ComponentAnimator::timerCallback()
|
|
{
|
|
const uint32 timeNow = Time::getMillisecondCounter();
|
|
|
|
if (lastTime == 0 || lastTime == timeNow)
|
|
lastTime = timeNow;
|
|
|
|
const int elapsed = timeNow - lastTime;
|
|
|
|
for (int i = tasks.size(); --i >= 0;)
|
|
{
|
|
AnimationTask* const at = tasks.getUnchecked(i);
|
|
|
|
if (! at->useTimeslice (elapsed))
|
|
{
|
|
tasks.remove (i);
|
|
delete at;
|
|
sendChangeMessage (this);
|
|
}
|
|
}
|
|
|
|
lastTime = timeNow;
|
|
|
|
if (tasks.size() == 0)
|
|
stopTimer();
|
|
}
|
|
|
|
|
|
END_JUCE_NAMESPACE
|