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.

298 lines
7.6KB

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