/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2020 - Raw Material Software Limited You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { struct ModalComponentManager::ModalItem : public ComponentMovementWatcher { ModalItem (Component* comp, bool shouldAutoDelete) : ComponentMovementWatcher (comp), component (comp), autoDelete (shouldAutoDelete) { jassert (comp != nullptr); } void componentMovedOrResized (bool, bool) override {} using ComponentMovementWatcher::componentMovedOrResized; void componentPeerChanged() override { componentVisibilityChanged(); } void componentVisibilityChanged() override { if (! component->isShowing()) cancel(); } using ComponentMovementWatcher::componentVisibilityChanged; void componentBeingDeleted (Component& comp) override { ComponentMovementWatcher::componentBeingDeleted (comp); if (component == &comp || comp.isParentOf (component)) { autoDelete = false; cancel(); } } void cancel() { if (isActive) { isActive = false; if (auto* mcm = ModalComponentManager::getInstanceWithoutCreating()) mcm->triggerAsyncUpdate(); } } Component* component; OwnedArray callbacks; int returnValue = 0; bool isActive = true, autoDelete; JUCE_DECLARE_NON_COPYABLE (ModalItem) }; //============================================================================== ModalComponentManager::ModalComponentManager() { } ModalComponentManager::~ModalComponentManager() { stack.clear(); clearSingletonInstance(); } JUCE_IMPLEMENT_SINGLETON (ModalComponentManager) //============================================================================== void ModalComponentManager::startModal (Component* component, bool autoDelete) { if (component != nullptr) stack.add (new ModalItem (component, autoDelete)); } void ModalComponentManager::attachCallback (Component* component, Callback* callback) { if (callback != nullptr) { std::unique_ptr callbackDeleter (callback); for (int i = stack.size(); --i >= 0;) { auto* item = stack.getUnchecked(i); if (item->component == component) { item->callbacks.add (callback); callbackDeleter.release(); break; } } } } void ModalComponentManager::endModal (Component* component) { for (int i = stack.size(); --i >= 0;) { auto* item = stack.getUnchecked(i); if (item->component == component) item->cancel(); } } void ModalComponentManager::endModal (Component* component, int returnValue) { for (int i = stack.size(); --i >= 0;) { auto* item = stack.getUnchecked(i); if (item->component == component) { item->returnValue = returnValue; item->cancel(); } } } int ModalComponentManager::getNumModalComponents() const { int n = 0; for (auto* item : stack) if (item->isActive) ++n; return n; } Component* ModalComponentManager::getModalComponent (int index) const { int n = 0; for (int i = stack.size(); --i >= 0;) { auto* item = stack.getUnchecked(i); if (item->isActive) if (n++ == index) return item->component; } return nullptr; } bool ModalComponentManager::isModal (const Component* comp) const { for (auto* item : stack) if (item->isActive && item->component == comp) return true; return false; } bool ModalComponentManager::isFrontModalComponent (const Component* comp) const { return comp == getModalComponent (0); } void ModalComponentManager::handleAsyncUpdate() { for (int i = stack.size(); --i >= 0;) { auto* item = stack.getUnchecked(i); if (! item->isActive) { std::unique_ptr deleter (stack.removeAndReturn (i)); Component::SafePointer compToDelete (item->autoDelete ? item->component : nullptr); for (int j = item->callbacks.size(); --j >= 0;) item->callbacks.getUnchecked(j)->modalStateFinished (item->returnValue); compToDelete.deleteAndZero(); } } } void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFocus) { ComponentPeer* lastOne = nullptr; for (int i = 0; i < getNumModalComponents(); ++i) { auto* c = getModalComponent (i); if (c == nullptr) break; if (auto* peer = c->getPeer()) { if (peer != lastOne) { if (lastOne == nullptr) { peer->toFront (topOneShouldGrabFocus); if (topOneShouldGrabFocus) peer->grabFocus(); } else { peer->toBehind (lastOne); } lastOne = peer; } } } } bool ModalComponentManager::cancelAllModalComponents() { auto numModal = getNumModalComponents(); for (int i = numModal; --i >= 0;) if (auto* c = getModalComponent(i)) c->exitModalState (0); return numModal > 0; } //============================================================================== #if JUCE_MODAL_LOOPS_PERMITTED int ModalComponentManager::runEventLoopForCurrentComponent() { // This can only be run from the message thread! JUCE_ASSERT_MESSAGE_THREAD int returnValue = 0; if (auto* currentlyModal = getModalComponent (0)) { FocusRestorer focusRestorer; bool finished = false; attachCallback (currentlyModal, ModalCallbackFunction::create ([&] (int r) { returnValue = r; finished = true; })); JUCE_TRY { while (! finished) { if (! MessageManager::getInstance()->runDispatchLoopUntil (20)) break; } } JUCE_CATCH_EXCEPTION } return returnValue; } #endif //============================================================================== struct LambdaCallback : public ModalComponentManager::Callback { LambdaCallback (std::function fn) noexcept : function (fn) {} void modalStateFinished (int result) override { if (function != nullptr) function (result); } std::function function; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaCallback) }; ModalComponentManager::Callback* ModalCallbackFunction::create (std::function f) { return new LambdaCallback (f); } } // namespace juce