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