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.

245 lines
7.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_ComponentAnimator.h"
  21. #include "../../../core/juce_Time.h"
  22. //==============================================================================
  23. class ComponentAnimator::AnimationTask
  24. {
  25. public:
  26. AnimationTask (Component* const comp)
  27. : component (comp)
  28. {
  29. }
  30. Component::SafePointer<Component> component;
  31. Rectangle<int> destination;
  32. int msElapsed, msTotal;
  33. double startSpeed, midSpeed, endSpeed, lastProgress;
  34. double left, top, right, bottom;
  35. bool useTimeslice (const int elapsed)
  36. {
  37. if (component == 0)
  38. return false;
  39. msElapsed += elapsed;
  40. double newProgress = msElapsed / (double) msTotal;
  41. if (newProgress >= 0 && newProgress < 1.0)
  42. {
  43. newProgress = timeToDistance (newProgress);
  44. const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
  45. jassert (newProgress >= lastProgress);
  46. lastProgress = newProgress;
  47. left += (destination.getX() - left) * delta;
  48. top += (destination.getY() - top) * delta;
  49. right += (destination.getRight() - right) * delta;
  50. bottom += (destination.getBottom() - bottom) * delta;
  51. if (delta < 1.0)
  52. {
  53. const Rectangle<int> newBounds (roundToInt (left),
  54. roundToInt (top),
  55. roundToInt (right - left),
  56. roundToInt (bottom - top));
  57. if (newBounds != destination)
  58. {
  59. component->setBounds (newBounds);
  60. return true;
  61. }
  62. }
  63. }
  64. component->setBounds (destination);
  65. return false;
  66. }
  67. void moveToFinalDestination()
  68. {
  69. if (component != 0)
  70. component->setBounds (destination);
  71. }
  72. private:
  73. inline double timeToDistance (const double time) const
  74. {
  75. return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
  76. : 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
  77. + (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
  78. }
  79. };
  80. //==============================================================================
  81. ComponentAnimator::ComponentAnimator()
  82. : lastTime (0)
  83. {
  84. }
  85. ComponentAnimator::~ComponentAnimator()
  86. {
  87. cancelAllAnimations (false);
  88. jassert (tasks.size() == 0);
  89. }
  90. //==============================================================================
  91. ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const
  92. {
  93. for (int i = tasks.size(); --i >= 0;)
  94. if (component == tasks.getUnchecked(i)->component.getComponent())
  95. return tasks.getUnchecked(i);
  96. return 0;
  97. }
  98. void ComponentAnimator::animateComponent (Component* const component,
  99. const Rectangle<int>& finalPosition,
  100. const int millisecondsToSpendMoving,
  101. const double startSpeed,
  102. const double endSpeed)
  103. {
  104. if (component != 0)
  105. {
  106. AnimationTask* at = findTaskFor (component);
  107. if (at == 0)
  108. {
  109. at = new AnimationTask (component);
  110. tasks.add (at);
  111. sendChangeMessage (this);
  112. }
  113. at->msElapsed = 0;
  114. at->lastProgress = 0;
  115. at->msTotal = jmax (1, millisecondsToSpendMoving);
  116. at->destination = finalPosition;
  117. // the speeds must be 0 or greater!
  118. jassert (startSpeed >= 0 && endSpeed >= 0)
  119. const double invTotalDistance = 4.0 / (startSpeed + endSpeed + 2.0);
  120. at->startSpeed = jmax (0.0, startSpeed * invTotalDistance);
  121. at->midSpeed = invTotalDistance;
  122. at->endSpeed = jmax (0.0, endSpeed * invTotalDistance);
  123. at->left = component->getX();
  124. at->top = component->getY();
  125. at->right = component->getRight();
  126. at->bottom = component->getBottom();
  127. if (! isTimerRunning())
  128. {
  129. lastTime = Time::getMillisecondCounter();
  130. startTimer (1000 / 50);
  131. }
  132. }
  133. }
  134. void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
  135. {
  136. for (int i = tasks.size(); --i >= 0;)
  137. {
  138. AnimationTask* const at = tasks.getUnchecked(i);
  139. if (moveComponentsToTheirFinalPositions)
  140. at->moveToFinalDestination();
  141. delete at;
  142. tasks.remove (i);
  143. sendChangeMessage (this);
  144. }
  145. }
  146. void ComponentAnimator::cancelAnimation (Component* const component,
  147. const bool moveComponentToItsFinalPosition)
  148. {
  149. AnimationTask* const at = findTaskFor (component);
  150. if (at != 0)
  151. {
  152. if (moveComponentToItsFinalPosition)
  153. at->moveToFinalDestination();
  154. tasks.removeValue (at);
  155. delete at;
  156. sendChangeMessage (this);
  157. }
  158. }
  159. const Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
  160. {
  161. AnimationTask* const at = findTaskFor (component);
  162. if (at != 0)
  163. return at->destination;
  164. else if (component != 0)
  165. return component->getBounds();
  166. return Rectangle<int>();
  167. }
  168. bool ComponentAnimator::isAnimating (Component* component) const
  169. {
  170. return findTaskFor (component) != 0;
  171. }
  172. void ComponentAnimator::timerCallback()
  173. {
  174. const uint32 timeNow = Time::getMillisecondCounter();
  175. if (lastTime == 0 || lastTime == timeNow)
  176. lastTime = timeNow;
  177. const int elapsed = timeNow - lastTime;
  178. for (int i = tasks.size(); --i >= 0;)
  179. {
  180. AnimationTask* const at = tasks.getUnchecked(i);
  181. if (! at->useTimeslice (elapsed))
  182. {
  183. tasks.remove (i);
  184. delete at;
  185. sendChangeMessage (this);
  186. }
  187. }
  188. lastTime = timeNow;
  189. if (tasks.size() == 0)
  190. stopTimer();
  191. }
  192. END_JUCE_NAMESPACE