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.

246 lines
7.5KB

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