|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2020 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 6 End-User License
- Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
-
- End User License Agreement: www.juce.com/juce-6-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- class ComponentAnimator::AnimationTask
- {
- public:
- AnimationTask (Component* c) noexcept : component (c) {}
-
- ~AnimationTask()
- {
- proxy.deleteAndZero();
- }
-
- void reset (const Rectangle<int>& finalBounds,
- float finalAlpha,
- int millisecondsToSpendMoving,
- bool useProxyComponent,
- double startSpd, double endSpd)
- {
- msElapsed = 0;
- msTotal = jmax (1, millisecondsToSpendMoving);
- lastProgress = 0;
- destination = finalBounds;
- destAlpha = finalAlpha;
-
- isMoving = (finalBounds != component->getBounds());
- isChangingAlpha = (finalAlpha != component->getAlpha());
-
- left = component->getX();
- top = component->getY();
- right = component->getRight();
- bottom = component->getBottom();
- alpha = component->getAlpha();
-
- const double invTotalDistance = 4.0 / (startSpd + endSpd + 2.0);
- startSpeed = jmax (0.0, startSpd * invTotalDistance);
- midSpeed = invTotalDistance;
- endSpeed = jmax (0.0, endSpd * invTotalDistance);
-
- proxy.deleteAndZero();
-
- if (useProxyComponent)
- proxy = new ProxyComponent (*component);
-
- component->setVisible (! useProxyComponent);
- }
-
- bool useTimeslice (const int elapsed)
- {
- if (auto* c = proxy != nullptr ? proxy.getComponent()
- : component.get())
- {
- msElapsed += elapsed;
- double newProgress = msElapsed / (double) msTotal;
-
- if (newProgress >= 0 && newProgress < 1.0)
- {
- const WeakReference<AnimationTask> weakRef (this);
- newProgress = timeToDistance (newProgress);
- const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
- jassert (newProgress >= lastProgress);
- lastProgress = newProgress;
-
- if (delta < 1.0)
- {
- bool stillBusy = false;
-
- if (isMoving)
- {
- left += (destination.getX() - left) * delta;
- top += (destination.getY() - top) * delta;
- right += (destination.getRight() - right) * delta;
- bottom += (destination.getBottom() - bottom) * delta;
-
- const Rectangle<int> newBounds (roundToInt (left),
- roundToInt (top),
- roundToInt (right - left),
- roundToInt (bottom - top));
-
- if (newBounds != destination)
- {
- c->setBounds (newBounds);
- stillBusy = true;
- }
- }
-
- // Check whether the animation was cancelled/deleted during
- // a callback during the setBounds method
- if (weakRef.wasObjectDeleted())
- return false;
-
- if (isChangingAlpha)
- {
- alpha += (destAlpha - alpha) * delta;
- c->setAlpha ((float) alpha);
- stillBusy = true;
- }
-
- if (stillBusy)
- return true;
- }
- }
- }
-
- moveToFinalDestination();
- return false;
- }
-
- void moveToFinalDestination()
- {
- if (component != nullptr)
- {
- const WeakReference<AnimationTask> weakRef (this);
- component->setAlpha ((float) destAlpha);
- component->setBounds (destination);
-
- if (! weakRef.wasObjectDeleted())
- if (proxy != nullptr)
- component->setVisible (destAlpha > 0);
- }
- }
-
- //==============================================================================
- struct ProxyComponent : public Component
- {
- ProxyComponent (Component& c)
- {
- setWantsKeyboardFocus (false);
- setBounds (c.getBounds());
- setTransform (c.getTransform());
- setAlpha (c.getAlpha());
- setInterceptsMouseClicks (false, false);
-
- if (auto* parent = c.getParentComponent())
- parent->addAndMakeVisible (this);
- else if (c.isOnDesktop() && c.getPeer() != nullptr)
- addToDesktop (c.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses);
- else
- jassertfalse; // seem to be trying to animate a component that's not visible..
-
- auto scale = (float) Desktop::getInstance().getDisplays().findDisplayForRect (getScreenBounds()).scale
- * Component::getApproximateScaleFactorForComponent (&c);
-
- image = c.createComponentSnapshot (c.getLocalBounds(), false, scale);
-
- setVisible (true);
- toBehind (&c);
- }
-
- void paint (Graphics& g) override
- {
- g.setOpacity (1.0f);
- g.drawImageTransformed (image, AffineTransform::scale ((float) getWidth() / (float) jmax (1, image.getWidth()),
- (float) getHeight() / (float) jmax (1, image.getHeight())), false);
- }
-
- private:
- Image image;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent)
- };
-
- WeakReference<Component> component;
- Component::SafePointer<Component> proxy;
-
- Rectangle<int> destination;
- double destAlpha;
-
- int msElapsed, msTotal;
- double startSpeed, midSpeed, endSpeed, lastProgress;
- double left, top, right, bottom, alpha;
- bool isMoving, isChangingAlpha;
-
- private:
- double timeToDistance (const double time) const noexcept
- {
- 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));
- }
-
- JUCE_DECLARE_WEAK_REFERENCEABLE (AnimationTask)
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimationTask)
- };
-
- //==============================================================================
- ComponentAnimator::ComponentAnimator() : lastTime (0) {}
- ComponentAnimator::~ComponentAnimator() {}
-
- //==============================================================================
- ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const noexcept
- {
- for (int i = tasks.size(); --i >= 0;)
- if (component == tasks.getUnchecked(i)->component.get())
- return tasks.getUnchecked(i);
-
- return nullptr;
- }
-
- void ComponentAnimator::animateComponent (Component* const component,
- const Rectangle<int>& finalBounds,
- const float finalAlpha,
- const int millisecondsToSpendMoving,
- const bool useProxyComponent,
- const double startSpeed,
- const double endSpeed)
- {
- // the speeds must be 0 or greater!
- jassert (startSpeed >= 0 && endSpeed >= 0);
-
- if (component != nullptr)
- {
- auto* at = findTaskFor (component);
-
- if (at == nullptr)
- {
- at = new AnimationTask (component);
- tasks.add (at);
- sendChangeMessage();
- }
-
- at->reset (finalBounds, finalAlpha, millisecondsToSpendMoving,
- useProxyComponent, startSpeed, endSpeed);
-
- if (! isTimerRunning())
- {
- lastTime = Time::getMillisecondCounter();
- startTimerHz (50);
- }
- }
- }
-
- void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake)
- {
- if (component != nullptr)
- {
- if (component->isShowing() && millisecondsToTake > 0)
- animateComponent (component, component->getBounds(), 0.0f, millisecondsToTake, true, 1.0, 1.0);
-
- component->setVisible (false);
- }
- }
-
- void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake)
- {
- if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f))
- {
- component->setAlpha (0.0f);
- component->setVisible (true);
- animateComponent (component, component->getBounds(), 1.0f, millisecondsToTake, false, 1.0, 1.0);
- }
- }
-
- void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
- {
- if (tasks.size() > 0)
- {
- if (moveComponentsToTheirFinalPositions)
- for (int i = tasks.size(); --i >= 0;)
- tasks.getUnchecked(i)->moveToFinalDestination();
-
- tasks.clear();
- sendChangeMessage();
- }
- }
-
- void ComponentAnimator::cancelAnimation (Component* const component,
- const bool moveComponentToItsFinalPosition)
- {
- if (auto* at = findTaskFor (component))
- {
- if (moveComponentToItsFinalPosition)
- at->moveToFinalDestination();
-
- tasks.removeObject (at);
- sendChangeMessage();
- }
- }
-
- Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
- {
- jassert (component != nullptr);
-
- if (auto* at = findTaskFor (component))
- return at->destination;
-
- return component->getBounds();
- }
-
- bool ComponentAnimator::isAnimating (Component* component) const noexcept
- {
- return findTaskFor (component) != nullptr;
- }
-
- bool ComponentAnimator::isAnimating() const noexcept
- {
- return tasks.size() != 0;
- }
-
- void ComponentAnimator::timerCallback()
- {
- auto timeNow = Time::getMillisecondCounter();
-
- if (lastTime == 0)
- lastTime = timeNow;
-
- auto elapsed = (int) (timeNow - lastTime);
-
- for (auto* task : Array<AnimationTask*> (tasks.begin(), tasks.size()))
- {
- if (tasks.contains (task) && ! task->useTimeslice (elapsed))
- {
- tasks.removeObject (task);
- sendChangeMessage();
- }
- }
-
- lastTime = timeNow;
-
- if (tasks.size() == 0)
- stopTimer();
- }
-
- } // namespace juce
|