Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

343 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. class ComponentAnimator::AnimationTask
  16. {
  17. public:
  18. AnimationTask (Component* c) noexcept : component (c) {}
  19. ~AnimationTask()
  20. {
  21. proxy.deleteAndZero();
  22. }
  23. void reset (const Rectangle<int>& finalBounds,
  24. float finalAlpha,
  25. int millisecondsToSpendMoving,
  26. bool useProxyComponent,
  27. double startSpd, double endSpd)
  28. {
  29. msElapsed = 0;
  30. msTotal = jmax (1, millisecondsToSpendMoving);
  31. lastProgress = 0;
  32. destination = finalBounds;
  33. destAlpha = finalAlpha;
  34. isMoving = (finalBounds != component->getBounds());
  35. isChangingAlpha = (finalAlpha != component->getAlpha());
  36. left = component->getX();
  37. top = component->getY();
  38. right = component->getRight();
  39. bottom = component->getBottom();
  40. alpha = component->getAlpha();
  41. const double invTotalDistance = 4.0 / (startSpd + endSpd + 2.0);
  42. startSpeed = jmax (0.0, startSpd * invTotalDistance);
  43. midSpeed = invTotalDistance;
  44. endSpeed = jmax (0.0, endSpd * invTotalDistance);
  45. proxy.deleteAndZero();
  46. if (useProxyComponent)
  47. proxy = new ProxyComponent (*component);
  48. component->setVisible (! useProxyComponent);
  49. }
  50. bool useTimeslice (const int elapsed)
  51. {
  52. if (auto* c = proxy != nullptr ? proxy.getComponent()
  53. : component.get())
  54. {
  55. msElapsed += elapsed;
  56. double newProgress = msElapsed / (double) msTotal;
  57. if (newProgress >= 0 && newProgress < 1.0)
  58. {
  59. const WeakReference<AnimationTask> weakRef (this);
  60. newProgress = timeToDistance (newProgress);
  61. const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
  62. jassert (newProgress >= lastProgress);
  63. lastProgress = newProgress;
  64. if (delta < 1.0)
  65. {
  66. bool stillBusy = false;
  67. if (isMoving)
  68. {
  69. left += (destination.getX() - left) * delta;
  70. top += (destination.getY() - top) * delta;
  71. right += (destination.getRight() - right) * delta;
  72. bottom += (destination.getBottom() - bottom) * delta;
  73. const Rectangle<int> newBounds (roundToInt (left),
  74. roundToInt (top),
  75. roundToInt (right - left),
  76. roundToInt (bottom - top));
  77. if (newBounds != destination)
  78. {
  79. c->setBounds (newBounds);
  80. stillBusy = true;
  81. }
  82. }
  83. // Check whether the animation was cancelled/deleted during
  84. // a callback during the setBounds method
  85. if (weakRef.wasObjectDeleted())
  86. return false;
  87. if (isChangingAlpha)
  88. {
  89. alpha += (destAlpha - alpha) * delta;
  90. c->setAlpha ((float) alpha);
  91. stillBusy = true;
  92. }
  93. if (stillBusy)
  94. return true;
  95. }
  96. }
  97. }
  98. moveToFinalDestination();
  99. return false;
  100. }
  101. void moveToFinalDestination()
  102. {
  103. if (component != nullptr)
  104. {
  105. const WeakReference<AnimationTask> weakRef (this);
  106. component->setAlpha ((float) destAlpha);
  107. component->setBounds (destination);
  108. if (! weakRef.wasObjectDeleted())
  109. if (proxy != nullptr)
  110. component->setVisible (destAlpha > 0);
  111. }
  112. }
  113. //==============================================================================
  114. struct ProxyComponent : public Component
  115. {
  116. ProxyComponent (Component& c)
  117. {
  118. setWantsKeyboardFocus (false);
  119. setBounds (c.getBounds());
  120. setTransform (c.getTransform());
  121. setAlpha (c.getAlpha());
  122. setInterceptsMouseClicks (false, false);
  123. if (auto* parent = c.getParentComponent())
  124. parent->addAndMakeVisible (this);
  125. else if (c.isOnDesktop() && c.getPeer() != nullptr)
  126. addToDesktop (c.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses);
  127. else
  128. jassertfalse; // seem to be trying to animate a component that's not visible..
  129. auto scale = (float) Desktop::getInstance().getDisplays().findDisplayForRect (getScreenBounds()).scale
  130. * Component::getApproximateScaleFactorForComponent (&c);
  131. image = c.createComponentSnapshot (c.getLocalBounds(), false, scale);
  132. setVisible (true);
  133. toBehind (&c);
  134. }
  135. void paint (Graphics& g) override
  136. {
  137. g.setOpacity (1.0f);
  138. g.drawImageTransformed (image, AffineTransform::scale (getWidth() / (float) jmax (1, image.getWidth()),
  139. getHeight() / (float) jmax (1, image.getHeight())), false);
  140. }
  141. private:
  142. Image image;
  143. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent)
  144. };
  145. WeakReference<Component> component;
  146. Component::SafePointer<Component> proxy;
  147. Rectangle<int> destination;
  148. double destAlpha;
  149. int msElapsed, msTotal;
  150. double startSpeed, midSpeed, endSpeed, lastProgress;
  151. double left, top, right, bottom, alpha;
  152. bool isMoving, isChangingAlpha;
  153. private:
  154. double timeToDistance (const double time) const noexcept
  155. {
  156. return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
  157. : 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
  158. + (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
  159. }
  160. JUCE_DECLARE_WEAK_REFERENCEABLE (AnimationTask)
  161. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimationTask)
  162. };
  163. //==============================================================================
  164. ComponentAnimator::ComponentAnimator() : lastTime (0) {}
  165. ComponentAnimator::~ComponentAnimator() {}
  166. //==============================================================================
  167. ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const noexcept
  168. {
  169. for (int i = tasks.size(); --i >= 0;)
  170. if (component == tasks.getUnchecked(i)->component.get())
  171. return tasks.getUnchecked(i);
  172. return nullptr;
  173. }
  174. void ComponentAnimator::animateComponent (Component* const component,
  175. const Rectangle<int>& finalBounds,
  176. const float finalAlpha,
  177. const int millisecondsToSpendMoving,
  178. const bool useProxyComponent,
  179. const double startSpeed,
  180. const double endSpeed)
  181. {
  182. // the speeds must be 0 or greater!
  183. jassert (startSpeed >= 0 && endSpeed >= 0);
  184. if (component != nullptr)
  185. {
  186. auto* at = findTaskFor (component);
  187. if (at == nullptr)
  188. {
  189. at = new AnimationTask (component);
  190. tasks.add (at);
  191. sendChangeMessage();
  192. }
  193. at->reset (finalBounds, finalAlpha, millisecondsToSpendMoving,
  194. useProxyComponent, startSpeed, endSpeed);
  195. if (! isTimerRunning())
  196. {
  197. lastTime = Time::getMillisecondCounter();
  198. startTimerHz (50);
  199. }
  200. }
  201. }
  202. void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake)
  203. {
  204. if (component != nullptr)
  205. {
  206. if (component->isShowing() && millisecondsToTake > 0)
  207. animateComponent (component, component->getBounds(), 0.0f, millisecondsToTake, true, 1.0, 1.0);
  208. component->setVisible (false);
  209. }
  210. }
  211. void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake)
  212. {
  213. if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f))
  214. {
  215. component->setAlpha (0.0f);
  216. component->setVisible (true);
  217. animateComponent (component, component->getBounds(), 1.0f, millisecondsToTake, false, 1.0, 1.0);
  218. }
  219. }
  220. void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
  221. {
  222. if (tasks.size() > 0)
  223. {
  224. if (moveComponentsToTheirFinalPositions)
  225. for (int i = tasks.size(); --i >= 0;)
  226. tasks.getUnchecked(i)->moveToFinalDestination();
  227. tasks.clear();
  228. sendChangeMessage();
  229. }
  230. }
  231. void ComponentAnimator::cancelAnimation (Component* const component,
  232. const bool moveComponentToItsFinalPosition)
  233. {
  234. if (auto* at = findTaskFor (component))
  235. {
  236. if (moveComponentToItsFinalPosition)
  237. at->moveToFinalDestination();
  238. tasks.removeObject (at);
  239. sendChangeMessage();
  240. }
  241. }
  242. Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
  243. {
  244. jassert (component != nullptr);
  245. if (auto* at = findTaskFor (component))
  246. return at->destination;
  247. return component->getBounds();
  248. }
  249. bool ComponentAnimator::isAnimating (Component* component) const noexcept
  250. {
  251. return findTaskFor (component) != nullptr;
  252. }
  253. bool ComponentAnimator::isAnimating() const noexcept
  254. {
  255. return tasks.size() != 0;
  256. }
  257. void ComponentAnimator::timerCallback()
  258. {
  259. auto timeNow = Time::getMillisecondCounter();
  260. if (lastTime == 0)
  261. lastTime = timeNow;
  262. auto elapsed = (int) (timeNow - lastTime);
  263. for (auto* task : Array<AnimationTask*> (tasks.begin(), tasks.size()))
  264. {
  265. if (tasks.contains (task) && ! task->useTimeslice (elapsed))
  266. {
  267. tasks.removeObject (task);
  268. sendChangeMessage();
  269. }
  270. }
  271. lastTime = timeNow;
  272. if (tasks.size() == 0)
  273. stopTimer();
  274. }
  275. } // namespace juce