1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24 */
25
26 namespace juce
27 {
28
29 struct ModalComponentManager::ModalItem : public ComponentMovementWatcher
30 {
ModalItemjuce::ModalComponentManager::ModalItem31 ModalItem (Component* comp, bool shouldAutoDelete)
32 : ComponentMovementWatcher (comp),
33 component (comp), autoDelete (shouldAutoDelete)
34 {
35 jassert (comp != nullptr);
36 }
37
componentMovedOrResizedjuce::ModalComponentManager::ModalItem38 void componentMovedOrResized (bool, bool) override {}
39
40 using ComponentMovementWatcher::componentMovedOrResized;
41
componentPeerChangedjuce::ModalComponentManager::ModalItem42 void componentPeerChanged() override
43 {
44 componentVisibilityChanged();
45 }
46
componentVisibilityChangedjuce::ModalComponentManager::ModalItem47 void componentVisibilityChanged() override
48 {
49 if (! component->isShowing())
50 cancel();
51 }
52
53 using ComponentMovementWatcher::componentVisibilityChanged;
54
componentBeingDeletedjuce::ModalComponentManager::ModalItem55 void componentBeingDeleted (Component& comp) override
56 {
57 ComponentMovementWatcher::componentBeingDeleted (comp);
58
59 if (component == &comp || comp.isParentOf (component))
60 {
61 autoDelete = false;
62 cancel();
63 }
64 }
65
canceljuce::ModalComponentManager::ModalItem66 void cancel()
67 {
68 if (isActive)
69 {
70 isActive = false;
71
72 if (auto* mcm = ModalComponentManager::getInstanceWithoutCreating())
73 mcm->triggerAsyncUpdate();
74 }
75 }
76
77 Component* component;
78 OwnedArray<Callback> callbacks;
79 int returnValue = 0;
80 bool isActive = true, autoDelete;
81
82 JUCE_DECLARE_NON_COPYABLE (ModalItem)
83 };
84
85 //==============================================================================
ModalComponentManager()86 ModalComponentManager::ModalComponentManager()
87 {
88 }
89
~ModalComponentManager()90 ModalComponentManager::~ModalComponentManager()
91 {
92 stack.clear();
93 clearSingletonInstance();
94 }
95
JUCE_IMPLEMENT_SINGLETON(ModalComponentManager)96 JUCE_IMPLEMENT_SINGLETON (ModalComponentManager)
97
98
99 //==============================================================================
100 void ModalComponentManager::startModal (Component* component, bool autoDelete)
101 {
102 if (component != nullptr)
103 stack.add (new ModalItem (component, autoDelete));
104 }
105
attachCallback(Component * component,Callback * callback)106 void ModalComponentManager::attachCallback (Component* component, Callback* callback)
107 {
108 if (callback != nullptr)
109 {
110 std::unique_ptr<Callback> callbackDeleter (callback);
111
112 for (int i = stack.size(); --i >= 0;)
113 {
114 auto* item = stack.getUnchecked(i);
115
116 if (item->component == component)
117 {
118 item->callbacks.add (callback);
119 callbackDeleter.release();
120 break;
121 }
122 }
123 }
124 }
125
endModal(Component * component)126 void ModalComponentManager::endModal (Component* component)
127 {
128 for (int i = stack.size(); --i >= 0;)
129 {
130 auto* item = stack.getUnchecked(i);
131
132 if (item->component == component)
133 item->cancel();
134 }
135 }
136
endModal(Component * component,int returnValue)137 void ModalComponentManager::endModal (Component* component, int returnValue)
138 {
139 for (int i = stack.size(); --i >= 0;)
140 {
141 auto* item = stack.getUnchecked(i);
142
143 if (item->component == component)
144 {
145 item->returnValue = returnValue;
146 item->cancel();
147 }
148 }
149 }
150
getNumModalComponents() const151 int ModalComponentManager::getNumModalComponents() const
152 {
153 int n = 0;
154
155 for (auto* item : stack)
156 if (item->isActive)
157 ++n;
158
159 return n;
160 }
161
getModalComponent(int index) const162 Component* ModalComponentManager::getModalComponent (int index) const
163 {
164 int n = 0;
165
166 for (int i = stack.size(); --i >= 0;)
167 {
168 auto* item = stack.getUnchecked(i);
169
170 if (item->isActive)
171 if (n++ == index)
172 return item->component;
173 }
174
175 return nullptr;
176 }
177
isModal(const Component * comp) const178 bool ModalComponentManager::isModal (const Component* comp) const
179 {
180 for (auto* item : stack)
181 if (item->isActive && item->component == comp)
182 return true;
183
184 return false;
185 }
186
isFrontModalComponent(const Component * comp) const187 bool ModalComponentManager::isFrontModalComponent (const Component* comp) const
188 {
189 return comp == getModalComponent (0);
190 }
191
handleAsyncUpdate()192 void ModalComponentManager::handleAsyncUpdate()
193 {
194 for (int i = stack.size(); --i >= 0;)
195 {
196 auto* item = stack.getUnchecked(i);
197
198 if (! item->isActive)
199 {
200 std::unique_ptr<ModalItem> deleter (stack.removeAndReturn (i));
201 Component::SafePointer<Component> compToDelete (item->autoDelete ? item->component : nullptr);
202
203 for (int j = item->callbacks.size(); --j >= 0;)
204 item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue);
205
206 compToDelete.deleteAndZero();
207 }
208 }
209 }
210
bringModalComponentsToFront(bool topOneShouldGrabFocus)211 void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus)
212 {
213 ComponentPeer* lastOne = nullptr;
214
215 for (int i = 0; i < getNumModalComponents(); ++i)
216 {
217 auto* c = getModalComponent (i);
218
219 if (c == nullptr)
220 break;
221
222 if (auto* peer = c->getPeer())
223 {
224 if (peer != lastOne)
225 {
226 if (lastOne == nullptr)
227 {
228 peer->toFront (topOneShouldGrabFocus);
229
230 if (topOneShouldGrabFocus)
231 peer->grabFocus();
232 }
233 else
234 {
235 peer->toBehind (lastOne);
236 }
237
238 lastOne = peer;
239 }
240 }
241 }
242 }
243
cancelAllModalComponents()244 bool ModalComponentManager::cancelAllModalComponents()
245 {
246 auto numModal = getNumModalComponents();
247
248 for (int i = numModal; --i >= 0;)
249 if (auto* c = getModalComponent(i))
250 c->exitModalState (0);
251
252 return numModal > 0;
253 }
254
255 //==============================================================================
256 #if JUCE_MODAL_LOOPS_PERMITTED
runEventLoopForCurrentComponent()257 int ModalComponentManager::runEventLoopForCurrentComponent()
258 {
259 // This can only be run from the message thread!
260 JUCE_ASSERT_MESSAGE_THREAD
261
262 int returnValue = 0;
263
264 if (auto* currentlyModal = getModalComponent (0))
265 {
266 FocusRestorer focusRestorer;
267 bool finished = false;
268
269 attachCallback (currentlyModal, ModalCallbackFunction::create ([&] (int r) { returnValue = r; finished = true; }));
270
271 JUCE_TRY
272 {
273 while (! finished)
274 {
275 if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
276 break;
277 }
278 }
279 JUCE_CATCH_EXCEPTION
280 }
281
282 return returnValue;
283 }
284 #endif
285
286 //==============================================================================
287 struct LambdaCallback : public ModalComponentManager::Callback
288 {
LambdaCallbackjuce::LambdaCallback289 LambdaCallback (std::function<void (int)> fn) noexcept : function (fn) {}
290
modalStateFinishedjuce::LambdaCallback291 void modalStateFinished (int result) override
292 {
293 if (function != nullptr)
294 function (result);
295 }
296
297 std::function<void (int)> function;
298
299 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaCallback)
300 };
301
create(std::function<void (int)> f)302 ModalComponentManager::Callback* ModalCallbackFunction::create (std::function<void (int)> f)
303 {
304 return new LambdaCallback (f);
305 }
306
307 } // namespace juce
308