|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2013 - Raw Material Software Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
- */
-
- /** Keeps track of the active top level window. */
- class TopLevelWindowManager : private Timer,
- private DeletedAtShutdown
- {
- public:
- //==============================================================================
- TopLevelWindowManager() : currentActive (nullptr)
- {
- }
-
- ~TopLevelWindowManager()
- {
- clearSingletonInstance();
- }
-
- juce_DeclareSingleton_SingleThreaded_Minimal (TopLevelWindowManager);
-
- void checkFocusAsync()
- {
- startTimer (10);
- }
-
- void checkFocus()
- {
- startTimer (jmin (1731, getTimerInterval() * 2));
-
- TopLevelWindow* active = nullptr;
-
- if (Process::isForegroundProcess())
- {
- active = currentActive;
-
- Component* const c = Component::getCurrentlyFocusedComponent();
- TopLevelWindow* tlw = dynamic_cast <TopLevelWindow*> (c);
-
- if (tlw == nullptr && c != nullptr)
- tlw = c->findParentComponentOfClass<TopLevelWindow>();
-
- if (tlw != nullptr)
- active = tlw;
- }
-
- if (active != currentActive)
- {
- currentActive = active;
-
- for (int i = windows.size(); --i >= 0;)
- {
- TopLevelWindow* const tlw = windows.getUnchecked (i);
- tlw->setWindowActive (isWindowActive (tlw));
-
- i = jmin (i, windows.size() - 1);
- }
-
- Desktop::getInstance().triggerFocusCallback();
- }
- }
-
- bool addWindow (TopLevelWindow* const w)
- {
- windows.add (w);
- checkFocusAsync();
-
- return isWindowActive (w);
- }
-
- void removeWindow (TopLevelWindow* const w)
- {
- checkFocusAsync();
-
- if (currentActive == w)
- currentActive = nullptr;
-
- windows.removeFirstMatchingValue (w);
-
- if (windows.size() == 0)
- deleteInstance();
- }
-
- Array <TopLevelWindow*> windows;
-
- private:
- TopLevelWindow* currentActive;
-
- void timerCallback() override
- {
- checkFocus();
- }
-
- bool isWindowActive (TopLevelWindow* const tlw) const
- {
- return (tlw == currentActive
- || tlw->isParentOf (currentActive)
- || tlw->hasKeyboardFocus (true))
- && tlw->isShowing();
- }
-
- JUCE_DECLARE_NON_COPYABLE (TopLevelWindowManager)
- };
-
- juce_ImplementSingleton_SingleThreaded (TopLevelWindowManager)
-
- void juce_checkCurrentlyFocusedTopLevelWindow();
- void juce_checkCurrentlyFocusedTopLevelWindow()
- {
- if (TopLevelWindowManager* const wm = TopLevelWindowManager::getInstanceWithoutCreating())
- wm->checkFocusAsync();
- }
-
- //==============================================================================
- TopLevelWindow::TopLevelWindow (const String& name, const bool shouldAddToDesktop)
- : Component (name),
- useDropShadow (true),
- useNativeTitleBar (false),
- isCurrentlyActive (false)
- {
- setOpaque (true);
-
- if (shouldAddToDesktop)
- Component::addToDesktop (TopLevelWindow::getDesktopWindowStyleFlags());
- else
- setDropShadowEnabled (true);
-
- setWantsKeyboardFocus (true);
- setBroughtToFrontOnMouseClick (true);
- isCurrentlyActive = TopLevelWindowManager::getInstance()->addWindow (this);
- }
-
- TopLevelWindow::~TopLevelWindow()
- {
- shadower = nullptr;
- TopLevelWindowManager::getInstance()->removeWindow (this);
- }
-
- //==============================================================================
- void TopLevelWindow::focusOfChildComponentChanged (FocusChangeType)
- {
- TopLevelWindowManager* const wm = TopLevelWindowManager::getInstance();
-
- if (hasKeyboardFocus (true))
- wm->checkFocus();
- else
- wm->checkFocusAsync();
- }
-
- void TopLevelWindow::setWindowActive (const bool isNowActive)
- {
- if (isCurrentlyActive != isNowActive)
- {
- isCurrentlyActive = isNowActive;
- activeWindowStatusChanged();
- }
- }
-
- void TopLevelWindow::activeWindowStatusChanged()
- {
- }
-
- bool TopLevelWindow::isUsingNativeTitleBar() const noexcept
- {
- return useNativeTitleBar && (isOnDesktop() || ! isShowing());
- }
-
- void TopLevelWindow::visibilityChanged()
- {
- if (isShowing()
- && (getPeer()->getStyleFlags() & (ComponentPeer::windowIsTemporary
- | ComponentPeer::windowIgnoresKeyPresses)) == 0)
- {
- toFront (true);
- }
- }
-
- void TopLevelWindow::parentHierarchyChanged()
- {
- setDropShadowEnabled (useDropShadow);
- }
-
- int TopLevelWindow::getDesktopWindowStyleFlags() const
- {
- int styleFlags = ComponentPeer::windowAppearsOnTaskbar;
-
- if (useDropShadow) styleFlags |= ComponentPeer::windowHasDropShadow;
- if (useNativeTitleBar) styleFlags |= ComponentPeer::windowHasTitleBar;
-
- return styleFlags;
- }
-
- void TopLevelWindow::setDropShadowEnabled (const bool useShadow)
- {
- useDropShadow = useShadow;
-
- if (isOnDesktop())
- {
- shadower = nullptr;
- Component::addToDesktop (getDesktopWindowStyleFlags());
- }
- else
- {
- if (useShadow && isOpaque())
- {
- if (shadower == nullptr)
- {
- shadower = getLookAndFeel().createDropShadowerForComponent (this);
-
- if (shadower != nullptr)
- shadower->setOwner (this);
- }
- }
- else
- {
- shadower = nullptr;
- }
- }
- }
-
- void TopLevelWindow::setUsingNativeTitleBar (const bool shouldUseNativeTitleBar)
- {
- if (useNativeTitleBar != shouldUseNativeTitleBar)
- {
- useNativeTitleBar = shouldUseNativeTitleBar;
- recreateDesktopWindow();
- sendLookAndFeelChange();
- }
- }
-
- void TopLevelWindow::recreateDesktopWindow()
- {
- if (isOnDesktop())
- {
- Component::addToDesktop (getDesktopWindowStyleFlags());
- toFront (true);
- }
- }
-
- void TopLevelWindow::addToDesktop()
- {
- shadower = nullptr;
- Component::addToDesktop (getDesktopWindowStyleFlags());
- setDropShadowEnabled (isDropShadowEnabled()); // force an update to clear away any fake shadows if necessary.
- }
-
- void TopLevelWindow::addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo)
- {
- /* It's not recommended to change the desktop window flags directly for a TopLevelWindow,
- because this class needs to make sure its layout corresponds with settings like whether
- it's got a native title bar or not.
-
- If you need custom flags for your window, you can override the getDesktopWindowStyleFlags()
- method. If you do this, it's best to call the base class's getDesktopWindowStyleFlags()
- method, then add or remove whatever flags are necessary from this value before returning it.
- */
-
- jassert ((windowStyleFlags & ~ComponentPeer::windowIsSemiTransparent)
- == (getDesktopWindowStyleFlags() & ~ComponentPeer::windowIsSemiTransparent));
-
- Component::addToDesktop (windowStyleFlags, nativeWindowToAttachTo);
-
- if (windowStyleFlags != getDesktopWindowStyleFlags())
- sendLookAndFeelChange();
- }
-
- //==============================================================================
- void TopLevelWindow::centreAroundComponent (Component* c, const int width, const int height)
- {
- if (c == nullptr)
- c = TopLevelWindow::getActiveTopLevelWindow();
-
- if (c == nullptr || c->getBounds().isEmpty())
- {
- centreWithSize (width, height);
- }
- else
- {
- Point<int> targetCentre (c->localPointToGlobal (c->getLocalBounds().getCentre()));
- Rectangle<int> parentArea (c->getParentMonitorArea());
-
- if (Component* const parent = getParentComponent())
- {
- targetCentre = parent->getLocalPoint (nullptr, targetCentre);
- parentArea = parent->getLocalBounds();
- }
-
- setBounds (Rectangle<int> (targetCentre.x - width / 2,
- targetCentre.y - height / 2,
- width, height)
- .constrainedWithin (parentArea.reduced (12, 12)));
- }
- }
-
- //==============================================================================
- int TopLevelWindow::getNumTopLevelWindows() noexcept
- {
- return TopLevelWindowManager::getInstance()->windows.size();
- }
-
- TopLevelWindow* TopLevelWindow::getTopLevelWindow (const int index) noexcept
- {
- return TopLevelWindowManager::getInstance()->windows [index];
- }
-
- TopLevelWindow* TopLevelWindow::getActiveTopLevelWindow() noexcept
- {
- TopLevelWindow* best = nullptr;
- int bestNumTWLParents = -1;
-
- for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
- {
- TopLevelWindow* const tlw = TopLevelWindow::getTopLevelWindow (i);
-
- if (tlw->isActiveWindow())
- {
- int numTWLParents = 0;
-
- for (const Component* c = tlw->getParentComponent(); c != nullptr; c = c->getParentComponent())
- if (dynamic_cast<const TopLevelWindow*> (c) != nullptr)
- ++numTWLParents;
-
- if (bestNumTWLParents < numTWLParents)
- {
- best = tlw;
- bestNumTWLParents = numTWLParents;
- }
- }
- }
-
- return best;
- }
|