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.

314 lines
8.2KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. class ModalComponentManager::ModalItem : public ComponentMovementWatcher
  20. {
  21. public:
  22. ModalItem (Component* const comp, const bool autoDelete_)
  23. : ComponentMovementWatcher (comp),
  24. component (comp), returnValue (0),
  25. isActive (true), autoDelete (autoDelete_)
  26. {
  27. jassert (comp != nullptr);
  28. }
  29. void componentMovedOrResized (bool, bool) override {}
  30. void componentPeerChanged() override
  31. {
  32. if (! component->isShowing())
  33. cancel();
  34. }
  35. void componentVisibilityChanged() override
  36. {
  37. if (! component->isShowing())
  38. cancel();
  39. }
  40. void componentBeingDeleted (Component& comp) override
  41. {
  42. ComponentMovementWatcher::componentBeingDeleted (comp);
  43. if (component == &comp || comp.isParentOf (component))
  44. {
  45. autoDelete = false;
  46. cancel();
  47. }
  48. }
  49. void cancel()
  50. {
  51. if (isActive)
  52. {
  53. isActive = false;
  54. if (ModalComponentManager* mcm = ModalComponentManager::getInstanceWithoutCreating())
  55. mcm->triggerAsyncUpdate();
  56. }
  57. }
  58. Component* component;
  59. OwnedArray<Callback> callbacks;
  60. int returnValue;
  61. bool isActive, autoDelete;
  62. private:
  63. JUCE_DECLARE_NON_COPYABLE (ModalItem)
  64. };
  65. //==============================================================================
  66. ModalComponentManager::ModalComponentManager()
  67. {
  68. }
  69. ModalComponentManager::~ModalComponentManager()
  70. {
  71. stack.clear();
  72. clearSingletonInstance();
  73. }
  74. juce_ImplementSingleton_SingleThreaded (ModalComponentManager)
  75. //==============================================================================
  76. void ModalComponentManager::startModal (Component* component, bool autoDelete)
  77. {
  78. if (component != nullptr)
  79. stack.add (new ModalItem (component, autoDelete));
  80. }
  81. void ModalComponentManager::attachCallback (Component* component, Callback* callback)
  82. {
  83. if (callback != nullptr)
  84. {
  85. ScopedPointer<Callback> callbackDeleter (callback);
  86. for (int i = stack.size(); --i >= 0;)
  87. {
  88. ModalItem* const item = stack.getUnchecked(i);
  89. if (item->component == component)
  90. {
  91. item->callbacks.add (callback);
  92. callbackDeleter.release();
  93. break;
  94. }
  95. }
  96. }
  97. }
  98. void ModalComponentManager::endModal (Component* component)
  99. {
  100. for (int i = stack.size(); --i >= 0;)
  101. {
  102. ModalItem* const item = stack.getUnchecked(i);
  103. if (item->component == component)
  104. item->cancel();
  105. }
  106. }
  107. void ModalComponentManager::endModal (Component* component, int returnValue)
  108. {
  109. for (int i = stack.size(); --i >= 0;)
  110. {
  111. ModalItem* const item = stack.getUnchecked(i);
  112. if (item->component == component)
  113. {
  114. item->returnValue = returnValue;
  115. item->cancel();
  116. }
  117. }
  118. }
  119. int ModalComponentManager::getNumModalComponents() const
  120. {
  121. int n = 0;
  122. for (int i = 0; i < stack.size(); ++i)
  123. if (stack.getUnchecked(i)->isActive)
  124. ++n;
  125. return n;
  126. }
  127. Component* ModalComponentManager::getModalComponent (const int index) const
  128. {
  129. int n = 0;
  130. for (int i = stack.size(); --i >= 0;)
  131. {
  132. const ModalItem* const item = stack.getUnchecked(i);
  133. if (item->isActive)
  134. if (n++ == index)
  135. return item->component;
  136. }
  137. return nullptr;
  138. }
  139. bool ModalComponentManager::isModal (Component* const comp) const
  140. {
  141. for (int i = stack.size(); --i >= 0;)
  142. {
  143. const ModalItem* const item = stack.getUnchecked(i);
  144. if (item->isActive && item->component == comp)
  145. return true;
  146. }
  147. return false;
  148. }
  149. bool ModalComponentManager::isFrontModalComponent (Component* const comp) const
  150. {
  151. return comp == getModalComponent (0);
  152. }
  153. void ModalComponentManager::handleAsyncUpdate()
  154. {
  155. for (int i = stack.size(); --i >= 0;)
  156. {
  157. const ModalItem* const item = stack.getUnchecked(i);
  158. if (! item->isActive)
  159. {
  160. ScopedPointer<ModalItem> deleter (stack.removeAndReturn (i));
  161. Component::SafePointer<Component> compToDelete (item->autoDelete ? item->component : nullptr);
  162. for (int j = item->callbacks.size(); --j >= 0;)
  163. item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
  164. compToDelete.deleteAndZero();
  165. }
  166. }
  167. }
  168. void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus)
  169. {
  170. ComponentPeer* lastOne = nullptr;
  171. for (int i = 0; i < getNumModalComponents(); ++i)
  172. {
  173. Component* const c = getModalComponent (i);
  174. if (c == nullptr)
  175. break;
  176. ComponentPeer* peer = c->getPeer();
  177. if (peer != nullptr && peer != lastOne)
  178. {
  179. if (lastOne == nullptr)
  180. {
  181. peer->toFront (topOneShouldGrabFocus);
  182. if (topOneShouldGrabFocus)
  183. peer->grabFocus();
  184. }
  185. else
  186. peer->toBehind (lastOne);
  187. lastOne = peer;
  188. }
  189. }
  190. }
  191. bool ModalComponentManager::cancelAllModalComponents()
  192. {
  193. const int numModal = getNumModalComponents();
  194. for (int i = numModal; --i >= 0;)
  195. if (Component* const c = getModalComponent(i))
  196. c->exitModalState (0);
  197. return numModal > 0;
  198. }
  199. //==============================================================================
  200. #if JUCE_MODAL_LOOPS_PERMITTED
  201. class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback
  202. {
  203. public:
  204. ReturnValueRetriever (int& v, bool& done) : value (v), finished (done) {}
  205. void modalStateFinished (int returnValue) override
  206. {
  207. finished = true;
  208. value = returnValue;
  209. }
  210. private:
  211. int& value;
  212. bool& finished;
  213. JUCE_DECLARE_NON_COPYABLE (ReturnValueRetriever)
  214. };
  215. int ModalComponentManager::runEventLoopForCurrentComponent()
  216. {
  217. // This can only be run from the message thread!
  218. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  219. int returnValue = 0;
  220. if (Component* currentlyModal = getModalComponent (0))
  221. {
  222. FocusRestorer focusRestorer;
  223. bool finished = false;
  224. attachCallback (currentlyModal, new ReturnValueRetriever (returnValue, finished));
  225. JUCE_TRY
  226. {
  227. while (! finished)
  228. {
  229. if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
  230. break;
  231. }
  232. }
  233. JUCE_CATCH_EXCEPTION
  234. }
  235. return returnValue;
  236. }
  237. #endif
  238. //==============================================================================
  239. struct LambdaCallback : public ModalComponentManager::Callback
  240. {
  241. LambdaCallback (std::function<void(int)> fn) noexcept : function (fn) {}
  242. void modalStateFinished (int result) override { function (result); }
  243. std::function<void(int)> function;
  244. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaCallback)
  245. };
  246. ModalComponentManager::Callback* ModalCallbackFunction::create (std::function<void(int)> f)
  247. {
  248. return new LambdaCallback (f);
  249. }