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.

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