The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

341 lines
11KB

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