1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-11 23:54:18 +00:00
JUCE/src/gui/components/layout/juce_ComponentAnimator.cpp
2010-04-23 09:37:13 +01:00

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