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.

333 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class ComponentAnimator::AnimationTask
  18. {
  19. public:
  20. AnimationTask (Component* const comp)
  21. : component (comp)
  22. {
  23. }
  24. void reset (const Rectangle<int>& finalBounds,
  25. float finalAlpha,
  26. int millisecondsToSpendMoving,
  27. bool useProxyComponent,
  28. double startSpeed_, double endSpeed_)
  29. {
  30. msElapsed = 0;
  31. msTotal = jmax (1, millisecondsToSpendMoving);
  32. lastProgress = 0;
  33. destination = finalBounds;
  34. destAlpha = finalAlpha;
  35. isMoving = (finalBounds != component->getBounds());
  36. isChangingAlpha = (finalAlpha != component->getAlpha());
  37. left = component->getX();
  38. top = component->getY();
  39. right = component->getRight();
  40. bottom = component->getBottom();
  41. alpha = component->getAlpha();
  42. const double invTotalDistance = 4.0 / (startSpeed_ + endSpeed_ + 2.0);
  43. startSpeed = jmax (0.0, startSpeed_ * invTotalDistance);
  44. midSpeed = invTotalDistance;
  45. endSpeed = jmax (0.0, endSpeed_ * invTotalDistance);
  46. if (useProxyComponent)
  47. proxy = new ProxyComponent (*component);
  48. else
  49. proxy = nullptr;
  50. component->setVisible (! useProxyComponent);
  51. }
  52. bool useTimeslice (const int elapsed)
  53. {
  54. if (Component* const c = proxy != nullptr ? static_cast <Component*> (proxy)
  55. : static_cast <Component*> (component))
  56. {
  57. msElapsed += elapsed;
  58. double newProgress = msElapsed / (double) msTotal;
  59. if (newProgress >= 0 && newProgress < 1.0)
  60. {
  61. newProgress = timeToDistance (newProgress);
  62. const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
  63. jassert (newProgress >= lastProgress);
  64. lastProgress = newProgress;
  65. if (delta < 1.0)
  66. {
  67. bool stillBusy = false;
  68. if (isMoving)
  69. {
  70. left += (destination.getX() - left) * delta;
  71. top += (destination.getY() - top) * delta;
  72. right += (destination.getRight() - right) * delta;
  73. bottom += (destination.getBottom() - bottom) * delta;
  74. const Rectangle<int> newBounds (roundToInt (left),
  75. roundToInt (top),
  76. roundToInt (right - left),
  77. roundToInt (bottom - top));
  78. if (newBounds != destination)
  79. {
  80. c->setBounds (newBounds);
  81. stillBusy = true;
  82. }
  83. }
  84. if (isChangingAlpha)
  85. {
  86. alpha += (destAlpha - alpha) * delta;
  87. c->setAlpha ((float) alpha);
  88. stillBusy = true;
  89. }
  90. if (stillBusy)
  91. return true;
  92. }
  93. }
  94. }
  95. moveToFinalDestination();
  96. return false;
  97. }
  98. void moveToFinalDestination()
  99. {
  100. if (component != nullptr)
  101. {
  102. component->setAlpha ((float) destAlpha);
  103. component->setBounds (destination);
  104. if (proxy != nullptr)
  105. component->setVisible (destAlpha > 0);
  106. }
  107. }
  108. //==============================================================================
  109. class ProxyComponent : public Component
  110. {
  111. public:
  112. ProxyComponent (Component& c)
  113. : image (c.createComponentSnapshot (c.getLocalBounds()))
  114. {
  115. setBounds (c.getBounds());
  116. setTransform (c.getTransform());
  117. setAlpha (c.getAlpha());
  118. setInterceptsMouseClicks (false, false);
  119. if (Component* const parent = c.getParentComponent())
  120. parent->addAndMakeVisible (this);
  121. else if (c.isOnDesktop() && c.getPeer() != nullptr)
  122. addToDesktop (c.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses);
  123. else
  124. jassertfalse; // seem to be trying to animate a component that's not visible..
  125. setVisible (true);
  126. toBehind (&c);
  127. }
  128. void paint (Graphics& g) override
  129. {
  130. g.setOpacity (1.0f);
  131. g.drawImageTransformed (image, AffineTransform::scale (getWidth() / (float) image.getWidth(),
  132. getHeight() / (float) image.getHeight()), false);
  133. }
  134. private:
  135. Image image;
  136. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent)
  137. };
  138. WeakReference<Component> component;
  139. ScopedPointer<Component> proxy;
  140. Rectangle<int> destination;
  141. double destAlpha;
  142. int msElapsed, msTotal;
  143. double startSpeed, midSpeed, endSpeed, lastProgress;
  144. double left, top, right, bottom, alpha;
  145. bool isMoving, isChangingAlpha;
  146. private:
  147. double timeToDistance (const double time) const noexcept
  148. {
  149. return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
  150. : 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
  151. + (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
  152. }
  153. };
  154. //==============================================================================
  155. ComponentAnimator::ComponentAnimator()
  156. : lastTime (0)
  157. {
  158. }
  159. ComponentAnimator::~ComponentAnimator()
  160. {
  161. }
  162. //==============================================================================
  163. ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const noexcept
  164. {
  165. for (int i = tasks.size(); --i >= 0;)
  166. if (component == tasks.getUnchecked(i)->component.get())
  167. return tasks.getUnchecked(i);
  168. return nullptr;
  169. }
  170. void ComponentAnimator::animateComponent (Component* const component,
  171. const Rectangle<int>& finalBounds,
  172. const float finalAlpha,
  173. const int millisecondsToSpendMoving,
  174. const bool useProxyComponent,
  175. const double startSpeed,
  176. const double endSpeed)
  177. {
  178. // the speeds must be 0 or greater!
  179. jassert (startSpeed >= 0 && endSpeed >= 0)
  180. if (component != nullptr)
  181. {
  182. AnimationTask* at = findTaskFor (component);
  183. if (at == nullptr)
  184. {
  185. at = new AnimationTask (component);
  186. tasks.add (at);
  187. sendChangeMessage();
  188. }
  189. at->reset (finalBounds, finalAlpha, millisecondsToSpendMoving,
  190. useProxyComponent, startSpeed, endSpeed);
  191. if (! isTimerRunning())
  192. {
  193. lastTime = Time::getMillisecondCounter();
  194. startTimer (1000 / 50);
  195. }
  196. }
  197. }
  198. void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake)
  199. {
  200. if (component != nullptr)
  201. {
  202. if (component->isShowing() && millisecondsToTake > 0)
  203. animateComponent (component, component->getBounds(), 0.0f, millisecondsToTake, true, 1.0, 1.0);
  204. component->setVisible (false);
  205. }
  206. }
  207. void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake)
  208. {
  209. if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f))
  210. {
  211. component->setAlpha (0.0f);
  212. component->setVisible (true);
  213. animateComponent (component, component->getBounds(), 1.0f, millisecondsToTake, false, 1.0, 1.0);
  214. }
  215. }
  216. void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
  217. {
  218. if (tasks.size() > 0)
  219. {
  220. if (moveComponentsToTheirFinalPositions)
  221. for (int i = tasks.size(); --i >= 0;)
  222. tasks.getUnchecked(i)->moveToFinalDestination();
  223. tasks.clear();
  224. sendChangeMessage();
  225. }
  226. }
  227. void ComponentAnimator::cancelAnimation (Component* const component,
  228. const bool moveComponentToItsFinalPosition)
  229. {
  230. if (AnimationTask* const at = findTaskFor (component))
  231. {
  232. if (moveComponentToItsFinalPosition)
  233. at->moveToFinalDestination();
  234. tasks.removeObject (at);
  235. sendChangeMessage();
  236. }
  237. }
  238. Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
  239. {
  240. jassert (component != nullptr);
  241. if (AnimationTask* const at = findTaskFor (component))
  242. return at->destination;
  243. return component->getBounds();
  244. }
  245. bool ComponentAnimator::isAnimating (Component* component) const noexcept
  246. {
  247. return findTaskFor (component) != nullptr;
  248. }
  249. bool ComponentAnimator::isAnimating() const noexcept
  250. {
  251. return tasks.size() != 0;
  252. }
  253. void ComponentAnimator::timerCallback()
  254. {
  255. const uint32 timeNow = Time::getMillisecondCounter();
  256. if (lastTime == 0 || lastTime == timeNow)
  257. lastTime = timeNow;
  258. const int elapsed = (int) (timeNow - lastTime);
  259. for (int i = tasks.size(); --i >= 0;)
  260. {
  261. if (! tasks.getUnchecked(i)->useTimeslice (elapsed))
  262. {
  263. tasks.remove (i);
  264. sendChangeMessage();
  265. }
  266. }
  267. lastTime = timeNow;
  268. if (tasks.size() == 0)
  269. stopTimer();
  270. }