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.

300 lines
7.6KB

  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. namespace juce
  20. {
  21. struct ModalComponentManager::ModalItem : public ComponentMovementWatcher
  22. {
  23. ModalItem (Component* comp, bool shouldAutoDelete)
  24. : ComponentMovementWatcher (comp),
  25. component (comp), autoDelete (shouldAutoDelete)
  26. {
  27. jassert (comp != nullptr);
  28. }
  29. void componentMovedOrResized (bool, bool) override {}
  30. void componentPeerChanged() override
  31. {
  32. componentVisibilityChanged();
  33. }
  34. void componentVisibilityChanged() override
  35. {
  36. if (! component->isShowing())
  37. cancel();
  38. }
  39. void componentBeingDeleted (Component& comp) override
  40. {
  41. ComponentMovementWatcher::componentBeingDeleted (comp);
  42. if (component == &comp || comp.isParentOf (component))
  43. {
  44. autoDelete = false;
  45. cancel();
  46. }
  47. }
  48. void cancel()
  49. {
  50. if (isActive)
  51. {
  52. isActive = false;
  53. if (auto* mcm = ModalComponentManager::getInstanceWithoutCreating())
  54. mcm->triggerAsyncUpdate();
  55. }
  56. }
  57. Component* component;
  58. OwnedArray<Callback> callbacks;
  59. int returnValue = 0;
  60. bool isActive = true, autoDelete;
  61. JUCE_DECLARE_NON_COPYABLE (ModalItem)
  62. };
  63. //==============================================================================
  64. ModalComponentManager::ModalComponentManager()
  65. {
  66. }
  67. ModalComponentManager::~ModalComponentManager()
  68. {
  69. stack.clear();
  70. clearSingletonInstance();
  71. }
  72. JUCE_IMPLEMENT_SINGLETON (ModalComponentManager)
  73. //==============================================================================
  74. void ModalComponentManager::startModal (Component* component, bool autoDelete)
  75. {
  76. if (component != nullptr)
  77. stack.add (new ModalItem (component, autoDelete));
  78. }
  79. void ModalComponentManager::attachCallback (Component* component, Callback* callback)
  80. {
  81. if (callback != nullptr)
  82. {
  83. std::unique_ptr<Callback> callbackDeleter (callback);
  84. for (int i = stack.size(); --i >= 0;)
  85. {
  86. auto* item = stack.getUnchecked(i);
  87. if (item->component == component)
  88. {
  89. item->callbacks.add (callback);
  90. callbackDeleter.release();
  91. break;
  92. }
  93. }
  94. }
  95. }
  96. void ModalComponentManager::endModal (Component* component)
  97. {
  98. for (int i = stack.size(); --i >= 0;)
  99. {
  100. auto* item = stack.getUnchecked(i);
  101. if (item->component == component)
  102. item->cancel();
  103. }
  104. }
  105. void ModalComponentManager::endModal (Component* component, int returnValue)
  106. {
  107. for (int i = stack.size(); --i >= 0;)
  108. {
  109. auto* item = stack.getUnchecked(i);
  110. if (item->component == component)
  111. {
  112. item->returnValue = returnValue;
  113. item->cancel();
  114. }
  115. }
  116. }
  117. int ModalComponentManager::getNumModalComponents() const
  118. {
  119. int n = 0;
  120. for (auto* item : stack)
  121. if (item->isActive)
  122. ++n;
  123. return n;
  124. }
  125. Component* ModalComponentManager::getModalComponent (int index) const
  126. {
  127. int n = 0;
  128. for (int i = stack.size(); --i >= 0;)
  129. {
  130. auto* item = stack.getUnchecked(i);
  131. if (item->isActive)
  132. if (n++ == index)
  133. return item->component;
  134. }
  135. return nullptr;
  136. }
  137. bool ModalComponentManager::isModal (const Component* comp) const
  138. {
  139. for (auto* item : stack)
  140. if (item->isActive && item->component == comp)
  141. return true;
  142. return false;
  143. }
  144. bool ModalComponentManager::isFrontModalComponent (const Component* comp) const
  145. {
  146. return comp == getModalComponent (0);
  147. }
  148. void ModalComponentManager::handleAsyncUpdate()
  149. {
  150. for (int i = stack.size(); --i >= 0;)
  151. {
  152. auto* item = stack.getUnchecked(i);
  153. if (! item->isActive)
  154. {
  155. std::unique_ptr<ModalItem> deleter (stack.removeAndReturn (i));
  156. Component::SafePointer<Component> compToDelete (item->autoDelete ? item->component : nullptr);
  157. for (int j = item->callbacks.size(); --j >= 0;)
  158. item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
  159. compToDelete.deleteAndZero();
  160. }
  161. }
  162. }
  163. void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus)
  164. {
  165. ComponentPeer* lastOne = nullptr;
  166. for (int i = 0; i < getNumModalComponents(); ++i)
  167. {
  168. auto* c = getModalComponent (i);
  169. if (c == nullptr)
  170. break;
  171. if (auto* peer = c->getPeer())
  172. {
  173. if (peer != lastOne)
  174. {
  175. if (lastOne == nullptr)
  176. {
  177. peer->toFront (topOneShouldGrabFocus);
  178. if (topOneShouldGrabFocus)
  179. peer->grabFocus();
  180. }
  181. else
  182. {
  183. peer->toBehind (lastOne);
  184. }
  185. lastOne = peer;
  186. }
  187. }
  188. }
  189. }
  190. bool ModalComponentManager::cancelAllModalComponents()
  191. {
  192. auto numModal = getNumModalComponents();
  193. for (int i = numModal; --i >= 0;)
  194. if (auto* c = getModalComponent(i))
  195. c->exitModalState (0);
  196. return numModal > 0;
  197. }
  198. //==============================================================================
  199. #if JUCE_MODAL_LOOPS_PERMITTED
  200. int ModalComponentManager::runEventLoopForCurrentComponent()
  201. {
  202. // This can only be run from the message thread!
  203. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  204. int returnValue = 0;
  205. if (auto* currentlyModal = getModalComponent (0))
  206. {
  207. FocusRestorer focusRestorer;
  208. bool finished = false;
  209. attachCallback (currentlyModal, ModalCallbackFunction::create ([&] (int r) { returnValue = r; finished = true; }));
  210. JUCE_TRY
  211. {
  212. while (! finished)
  213. {
  214. if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
  215. break;
  216. }
  217. }
  218. JUCE_CATCH_EXCEPTION
  219. }
  220. return returnValue;
  221. }
  222. #endif
  223. //==============================================================================
  224. struct LambdaCallback : public ModalComponentManager::Callback
  225. {
  226. LambdaCallback (std::function<void(int)> fn) noexcept : function (fn) {}
  227. void modalStateFinished (int result) override { function (result); }
  228. std::function<void(int)> function;
  229. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaCallback)
  230. };
  231. ModalComponentManager::Callback* ModalCallbackFunction::create (std::function<void(int)> f)
  232. {
  233. return new LambdaCallback (f);
  234. }
  235. } // namespace juce