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.

347 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. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. class ComponentAnimator::AnimationTask
  21. {
  22. public:
  23. AnimationTask (Component* const comp)
  24. : component (comp)
  25. {
  26. }
  27. void reset (const Rectangle<int>& finalBounds,
  28. float finalAlpha,
  29. int millisecondsToSpendMoving,
  30. bool useProxyComponent,
  31. double startSpeed_, double endSpeed_)
  32. {
  33. msElapsed = 0;
  34. msTotal = jmax (1, millisecondsToSpendMoving);
  35. lastProgress = 0;
  36. destination = finalBounds;
  37. destAlpha = finalAlpha;
  38. isMoving = (finalBounds != component->getBounds());
  39. isChangingAlpha = (finalAlpha != component->getAlpha());
  40. left = component->getX();
  41. top = component->getY();
  42. right = component->getRight();
  43. bottom = component->getBottom();
  44. alpha = component->getAlpha();
  45. const double invTotalDistance = 4.0 / (startSpeed_ + endSpeed_ + 2.0);
  46. startSpeed = jmax (0.0, startSpeed_ * invTotalDistance);
  47. midSpeed = invTotalDistance;
  48. endSpeed = jmax (0.0, endSpeed_ * invTotalDistance);
  49. if (useProxyComponent)
  50. proxy = new ProxyComponent (*component);
  51. else
  52. proxy = nullptr;
  53. component->setVisible (! useProxyComponent);
  54. }
  55. bool useTimeslice (const int elapsed)
  56. {
  57. Component* const c = proxy != nullptr ? static_cast <Component*> (proxy)
  58. : static_cast <Component*> (component);
  59. if (c != nullptr)
  60. {
  61. msElapsed += elapsed;
  62. double newProgress = msElapsed / (double) msTotal;
  63. if (newProgress >= 0 && newProgress < 1.0)
  64. {
  65. newProgress = timeToDistance (newProgress);
  66. const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
  67. jassert (newProgress >= lastProgress);
  68. lastProgress = newProgress;
  69. if (delta < 1.0)
  70. {
  71. bool stillBusy = false;
  72. if (isMoving)
  73. {
  74. left += (destination.getX() - left) * delta;
  75. top += (destination.getY() - top) * delta;
  76. right += (destination.getRight() - right) * delta;
  77. bottom += (destination.getBottom() - bottom) * delta;
  78. const Rectangle<int> newBounds (roundToInt (left),
  79. roundToInt (top),
  80. roundToInt (right - left),
  81. roundToInt (bottom - top));
  82. if (newBounds != destination)
  83. {
  84. c->setBounds (newBounds);
  85. stillBusy = true;
  86. }
  87. }
  88. if (isChangingAlpha)
  89. {
  90. alpha += (destAlpha - alpha) * delta;
  91. c->setAlpha ((float) alpha);
  92. stillBusy = true;
  93. }
  94. if (stillBusy)
  95. return true;
  96. }
  97. }
  98. }
  99. moveToFinalDestination();
  100. return false;
  101. }
  102. void moveToFinalDestination()
  103. {
  104. if (component != nullptr)
  105. {
  106. component->setAlpha ((float) destAlpha);
  107. component->setBounds (destination);
  108. if (proxy != nullptr)
  109. component->setVisible (destAlpha > 0);
  110. }
  111. }
  112. //==============================================================================
  113. class ProxyComponent : public Component
  114. {
  115. public:
  116. ProxyComponent (Component& component)
  117. : image (component.createComponentSnapshot (component.getLocalBounds()))
  118. {
  119. setBounds (component.getBounds());
  120. setTransform (component.getTransform());
  121. setAlpha (component.getAlpha());
  122. setInterceptsMouseClicks (false, false);
  123. Component* const parent = component.getParentComponent();
  124. if (parent != nullptr)
  125. parent->addAndMakeVisible (this);
  126. else if (component.isOnDesktop() && component.getPeer() != nullptr)
  127. addToDesktop (component.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses);
  128. else
  129. jassertfalse; // seem to be trying to animate a component that's not visible..
  130. setVisible (true);
  131. toBehind (&component);
  132. }
  133. void paint (Graphics& g)
  134. {
  135. g.setOpacity (1.0f);
  136. g.drawImage (image, 0, 0, getWidth(), getHeight(),
  137. 0, 0, image.getWidth(), image.getHeight());
  138. }
  139. private:
  140. Image image;
  141. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent);
  142. };
  143. WeakReference<Component> component;
  144. ScopedPointer<Component> proxy;
  145. Rectangle<int> destination;
  146. double destAlpha;
  147. int msElapsed, msTotal;
  148. double startSpeed, midSpeed, endSpeed, lastProgress;
  149. double left, top, right, bottom, alpha;
  150. bool isMoving, isChangingAlpha;
  151. private:
  152. double timeToDistance (const double time) const noexcept
  153. {
  154. return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
  155. : 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
  156. + (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
  157. }
  158. };
  159. //==============================================================================
  160. ComponentAnimator::ComponentAnimator()
  161. : lastTime (0)
  162. {
  163. }
  164. ComponentAnimator::~ComponentAnimator()
  165. {
  166. }
  167. //==============================================================================
  168. ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const noexcept
  169. {
  170. for (int i = tasks.size(); --i >= 0;)
  171. if (component == tasks.getUnchecked(i)->component.get())
  172. return tasks.getUnchecked(i);
  173. return nullptr;
  174. }
  175. void ComponentAnimator::animateComponent (Component* const component,
  176. const Rectangle<int>& finalBounds,
  177. const float finalAlpha,
  178. const int millisecondsToSpendMoving,
  179. const bool useProxyComponent,
  180. const double startSpeed,
  181. const double endSpeed)
  182. {
  183. // the speeds must be 0 or greater!
  184. jassert (startSpeed >= 0 && endSpeed >= 0)
  185. if (component != nullptr)
  186. {
  187. AnimationTask* at = findTaskFor (component);
  188. if (at == nullptr)
  189. {
  190. at = new AnimationTask (component);
  191. tasks.add (at);
  192. sendChangeMessage();
  193. }
  194. at->reset (finalBounds, finalAlpha, millisecondsToSpendMoving,
  195. useProxyComponent, startSpeed, endSpeed);
  196. if (! isTimerRunning())
  197. {
  198. lastTime = Time::getMillisecondCounter();
  199. startTimer (1000 / 50);
  200. }
  201. }
  202. }
  203. void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake)
  204. {
  205. if (component != nullptr)
  206. {
  207. if (component->isShowing() && millisecondsToTake > 0)
  208. animateComponent (component, component->getBounds(), 0.0f, millisecondsToTake, true, 1.0, 1.0);
  209. component->setVisible (false);
  210. }
  211. }
  212. void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake)
  213. {
  214. if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f))
  215. {
  216. component->setAlpha (0.0f);
  217. component->setVisible (true);
  218. animateComponent (component, component->getBounds(), 1.0f, millisecondsToTake, false, 1.0, 1.0);
  219. }
  220. }
  221. void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
  222. {
  223. if (tasks.size() > 0)
  224. {
  225. if (moveComponentsToTheirFinalPositions)
  226. for (int i = tasks.size(); --i >= 0;)
  227. tasks.getUnchecked(i)->moveToFinalDestination();
  228. tasks.clear();
  229. sendChangeMessage();
  230. }
  231. }
  232. void ComponentAnimator::cancelAnimation (Component* const component,
  233. const bool moveComponentToItsFinalPosition)
  234. {
  235. AnimationTask* const at = findTaskFor (component);
  236. if (at != nullptr)
  237. {
  238. if (moveComponentToItsFinalPosition)
  239. at->moveToFinalDestination();
  240. tasks.removeObject (at);
  241. sendChangeMessage();
  242. }
  243. }
  244. const Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
  245. {
  246. jassert (component != nullptr);
  247. AnimationTask* const at = findTaskFor (component);
  248. if (at != nullptr)
  249. return at->destination;
  250. return component->getBounds();
  251. }
  252. bool ComponentAnimator::isAnimating (Component* component) const noexcept
  253. {
  254. return findTaskFor (component) != nullptr;
  255. }
  256. bool ComponentAnimator::isAnimating() const noexcept
  257. {
  258. return tasks.size() != 0;
  259. }
  260. void ComponentAnimator::timerCallback()
  261. {
  262. const uint32 timeNow = Time::getMillisecondCounter();
  263. if (lastTime == 0 || lastTime == timeNow)
  264. lastTime = timeNow;
  265. const int elapsed = (int) (timeNow - lastTime);
  266. for (int i = tasks.size(); --i >= 0;)
  267. {
  268. if (! tasks.getUnchecked(i)->useTimeslice (elapsed))
  269. {
  270. tasks.remove (i);
  271. sendChangeMessage();
  272. }
  273. }
  274. lastTime = timeNow;
  275. if (tasks.size() == 0)
  276. stopTimer();
  277. }
  278. END_JUCE_NAMESPACE