migrating to the latest JUCE version
This commit is contained in:
		@@ -1,348 +1,368 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
Desktop::Desktop()
 | 
			
		||||
    : mouseSources (new MouseInputSource::SourceList()),
 | 
			
		||||
      masterScaleFactor ((float) getDefaultMasterScale()),
 | 
			
		||||
      nativeDarkModeChangeDetectorImpl (createNativeDarkModeChangeDetectorImpl())
 | 
			
		||||
{
 | 
			
		||||
    displays.reset (new Displays (*this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Desktop::~Desktop()
 | 
			
		||||
{
 | 
			
		||||
    setScreenSaverEnabled (true);
 | 
			
		||||
    animator.cancelAllAnimations (false);
 | 
			
		||||
 | 
			
		||||
    jassert (instance == this);
 | 
			
		||||
    instance = nullptr;
 | 
			
		||||
 | 
			
		||||
    // doh! If you don't delete all your windows before exiting, you're going to
 | 
			
		||||
    // be leaking memory!
 | 
			
		||||
    jassert (desktopComponents.size() == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Desktop& JUCE_CALLTYPE Desktop::getInstance()
 | 
			
		||||
{
 | 
			
		||||
    if (instance == nullptr)
 | 
			
		||||
        instance = new Desktop();
 | 
			
		||||
 | 
			
		||||
    return *instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Desktop* Desktop::instance = nullptr;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
int Desktop::getNumComponents() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return desktopComponents.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* Desktop::getComponent (int index) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return desktopComponents [index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* Desktop::findComponentAt (Point<int> screenPosition) const
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    for (int i = desktopComponents.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        auto* c = desktopComponents.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (c->isVisible())
 | 
			
		||||
        {
 | 
			
		||||
            auto relative = c->getLocalPoint (nullptr, screenPosition);
 | 
			
		||||
 | 
			
		||||
            if (c->contains (relative))
 | 
			
		||||
                return c->getComponentAt (relative);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto lf = currentLookAndFeel.get())
 | 
			
		||||
        return *lf;
 | 
			
		||||
 | 
			
		||||
    if (defaultLookAndFeel == nullptr)
 | 
			
		||||
        defaultLookAndFeel.reset (new LookAndFeel_V4());
 | 
			
		||||
 | 
			
		||||
    auto lf = defaultLookAndFeel.get();
 | 
			
		||||
    jassert (lf != nullptr);
 | 
			
		||||
    currentLookAndFeel = lf;
 | 
			
		||||
    return *lf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    currentLookAndFeel = newDefaultLookAndFeel;
 | 
			
		||||
 | 
			
		||||
    for (int i = getNumComponents(); --i >= 0;)
 | 
			
		||||
        if (auto* c = getComponent (i))
 | 
			
		||||
            c->sendLookAndFeelChange();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::addDesktopComponent (Component* c)
 | 
			
		||||
{
 | 
			
		||||
    jassert (c != nullptr);
 | 
			
		||||
    jassert (! desktopComponents.contains (c));
 | 
			
		||||
    desktopComponents.addIfNotAlreadyThere (c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::removeDesktopComponent (Component* c)
 | 
			
		||||
{
 | 
			
		||||
    desktopComponents.removeFirstMatchingValue (c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::componentBroughtToFront (Component* c)
 | 
			
		||||
{
 | 
			
		||||
    auto index = desktopComponents.indexOf (c);
 | 
			
		||||
    jassert (index >= 0);
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        int newIndex = -1;
 | 
			
		||||
 | 
			
		||||
        if (! c->isAlwaysOnTop())
 | 
			
		||||
        {
 | 
			
		||||
            newIndex = desktopComponents.size();
 | 
			
		||||
 | 
			
		||||
            while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
 | 
			
		||||
                --newIndex;
 | 
			
		||||
 | 
			
		||||
            --newIndex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        desktopComponents.move (index, newIndex);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
Point<int> Desktop::getMousePosition()
 | 
			
		||||
{
 | 
			
		||||
    return getMousePositionFloat().roundToInt();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point<float> Desktop::getMousePositionFloat()
 | 
			
		||||
{
 | 
			
		||||
    return getInstance().getMainMouseSource().getScreenPosition();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::setMousePosition (Point<int> newPosition)
 | 
			
		||||
{
 | 
			
		||||
    getInstance().getMainMouseSource().setScreenPosition (newPosition.toFloat());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point<int> Desktop::getLastMouseDownPosition()
 | 
			
		||||
{
 | 
			
		||||
    return getInstance().getMainMouseSource().getLastMouseDownPosition().roundToInt();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Desktop::getMouseButtonClickCounter() const noexcept    { return mouseClickCounter; }
 | 
			
		||||
int Desktop::getMouseWheelMoveCounter() const noexcept      { return mouseWheelCounter; }
 | 
			
		||||
 | 
			
		||||
void Desktop::incrementMouseClickCounter() noexcept         { ++mouseClickCounter; }
 | 
			
		||||
void Desktop::incrementMouseWheelCounter() noexcept         { ++mouseWheelCounter; }
 | 
			
		||||
 | 
			
		||||
const Array<MouseInputSource>& Desktop::getMouseSources() const noexcept        { return mouseSources->sourceArray; }
 | 
			
		||||
int Desktop::getNumMouseSources() const noexcept                                { return mouseSources->sources.size(); }
 | 
			
		||||
int Desktop::getNumDraggingMouseSources() const noexcept                        { return mouseSources->getNumDraggingMouseSources(); }
 | 
			
		||||
MouseInputSource* Desktop::getMouseSource (int index) const noexcept            { return mouseSources->getMouseSource (index); }
 | 
			
		||||
MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept    { return mouseSources->getDraggingMouseSource (index); }
 | 
			
		||||
MouseInputSource Desktop::getMainMouseSource() const noexcept                   { return MouseInputSource (mouseSources->sources.getUnchecked(0)); }
 | 
			
		||||
void Desktop::beginDragAutoRepeat (int interval)                                { mouseSources->beginDragAutoRepeat (interval); }
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::addFocusChangeListener    (FocusChangeListener* l)   { focusListeners.add (l); }
 | 
			
		||||
void Desktop::removeFocusChangeListener (FocusChangeListener* l)   { focusListeners.remove (l); }
 | 
			
		||||
void Desktop::triggerFocusCallback()                               { triggerAsyncUpdate(); }
 | 
			
		||||
 | 
			
		||||
void Desktop::handleAsyncUpdate()
 | 
			
		||||
{
 | 
			
		||||
    // The component may be deleted during this operation, but we'll use a SafePointer rather than a
 | 
			
		||||
    // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
 | 
			
		||||
    focusListeners.call ([currentFocus = WeakReference<Component> { Component::getCurrentlyFocusedComponent() }] (FocusChangeListener& l)
 | 
			
		||||
    {
 | 
			
		||||
        l.globalFocusChanged (currentFocus.get());
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::addDarkModeSettingListener    (DarkModeSettingListener* l)  { darkModeSettingListeners.add (l); }
 | 
			
		||||
void Desktop::removeDarkModeSettingListener (DarkModeSettingListener* l)  { darkModeSettingListeners.remove (l); }
 | 
			
		||||
 | 
			
		||||
void Desktop::darkModeChanged()  { darkModeSettingListeners.call ([] (DarkModeSettingListener& l) { l.darkModeSettingChanged(); }); }
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::resetTimer()
 | 
			
		||||
{
 | 
			
		||||
    if (mouseListeners.size() == 0)
 | 
			
		||||
        stopTimer();
 | 
			
		||||
    else
 | 
			
		||||
        startTimer (100);
 | 
			
		||||
 | 
			
		||||
    lastFakeMouseMove = getMousePositionFloat();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ListenerList<MouseListener>& Desktop::getMouseListeners()
 | 
			
		||||
{
 | 
			
		||||
    resetTimer();
 | 
			
		||||
    return mouseListeners;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::addGlobalMouseListener (MouseListener* listener)
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    mouseListeners.add (listener);
 | 
			
		||||
    resetTimer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::removeGlobalMouseListener (MouseListener* listener)
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    mouseListeners.remove (listener);
 | 
			
		||||
    resetTimer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::timerCallback()
 | 
			
		||||
{
 | 
			
		||||
    if (lastFakeMouseMove != getMousePositionFloat())
 | 
			
		||||
        sendMouseMove();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::sendMouseMove()
 | 
			
		||||
{
 | 
			
		||||
    if (! mouseListeners.isEmpty())
 | 
			
		||||
    {
 | 
			
		||||
        startTimer (20);
 | 
			
		||||
 | 
			
		||||
        lastFakeMouseMove = getMousePositionFloat();
 | 
			
		||||
 | 
			
		||||
        if (auto* target = findComponentAt (lastFakeMouseMove.roundToInt()))
 | 
			
		||||
        {
 | 
			
		||||
            Component::BailOutChecker checker (target);
 | 
			
		||||
            auto pos = target->getLocalPoint (nullptr, lastFakeMouseMove);
 | 
			
		||||
            auto now = Time::getCurrentTime();
 | 
			
		||||
 | 
			
		||||
            const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::currentModifiers, MouseInputSource::invalidPressure,
 | 
			
		||||
                                 MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
 | 
			
		||||
                                 MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
 | 
			
		||||
                                 target, target, now, pos, now, 0, false);
 | 
			
		||||
 | 
			
		||||
            if (me.mods.isAnyMouseButtonDown())
 | 
			
		||||
                mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (me); });
 | 
			
		||||
            else
 | 
			
		||||
                mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMove (me); });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::setKioskModeComponent (Component* componentToUse, bool allowMenusAndBars)
 | 
			
		||||
{
 | 
			
		||||
    if (kioskModeReentrant)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    const ScopedValueSetter<bool> setter (kioskModeReentrant, true, false);
 | 
			
		||||
 | 
			
		||||
    if (kioskModeComponent != componentToUse)
 | 
			
		||||
    {
 | 
			
		||||
        // agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
 | 
			
		||||
        jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
 | 
			
		||||
 | 
			
		||||
        if (auto* oldKioskComp = kioskModeComponent)
 | 
			
		||||
        {
 | 
			
		||||
            kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one)
 | 
			
		||||
            setKioskComponent (oldKioskComp, false, allowMenusAndBars);
 | 
			
		||||
            oldKioskComp->setBounds (kioskComponentOriginalBounds);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        kioskModeComponent = componentToUse;
 | 
			
		||||
 | 
			
		||||
        if (kioskModeComponent != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            // Only components that are already on the desktop can be put into kiosk mode!
 | 
			
		||||
            jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
 | 
			
		||||
 | 
			
		||||
            kioskComponentOriginalBounds = kioskModeComponent->getBounds();
 | 
			
		||||
            setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::setOrientationsEnabled (int newOrientations)
 | 
			
		||||
{
 | 
			
		||||
    if (allowedOrientations != newOrientations)
 | 
			
		||||
    {
 | 
			
		||||
        // Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
 | 
			
		||||
        jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
 | 
			
		||||
 | 
			
		||||
        allowedOrientations = newOrientations;
 | 
			
		||||
        allowedOrientationsChanged();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Desktop::getOrientationsEnabled() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return allowedOrientations;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Desktop::isOrientationEnabled (DisplayOrientation orientation) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    // Make sure you only pass one valid flag in here...
 | 
			
		||||
    jassert (orientation == upright || orientation == upsideDown
 | 
			
		||||
              || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
 | 
			
		||||
 | 
			
		||||
    return (allowedOrientations & orientation) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    if (masterScaleFactor != newScaleFactor)
 | 
			
		||||
    {
 | 
			
		||||
        masterScaleFactor = newScaleFactor;
 | 
			
		||||
        displays->refresh();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Desktop::isHeadless() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return displays->displays.isEmpty();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
Desktop::Desktop()
 | 
			
		||||
    : mouseSources (new MouseInputSource::SourceList()),
 | 
			
		||||
      masterScaleFactor ((float) getDefaultMasterScale()),
 | 
			
		||||
      nativeDarkModeChangeDetectorImpl (createNativeDarkModeChangeDetectorImpl())
 | 
			
		||||
{
 | 
			
		||||
    displays.reset (new Displays (*this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Desktop::~Desktop()
 | 
			
		||||
{
 | 
			
		||||
    setScreenSaverEnabled (true);
 | 
			
		||||
    animator.cancelAllAnimations (false);
 | 
			
		||||
 | 
			
		||||
    jassert (instance == this);
 | 
			
		||||
    instance = nullptr;
 | 
			
		||||
 | 
			
		||||
    // doh! If you don't delete all your windows before exiting, you're going to
 | 
			
		||||
    // be leaking memory!
 | 
			
		||||
    jassert (desktopComponents.size() == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Desktop& JUCE_CALLTYPE Desktop::getInstance()
 | 
			
		||||
{
 | 
			
		||||
    if (instance == nullptr)
 | 
			
		||||
        instance = new Desktop();
 | 
			
		||||
 | 
			
		||||
    return *instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Desktop* Desktop::instance = nullptr;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
int Desktop::getNumComponents() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return desktopComponents.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* Desktop::getComponent (int index) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return desktopComponents [index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* Desktop::findComponentAt (Point<int> screenPosition) const
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    for (int i = desktopComponents.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        auto* c = desktopComponents.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (c->isVisible())
 | 
			
		||||
        {
 | 
			
		||||
            auto relative = c->getLocalPoint (nullptr, screenPosition);
 | 
			
		||||
 | 
			
		||||
            if (c->contains (relative))
 | 
			
		||||
                return c->getComponentAt (relative);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto lf = currentLookAndFeel.get())
 | 
			
		||||
        return *lf;
 | 
			
		||||
 | 
			
		||||
    if (defaultLookAndFeel == nullptr)
 | 
			
		||||
        defaultLookAndFeel.reset (new LookAndFeel_V4());
 | 
			
		||||
 | 
			
		||||
    auto lf = defaultLookAndFeel.get();
 | 
			
		||||
    jassert (lf != nullptr);
 | 
			
		||||
    currentLookAndFeel = lf;
 | 
			
		||||
    return *lf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel)
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    currentLookAndFeel = newDefaultLookAndFeel;
 | 
			
		||||
 | 
			
		||||
    for (int i = getNumComponents(); --i >= 0;)
 | 
			
		||||
        if (auto* c = getComponent (i))
 | 
			
		||||
            c->sendLookAndFeelChange();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::addDesktopComponent (Component* c)
 | 
			
		||||
{
 | 
			
		||||
    jassert (c != nullptr);
 | 
			
		||||
    jassert (! desktopComponents.contains (c));
 | 
			
		||||
    desktopComponents.addIfNotAlreadyThere (c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::removeDesktopComponent (Component* c)
 | 
			
		||||
{
 | 
			
		||||
    desktopComponents.removeFirstMatchingValue (c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::componentBroughtToFront (Component* c)
 | 
			
		||||
{
 | 
			
		||||
    auto index = desktopComponents.indexOf (c);
 | 
			
		||||
    jassert (index >= 0);
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        int newIndex = -1;
 | 
			
		||||
 | 
			
		||||
        if (! c->isAlwaysOnTop())
 | 
			
		||||
        {
 | 
			
		||||
            newIndex = desktopComponents.size();
 | 
			
		||||
 | 
			
		||||
            while (newIndex > 0 && desktopComponents.getUnchecked (newIndex - 1)->isAlwaysOnTop())
 | 
			
		||||
                --newIndex;
 | 
			
		||||
 | 
			
		||||
            --newIndex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        desktopComponents.move (index, newIndex);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
Point<int> Desktop::getMousePosition()
 | 
			
		||||
{
 | 
			
		||||
    return getMousePositionFloat().roundToInt();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point<float> Desktop::getMousePositionFloat()
 | 
			
		||||
{
 | 
			
		||||
    return getInstance().getMainMouseSource().getScreenPosition();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::setMousePosition (Point<int> newPosition)
 | 
			
		||||
{
 | 
			
		||||
    getInstance().getMainMouseSource().setScreenPosition (newPosition.toFloat());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point<int> Desktop::getLastMouseDownPosition()
 | 
			
		||||
{
 | 
			
		||||
    return getInstance().getMainMouseSource().getLastMouseDownPosition().roundToInt();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Desktop::getMouseButtonClickCounter() const noexcept    { return mouseClickCounter; }
 | 
			
		||||
int Desktop::getMouseWheelMoveCounter() const noexcept      { return mouseWheelCounter; }
 | 
			
		||||
 | 
			
		||||
void Desktop::incrementMouseClickCounter() noexcept         { ++mouseClickCounter; }
 | 
			
		||||
void Desktop::incrementMouseWheelCounter() noexcept         { ++mouseWheelCounter; }
 | 
			
		||||
 | 
			
		||||
const Array<MouseInputSource>& Desktop::getMouseSources() const noexcept        { return mouseSources->sourceArray; }
 | 
			
		||||
int Desktop::getNumMouseSources() const noexcept                                { return mouseSources->sources.size(); }
 | 
			
		||||
int Desktop::getNumDraggingMouseSources() const noexcept                        { return mouseSources->getNumDraggingMouseSources(); }
 | 
			
		||||
MouseInputSource* Desktop::getMouseSource (int index) const noexcept            { return mouseSources->getMouseSource (index); }
 | 
			
		||||
MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept    { return mouseSources->getDraggingMouseSource (index); }
 | 
			
		||||
MouseInputSource Desktop::getMainMouseSource() const noexcept                   { return MouseInputSource (mouseSources->sources.getUnchecked(0)); }
 | 
			
		||||
void Desktop::beginDragAutoRepeat (int interval)                                { mouseSources->beginDragAutoRepeat (interval); }
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::addFocusChangeListener    (FocusChangeListener* l)   { focusListeners.add (l); }
 | 
			
		||||
void Desktop::removeFocusChangeListener (FocusChangeListener* l)   { focusListeners.remove (l); }
 | 
			
		||||
void Desktop::triggerFocusCallback()                               { triggerAsyncUpdate(); }
 | 
			
		||||
 | 
			
		||||
void Desktop::updateFocusOutline()
 | 
			
		||||
{
 | 
			
		||||
    if (auto* currentFocus = Component::getCurrentlyFocusedComponent())
 | 
			
		||||
    {
 | 
			
		||||
        if (currentFocus->hasFocusOutline())
 | 
			
		||||
        {
 | 
			
		||||
            focusOutline = currentFocus->getLookAndFeel().createFocusOutlineForComponent (*currentFocus);
 | 
			
		||||
 | 
			
		||||
            if (focusOutline != nullptr)
 | 
			
		||||
                focusOutline->setOwner (currentFocus);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    focusOutline = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::handleAsyncUpdate()
 | 
			
		||||
{
 | 
			
		||||
    // The component may be deleted during this operation, but we'll use a SafePointer rather than a
 | 
			
		||||
    // BailOutChecker so that any remaining listeners will still get a callback (with a null pointer).
 | 
			
		||||
    focusListeners.call ([currentFocus = WeakReference<Component> { Component::getCurrentlyFocusedComponent() }] (FocusChangeListener& l)
 | 
			
		||||
    {
 | 
			
		||||
        l.globalFocusChanged (currentFocus.get());
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    updateFocusOutline();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::addDarkModeSettingListener    (DarkModeSettingListener* l)  { darkModeSettingListeners.add (l); }
 | 
			
		||||
void Desktop::removeDarkModeSettingListener (DarkModeSettingListener* l)  { darkModeSettingListeners.remove (l); }
 | 
			
		||||
 | 
			
		||||
void Desktop::darkModeChanged()  { darkModeSettingListeners.call ([] (auto& l) { l.darkModeSettingChanged(); }); }
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::resetTimer()
 | 
			
		||||
{
 | 
			
		||||
    if (mouseListeners.size() == 0)
 | 
			
		||||
        stopTimer();
 | 
			
		||||
    else
 | 
			
		||||
        startTimer (100);
 | 
			
		||||
 | 
			
		||||
    lastFakeMouseMove = getMousePositionFloat();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ListenerList<MouseListener>& Desktop::getMouseListeners()
 | 
			
		||||
{
 | 
			
		||||
    resetTimer();
 | 
			
		||||
    return mouseListeners;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::addGlobalMouseListener (MouseListener* listener)
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    mouseListeners.add (listener);
 | 
			
		||||
    resetTimer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::removeGlobalMouseListener (MouseListener* listener)
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    mouseListeners.remove (listener);
 | 
			
		||||
    resetTimer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::timerCallback()
 | 
			
		||||
{
 | 
			
		||||
    if (lastFakeMouseMove != getMousePositionFloat())
 | 
			
		||||
        sendMouseMove();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::sendMouseMove()
 | 
			
		||||
{
 | 
			
		||||
    if (! mouseListeners.isEmpty())
 | 
			
		||||
    {
 | 
			
		||||
        startTimer (20);
 | 
			
		||||
 | 
			
		||||
        lastFakeMouseMove = getMousePositionFloat();
 | 
			
		||||
 | 
			
		||||
        if (auto* target = findComponentAt (lastFakeMouseMove.roundToInt()))
 | 
			
		||||
        {
 | 
			
		||||
            Component::BailOutChecker checker (target);
 | 
			
		||||
            auto pos = target->getLocalPoint (nullptr, lastFakeMouseMove);
 | 
			
		||||
            auto now = Time::getCurrentTime();
 | 
			
		||||
 | 
			
		||||
            const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::currentModifiers, MouseInputSource::defaultPressure,
 | 
			
		||||
                                 MouseInputSource::defaultOrientation, MouseInputSource::defaultRotation,
 | 
			
		||||
                                 MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
 | 
			
		||||
                                 target, target, now, pos, now, 0, false);
 | 
			
		||||
 | 
			
		||||
            if (me.mods.isAnyMouseButtonDown())
 | 
			
		||||
                mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseDrag (me); });
 | 
			
		||||
            else
 | 
			
		||||
                mouseListeners.callChecked (checker, [&] (MouseListener& l) { l.mouseMove (me); });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::setKioskModeComponent (Component* componentToUse, bool allowMenusAndBars)
 | 
			
		||||
{
 | 
			
		||||
    if (kioskModeReentrant)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    const ScopedValueSetter<bool> setter (kioskModeReentrant, true, false);
 | 
			
		||||
 | 
			
		||||
    if (kioskModeComponent != componentToUse)
 | 
			
		||||
    {
 | 
			
		||||
        // agh! Don't delete or remove a component from the desktop while it's still the kiosk component!
 | 
			
		||||
        jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
 | 
			
		||||
 | 
			
		||||
        if (auto* oldKioskComp = kioskModeComponent)
 | 
			
		||||
        {
 | 
			
		||||
            kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one)
 | 
			
		||||
            setKioskComponent (oldKioskComp, false, allowMenusAndBars);
 | 
			
		||||
            oldKioskComp->setBounds (kioskComponentOriginalBounds);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        kioskModeComponent = componentToUse;
 | 
			
		||||
 | 
			
		||||
        if (kioskModeComponent != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            // Only components that are already on the desktop can be put into kiosk mode!
 | 
			
		||||
            jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr);
 | 
			
		||||
 | 
			
		||||
            kioskComponentOriginalBounds = kioskModeComponent->getBounds();
 | 
			
		||||
            setKioskComponent (kioskModeComponent, true, allowMenusAndBars);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void Desktop::setOrientationsEnabled (int newOrientations)
 | 
			
		||||
{
 | 
			
		||||
    if (allowedOrientations != newOrientations)
 | 
			
		||||
    {
 | 
			
		||||
        // Dodgy set of flags being passed here! Make sure you specify at least one permitted orientation.
 | 
			
		||||
        jassert (newOrientations != 0 && (newOrientations & ~allOrientations) == 0);
 | 
			
		||||
 | 
			
		||||
        allowedOrientations = newOrientations;
 | 
			
		||||
        allowedOrientationsChanged();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Desktop::getOrientationsEnabled() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return allowedOrientations;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Desktop::isOrientationEnabled (DisplayOrientation orientation) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    // Make sure you only pass one valid flag in here...
 | 
			
		||||
    jassert (orientation == upright || orientation == upsideDown
 | 
			
		||||
              || orientation == rotatedClockwise || orientation == rotatedAntiClockwise);
 | 
			
		||||
 | 
			
		||||
    return (allowedOrientations & orientation) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    if (masterScaleFactor != newScaleFactor)
 | 
			
		||||
    {
 | 
			
		||||
        masterScaleFactor = newScaleFactor;
 | 
			
		||||
        displays->refresh();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Desktop::isHeadless() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return displays->displays.isEmpty();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,481 +1,484 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Classes can implement this interface and register themselves with the Desktop class
 | 
			
		||||
    to receive callbacks when the currently focused component changes.
 | 
			
		||||
 | 
			
		||||
    @see Desktop::addFocusChangeListener, Desktop::removeFocusChangeListener
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  FocusChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~FocusChangeListener() = default;
 | 
			
		||||
 | 
			
		||||
    /** Callback to indicate that the currently focused component has changed. */
 | 
			
		||||
    virtual void globalFocusChanged (Component* focusedComponent) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Classes can implement this interface and register themselves with the Desktop class
 | 
			
		||||
    to receive callbacks when the operating system dark mode setting changes. The
 | 
			
		||||
    Desktop::isDarkModeActive() method can then be used to query the current setting.
 | 
			
		||||
 | 
			
		||||
    @see Desktop::addDarkModeSettingListener, Desktop::removeDarkModeSettingListener,
 | 
			
		||||
         Desktop::isDarkModeActive
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  DarkModeSettingListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~DarkModeSettingListener() = default;
 | 
			
		||||
 | 
			
		||||
    /** Callback to indicate that the dark mode setting has changed. */
 | 
			
		||||
    virtual void darkModeSettingChanged() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Describes and controls aspects of the computer's desktop.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  Desktop  : private DeletedAtShutdown,
 | 
			
		||||
                           private Timer,
 | 
			
		||||
                           private AsyncUpdater
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** There's only one desktop object, and this method will return it. */
 | 
			
		||||
    static Desktop& JUCE_CALLTYPE getInstance();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the mouse position.
 | 
			
		||||
 | 
			
		||||
        The coordinates are relative to the top-left of the main monitor.
 | 
			
		||||
 | 
			
		||||
        Note that this is just a shortcut for calling getMainMouseSource().getScreenPosition(), and
 | 
			
		||||
        you should only resort to grabbing the global mouse position if there's really no
 | 
			
		||||
        way to get the coordinates via a mouse event callback instead.
 | 
			
		||||
    */
 | 
			
		||||
    static Point<int> getMousePosition();
 | 
			
		||||
 | 
			
		||||
    /** Makes the mouse pointer jump to a given location.
 | 
			
		||||
        The coordinates are relative to the top-left of the main monitor.
 | 
			
		||||
        Note that this is a pretty old method, kept around mainly for backwards-compatibility,
 | 
			
		||||
        and you should use the MouseInputSource class directly in new code.
 | 
			
		||||
    */
 | 
			
		||||
    static void setMousePosition (Point<int> newPosition);
 | 
			
		||||
 | 
			
		||||
    /** Returns the last position at which a mouse button was pressed.
 | 
			
		||||
 | 
			
		||||
        Note that this is just a shortcut for calling getMainMouseSource().getLastMouseDownPosition(),
 | 
			
		||||
        and in a multi-touch environment, it doesn't make much sense. ALWAYS prefer to
 | 
			
		||||
        get this information via other means, such as MouseEvent::getMouseDownScreenPosition()
 | 
			
		||||
        if possible, and only ever call this as a last resort.
 | 
			
		||||
    */
 | 
			
		||||
    static Point<int> getLastMouseDownPosition();
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of times the mouse button has been clicked since the app started.
 | 
			
		||||
        Each mouse-down event increments this number by 1.
 | 
			
		||||
        @see getMouseWheelMoveCounter
 | 
			
		||||
    */
 | 
			
		||||
    int getMouseButtonClickCounter() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of times the mouse wheel has been moved since the app started.
 | 
			
		||||
        Each mouse-wheel event increments this number by 1.
 | 
			
		||||
        @see getMouseButtonClickCounter
 | 
			
		||||
    */
 | 
			
		||||
    int getMouseWheelMoveCounter() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This lets you prevent the screensaver from becoming active.
 | 
			
		||||
 | 
			
		||||
        Handy if you're running some sort of presentation app where having a screensaver
 | 
			
		||||
        appear would be annoying.
 | 
			
		||||
 | 
			
		||||
        Pass false to disable the screensaver, and true to re-enable it. (Note that this
 | 
			
		||||
        won't enable a screensaver unless the user has actually set one up).
 | 
			
		||||
 | 
			
		||||
        The disablement will only happen while the JUCE application is the foreground
 | 
			
		||||
        process - if another task is running in front of it, then the screensaver will
 | 
			
		||||
        be unaffected.
 | 
			
		||||
 | 
			
		||||
        @see isScreenSaverEnabled
 | 
			
		||||
    */
 | 
			
		||||
    static void setScreenSaverEnabled (bool isEnabled);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the screensaver has not been turned off.
 | 
			
		||||
 | 
			
		||||
        This will return the last value passed into setScreenSaverEnabled(). Note that
 | 
			
		||||
        it won't tell you whether the user is actually using a screen saver, just
 | 
			
		||||
        whether this app is deliberately preventing one from running.
 | 
			
		||||
 | 
			
		||||
        @see setScreenSaverEnabled
 | 
			
		||||
    */
 | 
			
		||||
    static bool isScreenSaverEnabled();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a MouseListener that will receive all mouse events that occur on
 | 
			
		||||
        any component.
 | 
			
		||||
 | 
			
		||||
        @see removeGlobalMouseListener
 | 
			
		||||
    */
 | 
			
		||||
    void addGlobalMouseListener (MouseListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Unregisters a MouseListener that was added with addGlobalMouseListener().
 | 
			
		||||
 | 
			
		||||
        @see addGlobalMouseListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeGlobalMouseListener (MouseListener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a FocusChangeListener that will receive a callback whenever the focused
 | 
			
		||||
        component changes.
 | 
			
		||||
 | 
			
		||||
        @see removeFocusChangeListener
 | 
			
		||||
    */
 | 
			
		||||
    void addFocusChangeListener (FocusChangeListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Unregisters a FocusChangeListener that was added with addFocusChangeListener().
 | 
			
		||||
 | 
			
		||||
        @see addFocusChangeListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeFocusChangeListener (FocusChangeListener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a DarkModeSettingListener that will receive a callback when the
 | 
			
		||||
        operating system dark mode setting changes. To query whether dark mode is on
 | 
			
		||||
        use the isDarkModeActive() method.
 | 
			
		||||
 | 
			
		||||
        @see isDarkModeActive, removeDarkModeSettingListener
 | 
			
		||||
    */
 | 
			
		||||
    void addDarkModeSettingListener (DarkModeSettingListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Unregisters a DarkModeSettingListener that was added with addDarkModeSettingListener().
 | 
			
		||||
 | 
			
		||||
        @see addDarkModeSettingListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeDarkModeSettingListener (DarkModeSettingListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** True if the operating system "dark mode" is active.
 | 
			
		||||
 | 
			
		||||
        To receive a callback when this setting changes implement the DarkModeSettingListener
 | 
			
		||||
        interface and use the addDarkModeSettingListener() to register a listener.
 | 
			
		||||
 | 
			
		||||
        @see addDarkModeSettingListener, removeDarkModeSettingListener
 | 
			
		||||
    */
 | 
			
		||||
    bool isDarkModeActive() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Takes a component and makes it full-screen, removing the taskbar, dock, etc.
 | 
			
		||||
 | 
			
		||||
        The component must already be on the desktop for this method to work. It will
 | 
			
		||||
        be resized to completely fill the screen and any extraneous taskbars, menu bars,
 | 
			
		||||
        etc will be hidden.
 | 
			
		||||
 | 
			
		||||
        To exit kiosk mode, just call setKioskModeComponent (nullptr). When this is called,
 | 
			
		||||
        the component that's currently being used will be resized back to the size
 | 
			
		||||
        and position it was in before being put into this mode.
 | 
			
		||||
 | 
			
		||||
        If allowMenusAndBars is true, things like the menu and dock (on mac) are still
 | 
			
		||||
        allowed to pop up when the mouse moves onto them. If this is false, it'll try
 | 
			
		||||
        to hide as much on-screen paraphernalia as possible.
 | 
			
		||||
    */
 | 
			
		||||
    void setKioskModeComponent (Component* componentToUse,
 | 
			
		||||
                                bool allowMenusAndBars = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that is currently being used in kiosk-mode.
 | 
			
		||||
 | 
			
		||||
        This is the component that was last set by setKioskModeComponent(). If none
 | 
			
		||||
        has been set, this returns nullptr.
 | 
			
		||||
    */
 | 
			
		||||
    Component* getKioskModeComponent() const noexcept               { return kioskModeComponent; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the number of components that are currently active as top-level
 | 
			
		||||
        desktop windows.
 | 
			
		||||
 | 
			
		||||
        @see getComponent, Component::addToDesktop
 | 
			
		||||
    */
 | 
			
		||||
    int getNumComponents() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the top-level desktop window components.
 | 
			
		||||
 | 
			
		||||
        The index is from 0 to getNumComponents() - 1. This could return 0 if the
 | 
			
		||||
        index is out-of-range.
 | 
			
		||||
 | 
			
		||||
        @see getNumComponents, Component::addToDesktop
 | 
			
		||||
    */
 | 
			
		||||
    Component* getComponent (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Finds the component at a given screen location.
 | 
			
		||||
 | 
			
		||||
        This will drill down into top-level windows to find the child component at
 | 
			
		||||
        the given position.
 | 
			
		||||
 | 
			
		||||
        Returns nullptr if the coordinates are inside a non-JUCE window.
 | 
			
		||||
    */
 | 
			
		||||
    Component* findComponentAt (Point<int> screenPosition) const;
 | 
			
		||||
 | 
			
		||||
    /** The Desktop object has a ComponentAnimator instance which can be used for performing
 | 
			
		||||
        your animations.
 | 
			
		||||
 | 
			
		||||
        Having a single shared ComponentAnimator object makes it more efficient when multiple
 | 
			
		||||
        components are being moved around simultaneously. It's also more convenient than having
 | 
			
		||||
        to manage your own instance of one.
 | 
			
		||||
 | 
			
		||||
        @see ComponentAnimator
 | 
			
		||||
    */
 | 
			
		||||
    ComponentAnimator& getAnimator() noexcept                       { return animator; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the current default look-and-feel for components which don't have one
 | 
			
		||||
        explicitly set.
 | 
			
		||||
        @see setDefaultLookAndFeel
 | 
			
		||||
    */
 | 
			
		||||
    LookAndFeel& getDefaultLookAndFeel() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Changes the default look-and-feel.
 | 
			
		||||
        @param newDefaultLookAndFeel    the new look-and-feel object to use - if this is
 | 
			
		||||
                                        set to nullptr, it will revert to using the system's
 | 
			
		||||
                                        default one. The object passed-in must be deleted by the
 | 
			
		||||
                                        caller when it's no longer needed.
 | 
			
		||||
        @see getDefaultLookAndFeel
 | 
			
		||||
    */
 | 
			
		||||
    void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Provides access to the array of mouse sources, for iteration.
 | 
			
		||||
        In a traditional single-mouse system, there might be only one MouseInputSource. On a
 | 
			
		||||
        multi-touch system, there could be one input source per potential finger. The number
 | 
			
		||||
        of mouse sources returned here may increase dynamically as the program runs.
 | 
			
		||||
        To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
 | 
			
		||||
    */
 | 
			
		||||
    const Array<MouseInputSource>& getMouseSources() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of MouseInputSource objects the system has at its disposal.
 | 
			
		||||
        In a traditional single-mouse system, there might be only one MouseInputSource. On a
 | 
			
		||||
        multi-touch system, there could be one input source per potential finger. The number
 | 
			
		||||
        of mouse sources returned here may increase dynamically as the program runs.
 | 
			
		||||
        To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
 | 
			
		||||
        @see getMouseSource
 | 
			
		||||
    */
 | 
			
		||||
    int getNumMouseSources() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the system's MouseInputSource objects.
 | 
			
		||||
        The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return
 | 
			
		||||
        a null pointer.
 | 
			
		||||
        In a traditional single-mouse system, there might be only one object. On a multi-touch
 | 
			
		||||
        system, there could be one input source per potential finger.
 | 
			
		||||
    */
 | 
			
		||||
    MouseInputSource* getMouseSource (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the main mouse input device that the system is using.
 | 
			
		||||
        @see getNumMouseSources()
 | 
			
		||||
    */
 | 
			
		||||
    MouseInputSource getMainMouseSource() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of mouse-sources that are currently being dragged.
 | 
			
		||||
        In a traditional single-mouse system, this will be 0 or 1, depending on whether a
 | 
			
		||||
        JUCE component has the button down on it. In a multi-touch system, this could
 | 
			
		||||
        be any number from 0 to the number of simultaneous touches that can be detected.
 | 
			
		||||
    */
 | 
			
		||||
    int getNumDraggingMouseSources() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the mouse sources that's currently being dragged.
 | 
			
		||||
        The index should be between 0 and getNumDraggingMouseSources() - 1. If the index is
 | 
			
		||||
        out of range, or if no mice or fingers are down, this will return a null pointer.
 | 
			
		||||
    */
 | 
			
		||||
    MouseInputSource* getDraggingMouseSource (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Ensures that a non-stop stream of mouse-drag events will be sent during the
 | 
			
		||||
        current mouse-drag operation.
 | 
			
		||||
 | 
			
		||||
        This allows you to make sure that mouseDrag() events are sent continuously, even
 | 
			
		||||
        when the mouse isn't moving. This can be useful for things like auto-scrolling
 | 
			
		||||
        components when the mouse is near an edge.
 | 
			
		||||
 | 
			
		||||
        Call this method during a mouseDown() or mouseDrag() callback, specifying the
 | 
			
		||||
        minimum interval between consecutive mouse drag callbacks. The callbacks
 | 
			
		||||
        will continue until the mouse is released, and then the interval will be reset,
 | 
			
		||||
        so you need to make sure it's called every time you begin a drag event.
 | 
			
		||||
        Passing an interval of 0 or less will cancel the auto-repeat.
 | 
			
		||||
 | 
			
		||||
        @see mouseDrag
 | 
			
		||||
    */
 | 
			
		||||
    void beginDragAutoRepeat (int millisecondsBetweenCallbacks);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** In a tablet/mobile device which can be turned around, this is used to indicate the orientation. */
 | 
			
		||||
    enum DisplayOrientation
 | 
			
		||||
    {
 | 
			
		||||
        upright                 = 1,  /**< Indicates that the device is the normal way up. */
 | 
			
		||||
        upsideDown              = 2,  /**< Indicates that the device is upside-down. */
 | 
			
		||||
        rotatedClockwise        = 4,  /**< Indicates that the device is turned 90 degrees clockwise from its upright position. */
 | 
			
		||||
        rotatedAntiClockwise    = 8,  /**< Indicates that the device is turned 90 degrees anti-clockwise from its upright position. */
 | 
			
		||||
 | 
			
		||||
        allOrientations         = 1 + 2 + 4 + 8   /**< A combination of all the orientation values */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** In a tablet device which can be turned around, this returns the current orientation. */
 | 
			
		||||
    DisplayOrientation getCurrentOrientation() const;
 | 
			
		||||
 | 
			
		||||
    /** Sets which orientations the display is allowed to auto-rotate to.
 | 
			
		||||
 | 
			
		||||
        For devices that support rotating desktops, this lets you specify which of the orientations your app can use.
 | 
			
		||||
 | 
			
		||||
        The parameter is a bitwise or-ed combination of the values in DisplayOrientation, and must contain at least one
 | 
			
		||||
        set bit.
 | 
			
		||||
    */
 | 
			
		||||
    void setOrientationsEnabled (int allowedOrientations);
 | 
			
		||||
 | 
			
		||||
    /** Returns the set of orientations the display is allowed to rotate to.
 | 
			
		||||
        @see setOrientationsEnabled
 | 
			
		||||
    */
 | 
			
		||||
    int getOrientationsEnabled() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns whether the display is allowed to auto-rotate to the given orientation.
 | 
			
		||||
        Each orientation can be enabled using setOrientationEnabled(). By default, all orientations are allowed.
 | 
			
		||||
    */
 | 
			
		||||
    bool isOrientationEnabled (DisplayOrientation orientation) const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the Displays object representing the connected displays.
 | 
			
		||||
 | 
			
		||||
        @see Displays
 | 
			
		||||
    */
 | 
			
		||||
    const Displays& getDisplays() const noexcept        { return *displays; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets a global scale factor to be used for all desktop windows.
 | 
			
		||||
        Setting this will also scale the monitor sizes that are returned by getDisplays().
 | 
			
		||||
    */
 | 
			
		||||
    void setGlobalScaleFactor (float newScaleFactor) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current global scale factor, as set by setGlobalScaleFactor().
 | 
			
		||||
        @see setGlobalScaleFactor
 | 
			
		||||
    */
 | 
			
		||||
    float getGlobalScaleFactor() const noexcept         { return masterScaleFactor; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** True if the OS supports semitransparent windows */
 | 
			
		||||
    static bool canUseSemiTransparentWindows() noexcept;
 | 
			
		||||
 | 
			
		||||
   #if JUCE_MAC && ! defined (DOXYGEN)
 | 
			
		||||
    [[deprecated ("This macOS-specific method has been deprecated in favour of the cross-platform "
 | 
			
		||||
                  " isDarkModeActive() method.")]]
 | 
			
		||||
    static bool isOSXDarkModeActive()  { return Desktop::getInstance().isDarkModeActive(); }
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns true on a headless system where there are no connected displays. */
 | 
			
		||||
    bool isHeadless() const noexcept;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static Desktop* instance;
 | 
			
		||||
 | 
			
		||||
    friend class Component;
 | 
			
		||||
    friend class ComponentPeer;
 | 
			
		||||
    friend class MouseInputSourceInternal;
 | 
			
		||||
    friend class DeletedAtShutdown;
 | 
			
		||||
    friend class TopLevelWindowManager;
 | 
			
		||||
    friend class Displays;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<MouseInputSource::SourceList> mouseSources;
 | 
			
		||||
 | 
			
		||||
    ListenerList<MouseListener> mouseListeners;
 | 
			
		||||
    ListenerList<FocusChangeListener> focusListeners;
 | 
			
		||||
    ListenerList<DarkModeSettingListener> darkModeSettingListeners;
 | 
			
		||||
 | 
			
		||||
    Array<Component*> desktopComponents;
 | 
			
		||||
    Array<ComponentPeer*> peers;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Displays> displays;
 | 
			
		||||
 | 
			
		||||
    Point<float> lastFakeMouseMove;
 | 
			
		||||
    void sendMouseMove();
 | 
			
		||||
 | 
			
		||||
    int mouseClickCounter = 0, mouseWheelCounter = 0;
 | 
			
		||||
    void incrementMouseClickCounter() noexcept;
 | 
			
		||||
    void incrementMouseWheelCounter() noexcept;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<LookAndFeel> defaultLookAndFeel;
 | 
			
		||||
    WeakReference<LookAndFeel> currentLookAndFeel;
 | 
			
		||||
 | 
			
		||||
    Component* kioskModeComponent = nullptr;
 | 
			
		||||
    Rectangle<int> kioskComponentOriginalBounds;
 | 
			
		||||
    bool kioskModeReentrant = false;
 | 
			
		||||
 | 
			
		||||
    int allowedOrientations = allOrientations;
 | 
			
		||||
    void allowedOrientationsChanged();
 | 
			
		||||
 | 
			
		||||
    float masterScaleFactor;
 | 
			
		||||
 | 
			
		||||
    ComponentAnimator animator;
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override;
 | 
			
		||||
    void resetTimer();
 | 
			
		||||
    ListenerList<MouseListener>& getMouseListeners();
 | 
			
		||||
 | 
			
		||||
    void addDesktopComponent (Component*);
 | 
			
		||||
    void removeDesktopComponent (Component*);
 | 
			
		||||
    void componentBroughtToFront (Component*);
 | 
			
		||||
 | 
			
		||||
    void setKioskComponent (Component*, bool shouldBeEnabled, bool allowMenusAndBars);
 | 
			
		||||
 | 
			
		||||
    void triggerFocusCallback();
 | 
			
		||||
    void handleAsyncUpdate() override;
 | 
			
		||||
 | 
			
		||||
    static Point<float> getMousePositionFloat();
 | 
			
		||||
 | 
			
		||||
    static double getDefaultMasterScale();
 | 
			
		||||
 | 
			
		||||
    Desktop();
 | 
			
		||||
    ~Desktop() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class NativeDarkModeChangeDetectorImpl;
 | 
			
		||||
    std::unique_ptr<NativeDarkModeChangeDetectorImpl> nativeDarkModeChangeDetectorImpl;
 | 
			
		||||
 | 
			
		||||
    static std::unique_ptr<NativeDarkModeChangeDetectorImpl> createNativeDarkModeChangeDetectorImpl();
 | 
			
		||||
    void darkModeChanged();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Classes can implement this interface and register themselves with the Desktop class
 | 
			
		||||
    to receive callbacks when the currently focused component changes.
 | 
			
		||||
 | 
			
		||||
    @see Desktop::addFocusChangeListener, Desktop::removeFocusChangeListener
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  FocusChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~FocusChangeListener() = default;
 | 
			
		||||
 | 
			
		||||
    /** Callback to indicate that the currently focused component has changed. */
 | 
			
		||||
    virtual void globalFocusChanged (Component* focusedComponent) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Classes can implement this interface and register themselves with the Desktop class
 | 
			
		||||
    to receive callbacks when the operating system dark mode setting changes. The
 | 
			
		||||
    Desktop::isDarkModeActive() method can then be used to query the current setting.
 | 
			
		||||
 | 
			
		||||
    @see Desktop::addDarkModeSettingListener, Desktop::removeDarkModeSettingListener,
 | 
			
		||||
         Desktop::isDarkModeActive
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  DarkModeSettingListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~DarkModeSettingListener() = default;
 | 
			
		||||
 | 
			
		||||
    /** Callback to indicate that the dark mode setting has changed. */
 | 
			
		||||
    virtual void darkModeSettingChanged() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Describes and controls aspects of the computer's desktop.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  Desktop  : private DeletedAtShutdown,
 | 
			
		||||
                           private Timer,
 | 
			
		||||
                           private AsyncUpdater
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** There's only one desktop object, and this method will return it. */
 | 
			
		||||
    static Desktop& JUCE_CALLTYPE getInstance();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the mouse position.
 | 
			
		||||
 | 
			
		||||
        The coordinates are relative to the top-left of the main monitor.
 | 
			
		||||
 | 
			
		||||
        Note that this is just a shortcut for calling getMainMouseSource().getScreenPosition(), and
 | 
			
		||||
        you should only resort to grabbing the global mouse position if there's really no
 | 
			
		||||
        way to get the coordinates via a mouse event callback instead.
 | 
			
		||||
    */
 | 
			
		||||
    static Point<int> getMousePosition();
 | 
			
		||||
 | 
			
		||||
    /** Makes the mouse pointer jump to a given location.
 | 
			
		||||
        The coordinates are relative to the top-left of the main monitor.
 | 
			
		||||
        Note that this is a pretty old method, kept around mainly for backwards-compatibility,
 | 
			
		||||
        and you should use the MouseInputSource class directly in new code.
 | 
			
		||||
    */
 | 
			
		||||
    static void setMousePosition (Point<int> newPosition);
 | 
			
		||||
 | 
			
		||||
    /** Returns the last position at which a mouse button was pressed.
 | 
			
		||||
 | 
			
		||||
        Note that this is just a shortcut for calling getMainMouseSource().getLastMouseDownPosition(),
 | 
			
		||||
        and in a multi-touch environment, it doesn't make much sense. ALWAYS prefer to
 | 
			
		||||
        get this information via other means, such as MouseEvent::getMouseDownScreenPosition()
 | 
			
		||||
        if possible, and only ever call this as a last resort.
 | 
			
		||||
    */
 | 
			
		||||
    static Point<int> getLastMouseDownPosition();
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of times the mouse button has been clicked since the app started.
 | 
			
		||||
        Each mouse-down event increments this number by 1.
 | 
			
		||||
        @see getMouseWheelMoveCounter
 | 
			
		||||
    */
 | 
			
		||||
    int getMouseButtonClickCounter() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of times the mouse wheel has been moved since the app started.
 | 
			
		||||
        Each mouse-wheel event increments this number by 1.
 | 
			
		||||
        @see getMouseButtonClickCounter
 | 
			
		||||
    */
 | 
			
		||||
    int getMouseWheelMoveCounter() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This lets you prevent the screensaver from becoming active.
 | 
			
		||||
 | 
			
		||||
        Handy if you're running some sort of presentation app where having a screensaver
 | 
			
		||||
        appear would be annoying.
 | 
			
		||||
 | 
			
		||||
        Pass false to disable the screensaver, and true to re-enable it. (Note that this
 | 
			
		||||
        won't enable a screensaver unless the user has actually set one up).
 | 
			
		||||
 | 
			
		||||
        The disablement will only happen while the JUCE application is the foreground
 | 
			
		||||
        process - if another task is running in front of it, then the screensaver will
 | 
			
		||||
        be unaffected.
 | 
			
		||||
 | 
			
		||||
        @see isScreenSaverEnabled
 | 
			
		||||
    */
 | 
			
		||||
    static void setScreenSaverEnabled (bool isEnabled);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the screensaver has not been turned off.
 | 
			
		||||
 | 
			
		||||
        This will return the last value passed into setScreenSaverEnabled(). Note that
 | 
			
		||||
        it won't tell you whether the user is actually using a screen saver, just
 | 
			
		||||
        whether this app is deliberately preventing one from running.
 | 
			
		||||
 | 
			
		||||
        @see setScreenSaverEnabled
 | 
			
		||||
    */
 | 
			
		||||
    static bool isScreenSaverEnabled();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a MouseListener that will receive all mouse events that occur on
 | 
			
		||||
        any component.
 | 
			
		||||
 | 
			
		||||
        @see removeGlobalMouseListener
 | 
			
		||||
    */
 | 
			
		||||
    void addGlobalMouseListener (MouseListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Unregisters a MouseListener that was added with addGlobalMouseListener().
 | 
			
		||||
 | 
			
		||||
        @see addGlobalMouseListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeGlobalMouseListener (MouseListener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a FocusChangeListener that will receive a callback whenever the focused
 | 
			
		||||
        component changes.
 | 
			
		||||
 | 
			
		||||
        @see removeFocusChangeListener
 | 
			
		||||
    */
 | 
			
		||||
    void addFocusChangeListener (FocusChangeListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Unregisters a FocusChangeListener that was added with addFocusChangeListener().
 | 
			
		||||
 | 
			
		||||
        @see addFocusChangeListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeFocusChangeListener (FocusChangeListener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a DarkModeSettingListener that will receive a callback when the
 | 
			
		||||
        operating system dark mode setting changes. To query whether dark mode is on
 | 
			
		||||
        use the isDarkModeActive() method.
 | 
			
		||||
 | 
			
		||||
        @see isDarkModeActive, removeDarkModeSettingListener
 | 
			
		||||
    */
 | 
			
		||||
    void addDarkModeSettingListener (DarkModeSettingListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Unregisters a DarkModeSettingListener that was added with addDarkModeSettingListener().
 | 
			
		||||
 | 
			
		||||
        @see addDarkModeSettingListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeDarkModeSettingListener (DarkModeSettingListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** True if the operating system "dark mode" is active.
 | 
			
		||||
 | 
			
		||||
        To receive a callback when this setting changes implement the DarkModeSettingListener
 | 
			
		||||
        interface and use the addDarkModeSettingListener() to register a listener.
 | 
			
		||||
 | 
			
		||||
        @see addDarkModeSettingListener, removeDarkModeSettingListener
 | 
			
		||||
    */
 | 
			
		||||
    bool isDarkModeActive() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Takes a component and makes it full-screen, removing the taskbar, dock, etc.
 | 
			
		||||
 | 
			
		||||
        The component must already be on the desktop for this method to work. It will
 | 
			
		||||
        be resized to completely fill the screen and any extraneous taskbars, menu bars,
 | 
			
		||||
        etc will be hidden.
 | 
			
		||||
 | 
			
		||||
        To exit kiosk mode, just call setKioskModeComponent (nullptr). When this is called,
 | 
			
		||||
        the component that's currently being used will be resized back to the size
 | 
			
		||||
        and position it was in before being put into this mode.
 | 
			
		||||
 | 
			
		||||
        If allowMenusAndBars is true, things like the menu and dock (on mac) are still
 | 
			
		||||
        allowed to pop up when the mouse moves onto them. If this is false, it'll try
 | 
			
		||||
        to hide as much on-screen paraphernalia as possible.
 | 
			
		||||
    */
 | 
			
		||||
    void setKioskModeComponent (Component* componentToUse,
 | 
			
		||||
                                bool allowMenusAndBars = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that is currently being used in kiosk-mode.
 | 
			
		||||
 | 
			
		||||
        This is the component that was last set by setKioskModeComponent(). If none
 | 
			
		||||
        has been set, this returns nullptr.
 | 
			
		||||
    */
 | 
			
		||||
    Component* getKioskModeComponent() const noexcept               { return kioskModeComponent; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the number of components that are currently active as top-level
 | 
			
		||||
        desktop windows.
 | 
			
		||||
 | 
			
		||||
        @see getComponent, Component::addToDesktop
 | 
			
		||||
    */
 | 
			
		||||
    int getNumComponents() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the top-level desktop window components.
 | 
			
		||||
 | 
			
		||||
        The index is from 0 to getNumComponents() - 1. This could return 0 if the
 | 
			
		||||
        index is out-of-range.
 | 
			
		||||
 | 
			
		||||
        @see getNumComponents, Component::addToDesktop
 | 
			
		||||
    */
 | 
			
		||||
    Component* getComponent (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Finds the component at a given screen location.
 | 
			
		||||
 | 
			
		||||
        This will drill down into top-level windows to find the child component at
 | 
			
		||||
        the given position.
 | 
			
		||||
 | 
			
		||||
        Returns nullptr if the coordinates are inside a non-JUCE window.
 | 
			
		||||
    */
 | 
			
		||||
    Component* findComponentAt (Point<int> screenPosition) const;
 | 
			
		||||
 | 
			
		||||
    /** The Desktop object has a ComponentAnimator instance which can be used for performing
 | 
			
		||||
        your animations.
 | 
			
		||||
 | 
			
		||||
        Having a single shared ComponentAnimator object makes it more efficient when multiple
 | 
			
		||||
        components are being moved around simultaneously. It's also more convenient than having
 | 
			
		||||
        to manage your own instance of one.
 | 
			
		||||
 | 
			
		||||
        @see ComponentAnimator
 | 
			
		||||
    */
 | 
			
		||||
    ComponentAnimator& getAnimator() noexcept                       { return animator; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the current default look-and-feel for components which don't have one
 | 
			
		||||
        explicitly set.
 | 
			
		||||
        @see setDefaultLookAndFeel
 | 
			
		||||
    */
 | 
			
		||||
    LookAndFeel& getDefaultLookAndFeel() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Changes the default look-and-feel.
 | 
			
		||||
        @param newDefaultLookAndFeel    the new look-and-feel object to use - if this is
 | 
			
		||||
                                        set to nullptr, it will revert to using the system's
 | 
			
		||||
                                        default one. The object passed-in must be deleted by the
 | 
			
		||||
                                        caller when it's no longer needed.
 | 
			
		||||
        @see getDefaultLookAndFeel
 | 
			
		||||
    */
 | 
			
		||||
    void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Provides access to the array of mouse sources, for iteration.
 | 
			
		||||
        In a traditional single-mouse system, there might be only one MouseInputSource. On a
 | 
			
		||||
        multi-touch system, there could be one input source per potential finger. The number
 | 
			
		||||
        of mouse sources returned here may increase dynamically as the program runs.
 | 
			
		||||
        To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
 | 
			
		||||
    */
 | 
			
		||||
    const Array<MouseInputSource>& getMouseSources() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of MouseInputSource objects the system has at its disposal.
 | 
			
		||||
        In a traditional single-mouse system, there might be only one MouseInputSource. On a
 | 
			
		||||
        multi-touch system, there could be one input source per potential finger. The number
 | 
			
		||||
        of mouse sources returned here may increase dynamically as the program runs.
 | 
			
		||||
        To find out how many mouse events are currently happening, use getNumDraggingMouseSources().
 | 
			
		||||
        @see getMouseSource
 | 
			
		||||
    */
 | 
			
		||||
    int getNumMouseSources() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the system's MouseInputSource objects.
 | 
			
		||||
        The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return
 | 
			
		||||
        a null pointer.
 | 
			
		||||
        In a traditional single-mouse system, there might be only one object. On a multi-touch
 | 
			
		||||
        system, there could be one input source per potential finger.
 | 
			
		||||
    */
 | 
			
		||||
    MouseInputSource* getMouseSource (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the main mouse input device that the system is using.
 | 
			
		||||
        @see getNumMouseSources()
 | 
			
		||||
    */
 | 
			
		||||
    MouseInputSource getMainMouseSource() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of mouse-sources that are currently being dragged.
 | 
			
		||||
        In a traditional single-mouse system, this will be 0 or 1, depending on whether a
 | 
			
		||||
        JUCE component has the button down on it. In a multi-touch system, this could
 | 
			
		||||
        be any number from 0 to the number of simultaneous touches that can be detected.
 | 
			
		||||
    */
 | 
			
		||||
    int getNumDraggingMouseSources() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the mouse sources that's currently being dragged.
 | 
			
		||||
        The index should be between 0 and getNumDraggingMouseSources() - 1. If the index is
 | 
			
		||||
        out of range, or if no mice or fingers are down, this will return a null pointer.
 | 
			
		||||
    */
 | 
			
		||||
    MouseInputSource* getDraggingMouseSource (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Ensures that a non-stop stream of mouse-drag events will be sent during the
 | 
			
		||||
        current mouse-drag operation.
 | 
			
		||||
 | 
			
		||||
        This allows you to make sure that mouseDrag() events are sent continuously, even
 | 
			
		||||
        when the mouse isn't moving. This can be useful for things like auto-scrolling
 | 
			
		||||
        components when the mouse is near an edge.
 | 
			
		||||
 | 
			
		||||
        Call this method during a mouseDown() or mouseDrag() callback, specifying the
 | 
			
		||||
        minimum interval between consecutive mouse drag callbacks. The callbacks
 | 
			
		||||
        will continue until the mouse is released, and then the interval will be reset,
 | 
			
		||||
        so you need to make sure it's called every time you begin a drag event.
 | 
			
		||||
        Passing an interval of 0 or less will cancel the auto-repeat.
 | 
			
		||||
 | 
			
		||||
        @see mouseDrag
 | 
			
		||||
    */
 | 
			
		||||
    void beginDragAutoRepeat (int millisecondsBetweenCallbacks);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** In a tablet/mobile device which can be turned around, this is used to indicate the orientation. */
 | 
			
		||||
    enum DisplayOrientation
 | 
			
		||||
    {
 | 
			
		||||
        upright                 = 1,  /**< Indicates that the device is the normal way up. */
 | 
			
		||||
        upsideDown              = 2,  /**< Indicates that the device is upside-down. */
 | 
			
		||||
        rotatedClockwise        = 4,  /**< Indicates that the device is turned 90 degrees clockwise from its upright position. */
 | 
			
		||||
        rotatedAntiClockwise    = 8,  /**< Indicates that the device is turned 90 degrees anti-clockwise from its upright position. */
 | 
			
		||||
 | 
			
		||||
        allOrientations         = 1 + 2 + 4 + 8   /**< A combination of all the orientation values */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** In a tablet device which can be turned around, this returns the current orientation. */
 | 
			
		||||
    DisplayOrientation getCurrentOrientation() const;
 | 
			
		||||
 | 
			
		||||
    /** Sets which orientations the display is allowed to auto-rotate to.
 | 
			
		||||
 | 
			
		||||
        For devices that support rotating desktops, this lets you specify which of the orientations your app can use.
 | 
			
		||||
 | 
			
		||||
        The parameter is a bitwise or-ed combination of the values in DisplayOrientation, and must contain at least one
 | 
			
		||||
        set bit.
 | 
			
		||||
    */
 | 
			
		||||
    void setOrientationsEnabled (int allowedOrientations);
 | 
			
		||||
 | 
			
		||||
    /** Returns the set of orientations the display is allowed to rotate to.
 | 
			
		||||
        @see setOrientationsEnabled
 | 
			
		||||
    */
 | 
			
		||||
    int getOrientationsEnabled() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns whether the display is allowed to auto-rotate to the given orientation.
 | 
			
		||||
        Each orientation can be enabled using setOrientationEnabled(). By default, all orientations are allowed.
 | 
			
		||||
    */
 | 
			
		||||
    bool isOrientationEnabled (DisplayOrientation orientation) const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the Displays object representing the connected displays.
 | 
			
		||||
 | 
			
		||||
        @see Displays
 | 
			
		||||
    */
 | 
			
		||||
    const Displays& getDisplays() const noexcept        { return *displays; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets a global scale factor to be used for all desktop windows.
 | 
			
		||||
        Setting this will also scale the monitor sizes that are returned by getDisplays().
 | 
			
		||||
    */
 | 
			
		||||
    void setGlobalScaleFactor (float newScaleFactor) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current global scale factor, as set by setGlobalScaleFactor().
 | 
			
		||||
        @see setGlobalScaleFactor
 | 
			
		||||
    */
 | 
			
		||||
    float getGlobalScaleFactor() const noexcept         { return masterScaleFactor; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** True if the OS supports semitransparent windows */
 | 
			
		||||
    static bool canUseSemiTransparentWindows() noexcept;
 | 
			
		||||
 | 
			
		||||
   #if JUCE_MAC && ! defined (DOXYGEN)
 | 
			
		||||
    [[deprecated ("This macOS-specific method has been deprecated in favour of the cross-platform "
 | 
			
		||||
                  " isDarkModeActive() method.")]]
 | 
			
		||||
    static bool isOSXDarkModeActive()  { return Desktop::getInstance().isDarkModeActive(); }
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns true on a headless system where there are no connected displays. */
 | 
			
		||||
    bool isHeadless() const noexcept;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static Desktop* instance;
 | 
			
		||||
 | 
			
		||||
    friend class Component;
 | 
			
		||||
    friend class ComponentPeer;
 | 
			
		||||
    friend class MouseInputSourceInternal;
 | 
			
		||||
    friend class DeletedAtShutdown;
 | 
			
		||||
    friend class TopLevelWindowManager;
 | 
			
		||||
    friend class Displays;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<MouseInputSource::SourceList> mouseSources;
 | 
			
		||||
 | 
			
		||||
    ListenerList<MouseListener> mouseListeners;
 | 
			
		||||
    ListenerList<FocusChangeListener> focusListeners;
 | 
			
		||||
    ListenerList<DarkModeSettingListener> darkModeSettingListeners;
 | 
			
		||||
 | 
			
		||||
    Array<Component*> desktopComponents;
 | 
			
		||||
    Array<ComponentPeer*> peers;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Displays> displays;
 | 
			
		||||
 | 
			
		||||
    Point<float> lastFakeMouseMove;
 | 
			
		||||
    void sendMouseMove();
 | 
			
		||||
 | 
			
		||||
    int mouseClickCounter = 0, mouseWheelCounter = 0;
 | 
			
		||||
    void incrementMouseClickCounter() noexcept;
 | 
			
		||||
    void incrementMouseWheelCounter() noexcept;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<LookAndFeel> defaultLookAndFeel;
 | 
			
		||||
    WeakReference<LookAndFeel> currentLookAndFeel;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<FocusOutline> focusOutline;
 | 
			
		||||
 | 
			
		||||
    Component* kioskModeComponent = nullptr;
 | 
			
		||||
    Rectangle<int> kioskComponentOriginalBounds;
 | 
			
		||||
    bool kioskModeReentrant = false;
 | 
			
		||||
 | 
			
		||||
    int allowedOrientations = allOrientations;
 | 
			
		||||
    void allowedOrientationsChanged();
 | 
			
		||||
 | 
			
		||||
    float masterScaleFactor;
 | 
			
		||||
 | 
			
		||||
    ComponentAnimator animator;
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override;
 | 
			
		||||
    void resetTimer();
 | 
			
		||||
    ListenerList<MouseListener>& getMouseListeners();
 | 
			
		||||
 | 
			
		||||
    void addDesktopComponent (Component*);
 | 
			
		||||
    void removeDesktopComponent (Component*);
 | 
			
		||||
    void componentBroughtToFront (Component*);
 | 
			
		||||
 | 
			
		||||
    void setKioskComponent (Component*, bool shouldBeEnabled, bool allowMenusAndBars);
 | 
			
		||||
 | 
			
		||||
    void triggerFocusCallback();
 | 
			
		||||
    void updateFocusOutline();
 | 
			
		||||
    void handleAsyncUpdate() override;
 | 
			
		||||
 | 
			
		||||
    static Point<float> getMousePositionFloat();
 | 
			
		||||
 | 
			
		||||
    static double getDefaultMasterScale();
 | 
			
		||||
 | 
			
		||||
    Desktop();
 | 
			
		||||
    ~Desktop() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class NativeDarkModeChangeDetectorImpl;
 | 
			
		||||
    std::unique_ptr<NativeDarkModeChangeDetectorImpl> nativeDarkModeChangeDetectorImpl;
 | 
			
		||||
 | 
			
		||||
    static std::unique_ptr<NativeDarkModeChangeDetectorImpl> createNativeDarkModeChangeDetectorImpl();
 | 
			
		||||
    void darkModeChanged();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,438 +1,444 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
Displays::Displays (Desktop& desktop)
 | 
			
		||||
{
 | 
			
		||||
    init (desktop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Displays::init (Desktop& desktop)
 | 
			
		||||
{
 | 
			
		||||
    findDisplays (desktop.getGlobalScaleFactor());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display* Displays::getDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    int maxArea = -1;
 | 
			
		||||
    const Display* foundDisplay = nullptr;
 | 
			
		||||
 | 
			
		||||
    for (auto& display : displays)
 | 
			
		||||
    {
 | 
			
		||||
        auto displayArea = display.totalArea;
 | 
			
		||||
 | 
			
		||||
        if (isPhysical)
 | 
			
		||||
            displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
 | 
			
		||||
 | 
			
		||||
        displayArea = displayArea.getIntersection (rect);
 | 
			
		||||
        auto area = displayArea.getWidth() * displayArea.getHeight();
 | 
			
		||||
 | 
			
		||||
        if (area >= maxArea)
 | 
			
		||||
        {
 | 
			
		||||
            maxArea = area;
 | 
			
		||||
            foundDisplay = &display;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return foundDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display* Displays::getDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto minDistance = std::numeric_limits<int>::max();
 | 
			
		||||
    const Display* foundDisplay = nullptr;
 | 
			
		||||
 | 
			
		||||
    for (auto& display : displays)
 | 
			
		||||
    {
 | 
			
		||||
        auto displayArea = display.totalArea;
 | 
			
		||||
 | 
			
		||||
        if (isPhysical)
 | 
			
		||||
            displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
 | 
			
		||||
 | 
			
		||||
        if (displayArea.contains (point))
 | 
			
		||||
            return &display;
 | 
			
		||||
 | 
			
		||||
        auto distance = displayArea.getCentre().getDistanceFrom (point);
 | 
			
		||||
 | 
			
		||||
        if (distance <= minDistance)
 | 
			
		||||
        {
 | 
			
		||||
            minDistance = distance;
 | 
			
		||||
            foundDisplay = &display;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return foundDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> Displays::physicalToLogical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return physicalToLogical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<float> Displays::physicalToLogical (Rectangle<float> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
 | 
			
		||||
                                                             : getDisplayForRect (rect.toNearestInt(), true);
 | 
			
		||||
 | 
			
		||||
    if (display == nullptr)
 | 
			
		||||
        return rect;
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    return ((rect - display->topLeftPhysical.toFloat()) / (display->scale / globalScale))
 | 
			
		||||
            + (display->totalArea.getTopLeft().toFloat() * globalScale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> Displays::logicalToPhysical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return logicalToPhysical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<float> Displays::logicalToPhysical (Rectangle<float> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
 | 
			
		||||
                                                             : getDisplayForRect (rect.toNearestInt(), false);
 | 
			
		||||
 | 
			
		||||
    if (display == nullptr)
 | 
			
		||||
        return rect;
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    return ((rect.toFloat() - (display->totalArea.getTopLeft().toFloat() * globalScale)) * (display->scale / globalScale))
 | 
			
		||||
             + display->topLeftPhysical.toFloat();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ValueType>
 | 
			
		||||
Point<ValueType> Displays::physicalToLogical (Point<ValueType> point, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
 | 
			
		||||
                                                             : getDisplayForPoint (point.roundToInt(), true);
 | 
			
		||||
 | 
			
		||||
    if (display == nullptr)
 | 
			
		||||
        return point;
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    Point<ValueType> logicalTopLeft  (static_cast<ValueType> (display->totalArea.getX()),       static_cast<ValueType> (display->totalArea.getY()));
 | 
			
		||||
    Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
 | 
			
		||||
 | 
			
		||||
    return ((point - physicalTopLeft) / (display->scale / globalScale)) + (logicalTopLeft * globalScale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ValueType>
 | 
			
		||||
Point<ValueType> Displays::logicalToPhysical (Point<ValueType> point, const Display* useScaleFactorOfDisplay)  const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
 | 
			
		||||
                                                             : getDisplayForPoint (point.roundToInt(), false);
 | 
			
		||||
 | 
			
		||||
    if (display == nullptr)
 | 
			
		||||
        return point;
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    Point<ValueType> logicalTopLeft  (static_cast<ValueType> (display->totalArea.getX()),       static_cast<ValueType> (display->totalArea.getY()));
 | 
			
		||||
    Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
 | 
			
		||||
 | 
			
		||||
    return ((point - (logicalTopLeft * globalScale)) * (display->scale / globalScale)) + physicalTopLeft;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display* Displays::getPrimaryDisplay() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    for (auto& d : displays)
 | 
			
		||||
        if (d.isMain)
 | 
			
		||||
            return &d;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RectangleList<int> Displays::getRectangleList (bool userAreasOnly) const
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    RectangleList<int> rl;
 | 
			
		||||
 | 
			
		||||
    for (auto& d : displays)
 | 
			
		||||
        rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
 | 
			
		||||
 | 
			
		||||
    return rl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> Displays::getTotalBounds (bool userAreasOnly) const
 | 
			
		||||
{
 | 
			
		||||
    return getRectangleList (userAreasOnly).getBounds();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Displays::refresh()
 | 
			
		||||
{
 | 
			
		||||
    Array<Display> oldDisplays;
 | 
			
		||||
    oldDisplays.swapWith (displays);
 | 
			
		||||
 | 
			
		||||
    init (Desktop::getInstance());
 | 
			
		||||
 | 
			
		||||
    if (oldDisplays != displays)
 | 
			
		||||
    {
 | 
			
		||||
        for (auto i = ComponentPeer::getNumPeers(); --i >= 0;)
 | 
			
		||||
            if (auto* peer = ComponentPeer::getPeer (i))
 | 
			
		||||
                peer->handleScreenSizeChange();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool operator== (const Displays::Display& d1, const Displays::Display& d2) noexcept;
 | 
			
		||||
bool operator== (const Displays::Display& d1, const Displays::Display& d2) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return d1.isMain          == d2.isMain
 | 
			
		||||
        && d1.totalArea       == d2.totalArea
 | 
			
		||||
        && d1.userArea        == d2.userArea
 | 
			
		||||
        && d1.topLeftPhysical == d2.topLeftPhysical
 | 
			
		||||
        && d1.scale           == d2.scale
 | 
			
		||||
        && d1.dpi             == d2.dpi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool operator!= (const Displays::Display& d1, const Displays::Display& d2) noexcept;
 | 
			
		||||
bool operator!= (const Displays::Display& d1, const Displays::Display& d2) noexcept    { return ! (d1 == d2); }
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// These methods are used for converting the totalArea and userArea Rectangles in Display from physical to logical
 | 
			
		||||
// pixels. We do this by constructing a graph of connected displays where the root node has position (0, 0); this can be
 | 
			
		||||
// safely converted to logical pixels using its scale factor and we can then traverse the graph and work out the logical pixels
 | 
			
		||||
// for all the other connected displays. We need to do this as the logical bounds of a display depend not only on its scale
 | 
			
		||||
// factor but also the scale factor of the displays connected to it.
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Represents a node in our graph of displays.
 | 
			
		||||
*/
 | 
			
		||||
struct DisplayNode
 | 
			
		||||
{
 | 
			
		||||
    /** The Display object that this represents. */
 | 
			
		||||
    Displays::Display* display;
 | 
			
		||||
 | 
			
		||||
    /** True if this represents the 'root' display with position (0, 0). */
 | 
			
		||||
    bool isRoot = false;
 | 
			
		||||
 | 
			
		||||
    /** The parent node of this node in our display graph. This will have a correct logicalArea. */
 | 
			
		||||
    DisplayNode* parent = nullptr;
 | 
			
		||||
 | 
			
		||||
    /** The logical area to be calculated. This will be valid after processDisplay() has
 | 
			
		||||
        been called on this node.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<double> logicalArea;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** Recursive - will calculate and set the logicalArea member of current. */
 | 
			
		||||
static void processDisplay (DisplayNode* currentNode, Array<DisplayNode>& allNodes)
 | 
			
		||||
{
 | 
			
		||||
    const auto physicalArea = currentNode->display->totalArea.toDouble();
 | 
			
		||||
    const auto scale = currentNode->display->scale;
 | 
			
		||||
 | 
			
		||||
    if (! currentNode->isRoot)
 | 
			
		||||
    {
 | 
			
		||||
        const auto logicalWidth  = physicalArea.getWidth() / scale;
 | 
			
		||||
        const auto logicalHeight = physicalArea.getHeight() / scale;
 | 
			
		||||
 | 
			
		||||
        const auto physicalParentArea = currentNode->parent->display->totalArea.toDouble();
 | 
			
		||||
        const auto logicalParentArea  = currentNode->parent->logicalArea; // logical area of parent has already been calculated
 | 
			
		||||
        const auto parentScale        = currentNode->parent->display->scale;
 | 
			
		||||
 | 
			
		||||
        Rectangle<double> logicalArea (0.0, 0.0, logicalWidth, logicalHeight);
 | 
			
		||||
 | 
			
		||||
        if      (physicalArea.getRight() == physicalParentArea.getX())     logicalArea.setPosition ({ logicalParentArea.getX() - logicalWidth, physicalArea.getY() / parentScale });  // on left
 | 
			
		||||
        else if (physicalArea.getX() == physicalParentArea.getRight())     logicalArea.setPosition ({ logicalParentArea.getRight(),  physicalArea.getY() / parentScale });            // on right
 | 
			
		||||
        else if (physicalArea.getBottom() == physicalParentArea.getY())    logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getY() - logicalHeight }); // on top
 | 
			
		||||
        else if (physicalArea.getY() == physicalParentArea.getBottom())    logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getBottom() });            // on bottom
 | 
			
		||||
        else                                                               jassertfalse;
 | 
			
		||||
 | 
			
		||||
        currentNode->logicalArea = logicalArea;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        // If currentNode is the root (position (0, 0)) then we can just scale the physical area
 | 
			
		||||
        currentNode->logicalArea = physicalArea / scale;
 | 
			
		||||
        currentNode->parent = currentNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Find child nodes
 | 
			
		||||
    Array<DisplayNode*> children;
 | 
			
		||||
    for (auto& node : allNodes)
 | 
			
		||||
    {
 | 
			
		||||
        // Already calculated
 | 
			
		||||
        if (node.parent != nullptr)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        const auto otherPhysicalArea = node.display->totalArea.toDouble();
 | 
			
		||||
 | 
			
		||||
        // If the displays are touching on any side
 | 
			
		||||
        if (otherPhysicalArea.getX() == physicalArea.getRight()  || otherPhysicalArea.getRight() == physicalArea.getX()
 | 
			
		||||
            || otherPhysicalArea.getY() == physicalArea.getBottom() || otherPhysicalArea.getBottom() == physicalArea.getY())
 | 
			
		||||
        {
 | 
			
		||||
            node.parent = currentNode;
 | 
			
		||||
            children.add (&node);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Recursively process all child nodes
 | 
			
		||||
    for (auto child : children)
 | 
			
		||||
        processDisplay (child, allNodes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** This is called when the displays Array has been filled out with the info for all connected displays and the
 | 
			
		||||
    totalArea and userArea Rectangles need to be converted from physical to logical coordinates.
 | 
			
		||||
*/
 | 
			
		||||
void Displays::updateToLogical()
 | 
			
		||||
{
 | 
			
		||||
    if (displays.size() == 1)
 | 
			
		||||
    {
 | 
			
		||||
        auto& display = displays.getReference (0);
 | 
			
		||||
 | 
			
		||||
        display.totalArea = (display.totalArea.toDouble() / display.scale).toNearestInt();
 | 
			
		||||
        display.userArea  = (display.userArea.toDouble()  / display.scale).toNearestInt();
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Array<DisplayNode> displayNodes;
 | 
			
		||||
 | 
			
		||||
    for (auto& d : displays)
 | 
			
		||||
    {
 | 
			
		||||
        DisplayNode node;
 | 
			
		||||
 | 
			
		||||
        node.display = &d;
 | 
			
		||||
 | 
			
		||||
        if (d.totalArea.getTopLeft() == Point<int>())
 | 
			
		||||
            node.isRoot = true;
 | 
			
		||||
 | 
			
		||||
        displayNodes.add (node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto* root = [&displayNodes]() -> DisplayNode*
 | 
			
		||||
    {
 | 
			
		||||
        for (auto& node : displayNodes)
 | 
			
		||||
            if (node.isRoot)
 | 
			
		||||
                return &node;
 | 
			
		||||
 | 
			
		||||
        auto minDistance = std::numeric_limits<int>::max();
 | 
			
		||||
        DisplayNode* retVal = nullptr;
 | 
			
		||||
 | 
			
		||||
        for (auto& node : displayNodes)
 | 
			
		||||
        {
 | 
			
		||||
            auto distance = node.display->totalArea.getTopLeft().getDistanceFrom ({});
 | 
			
		||||
 | 
			
		||||
            if (distance < minDistance)
 | 
			
		||||
            {
 | 
			
		||||
                minDistance = distance;
 | 
			
		||||
                retVal = &node;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (retVal != nullptr)
 | 
			
		||||
            retVal->isRoot = true;
 | 
			
		||||
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    // Must have a root node!
 | 
			
		||||
    jassert (root != nullptr);
 | 
			
		||||
 | 
			
		||||
    // Recursively traverse the display graph from the root and work out logical bounds
 | 
			
		||||
    processDisplay (root, displayNodes);
 | 
			
		||||
 | 
			
		||||
    for (auto& node : displayNodes)
 | 
			
		||||
    {
 | 
			
		||||
        // All of the nodes should have a parent
 | 
			
		||||
        jassert (node.parent != nullptr);
 | 
			
		||||
 | 
			
		||||
        auto relativeUserArea = (node.display->userArea.toDouble() - node.display->totalArea.toDouble().getTopLeft()) / node.display->scale;
 | 
			
		||||
 | 
			
		||||
        // Now set Display::totalArea and ::userArea using the logical area that we have calculated
 | 
			
		||||
        node.display->topLeftPhysical = node.display->totalArea.getTopLeft();
 | 
			
		||||
        node.display->totalArea       = node.logicalArea.toNearestInt();
 | 
			
		||||
        node.display->userArea        = (relativeUserArea + node.logicalArea.getTopLeft()).toNearestInt();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef DOXYGEN
 | 
			
		||||
 // explicit template instantiations
 | 
			
		||||
 template Point<int>   Displays::physicalToLogical (Point<int>,   const Display*) const noexcept;
 | 
			
		||||
 template Point<float> Displays::physicalToLogical (Point<float>, const Display*) const noexcept;
 | 
			
		||||
 | 
			
		||||
 template Point<int>   Displays::logicalToPhysical (Point<int>,   const Display*) const noexcept;
 | 
			
		||||
 template Point<float> Displays::logicalToPhysical (Point<float>, const Display*) const noexcept;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// Deprecated methods
 | 
			
		||||
const Displays::Display& Displays::getDisplayContaining (Point<int> position) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    const auto* best = &displays.getReference (0);
 | 
			
		||||
    auto bestDistance = std::numeric_limits<int>::max();
 | 
			
		||||
 | 
			
		||||
    for (auto& d : displays)
 | 
			
		||||
    {
 | 
			
		||||
        if (d.totalArea.contains (position))
 | 
			
		||||
        {
 | 
			
		||||
            best = &d;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto distance = d.totalArea.getCentre().getDistanceFrom (position);
 | 
			
		||||
 | 
			
		||||
        if (distance < bestDistance)
 | 
			
		||||
        {
 | 
			
		||||
            bestDistance = distance;
 | 
			
		||||
            best = &d;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *best;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display& Displays::findDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* display = getDisplayForRect (rect, isPhysical))
 | 
			
		||||
        return *display;
 | 
			
		||||
 | 
			
		||||
    return emptyDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display& Displays::findDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* display = getDisplayForPoint (point, isPhysical))
 | 
			
		||||
        return *display;
 | 
			
		||||
 | 
			
		||||
    return emptyDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display& Displays::getMainDisplay() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* display = getPrimaryDisplay())
 | 
			
		||||
        return *display;
 | 
			
		||||
 | 
			
		||||
    return emptyDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
template <typename This>
 | 
			
		||||
auto* getPrimaryDisplayImpl (This& t)
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    const auto iter = std::find_if (t.displays.begin(), t.displays.end(), [] (auto& d) { return d.isMain; });
 | 
			
		||||
    return iter != t.displays.end() ? std::addressof (*iter) : nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Displays::Displays (Desktop& desktop)
 | 
			
		||||
{
 | 
			
		||||
    init (desktop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Displays::init (Desktop& desktop)
 | 
			
		||||
{
 | 
			
		||||
    findDisplays (desktop.getGlobalScaleFactor());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display* Displays::getDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    int maxArea = -1;
 | 
			
		||||
    const Display* foundDisplay = nullptr;
 | 
			
		||||
 | 
			
		||||
    for (auto& display : displays)
 | 
			
		||||
    {
 | 
			
		||||
        auto displayArea = display.totalArea;
 | 
			
		||||
 | 
			
		||||
        if (isPhysical)
 | 
			
		||||
            displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
 | 
			
		||||
 | 
			
		||||
        displayArea = displayArea.getIntersection (rect);
 | 
			
		||||
        auto area = displayArea.getWidth() * displayArea.getHeight();
 | 
			
		||||
 | 
			
		||||
        if (area >= maxArea)
 | 
			
		||||
        {
 | 
			
		||||
            maxArea = area;
 | 
			
		||||
            foundDisplay = &display;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return foundDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display* Displays::getDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto minDistance = std::numeric_limits<int>::max();
 | 
			
		||||
    const Display* foundDisplay = nullptr;
 | 
			
		||||
 | 
			
		||||
    for (auto& display : displays)
 | 
			
		||||
    {
 | 
			
		||||
        auto displayArea = display.totalArea;
 | 
			
		||||
 | 
			
		||||
        if (isPhysical)
 | 
			
		||||
            displayArea = (displayArea.withZeroOrigin() * display.scale) + display.topLeftPhysical;
 | 
			
		||||
 | 
			
		||||
        if (displayArea.contains (point))
 | 
			
		||||
            return &display;
 | 
			
		||||
 | 
			
		||||
        auto distance = displayArea.getCentre().getDistanceFrom (point);
 | 
			
		||||
 | 
			
		||||
        if (distance <= minDistance)
 | 
			
		||||
        {
 | 
			
		||||
            minDistance = distance;
 | 
			
		||||
            foundDisplay = &display;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return foundDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> Displays::physicalToLogical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return physicalToLogical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<float> Displays::physicalToLogical (Rectangle<float> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
 | 
			
		||||
                                                             : getDisplayForRect (rect.toNearestInt(), true);
 | 
			
		||||
 | 
			
		||||
    if (display == nullptr)
 | 
			
		||||
        return rect;
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    return ((rect - display->topLeftPhysical.toFloat()) / (display->scale / globalScale))
 | 
			
		||||
            + (display->totalArea.getTopLeft().toFloat() * globalScale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> Displays::logicalToPhysical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return logicalToPhysical (rect.toFloat(), useScaleFactorOfDisplay).toNearestInt();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<float> Displays::logicalToPhysical (Rectangle<float> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
 | 
			
		||||
                                                             : getDisplayForRect (rect.toNearestInt(), false);
 | 
			
		||||
 | 
			
		||||
    if (display == nullptr)
 | 
			
		||||
        return rect;
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    return ((rect.toFloat() - (display->totalArea.getTopLeft().toFloat() * globalScale)) * (display->scale / globalScale))
 | 
			
		||||
             + display->topLeftPhysical.toFloat();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ValueType>
 | 
			
		||||
Point<ValueType> Displays::physicalToLogical (Point<ValueType> point, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
 | 
			
		||||
                                                             : getDisplayForPoint (point.roundToInt(), true);
 | 
			
		||||
 | 
			
		||||
    if (display == nullptr)
 | 
			
		||||
        return point;
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    Point<ValueType> logicalTopLeft  (static_cast<ValueType> (display->totalArea.getX()),       static_cast<ValueType> (display->totalArea.getY()));
 | 
			
		||||
    Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
 | 
			
		||||
 | 
			
		||||
    return ((point - physicalTopLeft) / (display->scale / globalScale)) + (logicalTopLeft * globalScale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ValueType>
 | 
			
		||||
Point<ValueType> Displays::logicalToPhysical (Point<ValueType> point, const Display* useScaleFactorOfDisplay)  const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const auto* display = useScaleFactorOfDisplay != nullptr ? useScaleFactorOfDisplay
 | 
			
		||||
                                                             : getDisplayForPoint (point.roundToInt(), false);
 | 
			
		||||
 | 
			
		||||
    if (display == nullptr)
 | 
			
		||||
        return point;
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    Point<ValueType> logicalTopLeft  (static_cast<ValueType> (display->totalArea.getX()),       static_cast<ValueType> (display->totalArea.getY()));
 | 
			
		||||
    Point<ValueType> physicalTopLeft (static_cast<ValueType> (display->topLeftPhysical.getX()), static_cast<ValueType> (display->topLeftPhysical.getY()));
 | 
			
		||||
 | 
			
		||||
    return ((point - (logicalTopLeft * globalScale)) * (display->scale / globalScale)) + physicalTopLeft;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display* Displays::getPrimaryDisplay() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return getPrimaryDisplayImpl (*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RectangleList<int> Displays::getRectangleList (bool userAreasOnly) const
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    RectangleList<int> rl;
 | 
			
		||||
 | 
			
		||||
    for (auto& d : displays)
 | 
			
		||||
        rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea);
 | 
			
		||||
 | 
			
		||||
    return rl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> Displays::getTotalBounds (bool userAreasOnly) const
 | 
			
		||||
{
 | 
			
		||||
    return getRectangleList (userAreasOnly).getBounds();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Displays::refresh()
 | 
			
		||||
{
 | 
			
		||||
    Array<Display> oldDisplays;
 | 
			
		||||
    oldDisplays.swapWith (displays);
 | 
			
		||||
 | 
			
		||||
    init (Desktop::getInstance());
 | 
			
		||||
 | 
			
		||||
    if (oldDisplays != displays)
 | 
			
		||||
    {
 | 
			
		||||
        for (auto i = ComponentPeer::getNumPeers(); --i >= 0;)
 | 
			
		||||
            if (auto* peer = ComponentPeer::getPeer (i))
 | 
			
		||||
                peer->handleScreenSizeChange();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static auto tie (const Displays::Display& d)
 | 
			
		||||
{
 | 
			
		||||
    return std::tie (d.dpi,
 | 
			
		||||
                     d.isMain,
 | 
			
		||||
                     d.keyboardInsets,
 | 
			
		||||
                     d.safeAreaInsets,
 | 
			
		||||
                     d.scale,
 | 
			
		||||
                     d.topLeftPhysical,
 | 
			
		||||
                     d.totalArea,
 | 
			
		||||
                     d.userArea);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool operator== (const Displays::Display& d1, const Displays::Display& d2) noexcept
 | 
			
		||||
{
 | 
			
		||||
    return tie (d1) == tie (d2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// These methods are used for converting the totalArea and userArea Rectangles in Display from physical to logical
 | 
			
		||||
// pixels. We do this by constructing a graph of connected displays where the root node has position (0, 0); this can be
 | 
			
		||||
// safely converted to logical pixels using its scale factor and we can then traverse the graph and work out the logical pixels
 | 
			
		||||
// for all the other connected displays. We need to do this as the logical bounds of a display depend not only on its scale
 | 
			
		||||
// factor but also the scale factor of the displays connected to it.
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Represents a node in our graph of displays.
 | 
			
		||||
*/
 | 
			
		||||
struct DisplayNode
 | 
			
		||||
{
 | 
			
		||||
    /** The Display object that this represents. */
 | 
			
		||||
    Displays::Display* display;
 | 
			
		||||
 | 
			
		||||
    /** True if this represents the 'root' display with position (0, 0). */
 | 
			
		||||
    bool isRoot = false;
 | 
			
		||||
 | 
			
		||||
    /** The parent node of this node in our display graph. This will have a correct logicalArea. */
 | 
			
		||||
    DisplayNode* parent = nullptr;
 | 
			
		||||
 | 
			
		||||
    /** The logical area to be calculated. This will be valid after processDisplay() has
 | 
			
		||||
        been called on this node.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<double> logicalArea;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** Recursive - will calculate and set the logicalArea member of current. */
 | 
			
		||||
static void processDisplay (DisplayNode* currentNode, Array<DisplayNode>& allNodes)
 | 
			
		||||
{
 | 
			
		||||
    const auto physicalArea = currentNode->display->totalArea.toDouble();
 | 
			
		||||
    const auto scale = currentNode->display->scale;
 | 
			
		||||
 | 
			
		||||
    if (! currentNode->isRoot)
 | 
			
		||||
    {
 | 
			
		||||
        const auto logicalWidth  = physicalArea.getWidth() / scale;
 | 
			
		||||
        const auto logicalHeight = physicalArea.getHeight() / scale;
 | 
			
		||||
 | 
			
		||||
        const auto physicalParentArea = currentNode->parent->display->totalArea.toDouble();
 | 
			
		||||
        const auto logicalParentArea  = currentNode->parent->logicalArea; // logical area of parent has already been calculated
 | 
			
		||||
        const auto parentScale        = currentNode->parent->display->scale;
 | 
			
		||||
 | 
			
		||||
        Rectangle<double> logicalArea (0.0, 0.0, logicalWidth, logicalHeight);
 | 
			
		||||
 | 
			
		||||
        if      (physicalArea.getRight() == physicalParentArea.getX())     logicalArea.setPosition ({ logicalParentArea.getX() - logicalWidth, physicalArea.getY() / parentScale });  // on left
 | 
			
		||||
        else if (physicalArea.getX() == physicalParentArea.getRight())     logicalArea.setPosition ({ logicalParentArea.getRight(),  physicalArea.getY() / parentScale });            // on right
 | 
			
		||||
        else if (physicalArea.getBottom() == physicalParentArea.getY())    logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getY() - logicalHeight }); // on top
 | 
			
		||||
        else if (physicalArea.getY() == physicalParentArea.getBottom())    logicalArea.setPosition ({ physicalArea.getX() / parentScale, logicalParentArea.getBottom() });            // on bottom
 | 
			
		||||
        else                                                               jassertfalse;
 | 
			
		||||
 | 
			
		||||
        currentNode->logicalArea = logicalArea;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        // If currentNode is the root (position (0, 0)) then we can just scale the physical area
 | 
			
		||||
        currentNode->logicalArea = physicalArea / scale;
 | 
			
		||||
        currentNode->parent = currentNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Find child nodes
 | 
			
		||||
    Array<DisplayNode*> children;
 | 
			
		||||
    for (auto& node : allNodes)
 | 
			
		||||
    {
 | 
			
		||||
        // Already calculated
 | 
			
		||||
        if (node.parent != nullptr)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        const auto otherPhysicalArea = node.display->totalArea.toDouble();
 | 
			
		||||
 | 
			
		||||
        // If the displays are touching on any side
 | 
			
		||||
        if (otherPhysicalArea.getX() == physicalArea.getRight()  || otherPhysicalArea.getRight() == physicalArea.getX()
 | 
			
		||||
            || otherPhysicalArea.getY() == physicalArea.getBottom() || otherPhysicalArea.getBottom() == physicalArea.getY())
 | 
			
		||||
        {
 | 
			
		||||
            node.parent = currentNode;
 | 
			
		||||
            children.add (&node);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Recursively process all child nodes
 | 
			
		||||
    for (auto child : children)
 | 
			
		||||
        processDisplay (child, allNodes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** This is called when the displays Array has been filled out with the info for all connected displays and the
 | 
			
		||||
    totalArea and userArea Rectangles need to be converted from physical to logical coordinates.
 | 
			
		||||
*/
 | 
			
		||||
void Displays::updateToLogical()
 | 
			
		||||
{
 | 
			
		||||
    if (displays.size() == 1)
 | 
			
		||||
    {
 | 
			
		||||
        auto& display = displays.getReference (0);
 | 
			
		||||
 | 
			
		||||
        display.totalArea = (display.totalArea.toDouble() / display.scale).toNearestInt();
 | 
			
		||||
        display.userArea  = (display.userArea.toDouble()  / display.scale).toNearestInt();
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Array<DisplayNode> displayNodes;
 | 
			
		||||
 | 
			
		||||
    for (auto& d : displays)
 | 
			
		||||
    {
 | 
			
		||||
        DisplayNode node;
 | 
			
		||||
 | 
			
		||||
        node.display = &d;
 | 
			
		||||
 | 
			
		||||
        if (d.totalArea.getTopLeft() == Point<int>())
 | 
			
		||||
            node.isRoot = true;
 | 
			
		||||
 | 
			
		||||
        displayNodes.add (node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto* root = [&displayNodes]() -> DisplayNode*
 | 
			
		||||
    {
 | 
			
		||||
        for (auto& node : displayNodes)
 | 
			
		||||
            if (node.isRoot)
 | 
			
		||||
                return &node;
 | 
			
		||||
 | 
			
		||||
        auto minDistance = std::numeric_limits<int>::max();
 | 
			
		||||
        DisplayNode* retVal = nullptr;
 | 
			
		||||
 | 
			
		||||
        for (auto& node : displayNodes)
 | 
			
		||||
        {
 | 
			
		||||
            auto distance = node.display->totalArea.getTopLeft().getDistanceFrom ({});
 | 
			
		||||
 | 
			
		||||
            if (distance < minDistance)
 | 
			
		||||
            {
 | 
			
		||||
                minDistance = distance;
 | 
			
		||||
                retVal = &node;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (retVal != nullptr)
 | 
			
		||||
            retVal->isRoot = true;
 | 
			
		||||
 | 
			
		||||
        return retVal;
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    // Must have a root node!
 | 
			
		||||
    jassert (root != nullptr);
 | 
			
		||||
 | 
			
		||||
    // Recursively traverse the display graph from the root and work out logical bounds
 | 
			
		||||
    processDisplay (root, displayNodes);
 | 
			
		||||
 | 
			
		||||
    for (auto& node : displayNodes)
 | 
			
		||||
    {
 | 
			
		||||
        // All of the nodes should have a parent
 | 
			
		||||
        jassert (node.parent != nullptr);
 | 
			
		||||
 | 
			
		||||
        auto relativeUserArea = (node.display->userArea.toDouble() - node.display->totalArea.toDouble().getTopLeft()) / node.display->scale;
 | 
			
		||||
 | 
			
		||||
        // Now set Display::totalArea and ::userArea using the logical area that we have calculated
 | 
			
		||||
        node.display->topLeftPhysical = node.display->totalArea.getTopLeft();
 | 
			
		||||
        node.display->totalArea       = node.logicalArea.toNearestInt();
 | 
			
		||||
        node.display->userArea        = (relativeUserArea + node.logicalArea.getTopLeft()).toNearestInt();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef DOXYGEN
 | 
			
		||||
 // explicit template instantiations
 | 
			
		||||
 template Point<int>   Displays::physicalToLogical (Point<int>,   const Display*) const noexcept;
 | 
			
		||||
 template Point<float> Displays::physicalToLogical (Point<float>, const Display*) const noexcept;
 | 
			
		||||
 | 
			
		||||
 template Point<int>   Displays::logicalToPhysical (Point<int>,   const Display*) const noexcept;
 | 
			
		||||
 template Point<float> Displays::logicalToPhysical (Point<float>, const Display*) const noexcept;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// Deprecated methods
 | 
			
		||||
const Displays::Display& Displays::getDisplayContaining (Point<int> position) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    const auto* best = &displays.getReference (0);
 | 
			
		||||
    auto bestDistance = std::numeric_limits<int>::max();
 | 
			
		||||
 | 
			
		||||
    for (auto& d : displays)
 | 
			
		||||
    {
 | 
			
		||||
        if (d.totalArea.contains (position))
 | 
			
		||||
        {
 | 
			
		||||
            best = &d;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto distance = d.totalArea.getCentre().getDistanceFrom (position);
 | 
			
		||||
 | 
			
		||||
        if (distance < bestDistance)
 | 
			
		||||
        {
 | 
			
		||||
            bestDistance = distance;
 | 
			
		||||
            best = &d;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *best;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display& Displays::findDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* display = getDisplayForRect (rect, isPhysical))
 | 
			
		||||
        return *display;
 | 
			
		||||
 | 
			
		||||
    return emptyDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display& Displays::findDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* display = getDisplayForPoint (point, isPhysical))
 | 
			
		||||
        return *display;
 | 
			
		||||
 | 
			
		||||
    return emptyDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display& Displays::getMainDisplay() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* display = getPrimaryDisplay())
 | 
			
		||||
        return *display;
 | 
			
		||||
 | 
			
		||||
    return emptyDisplay;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,194 +1,203 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Manages details about connected display devices.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  Displays
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    Displays (Desktop&);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents a connected display device. */
 | 
			
		||||
    struct JUCE_API  Display
 | 
			
		||||
    {
 | 
			
		||||
        /** This will be true if this is the user's main display device. */
 | 
			
		||||
        bool isMain;
 | 
			
		||||
 | 
			
		||||
        /** The total area of this display in logical pixels including any OS-dependent objects
 | 
			
		||||
            like the taskbar, menu bar, etc.
 | 
			
		||||
        */
 | 
			
		||||
        Rectangle<int> totalArea;
 | 
			
		||||
 | 
			
		||||
        /** The total area of this display in logical pixels which isn't covered by OS-dependent
 | 
			
		||||
            objects like the taskbar, menu bar, etc.
 | 
			
		||||
        */
 | 
			
		||||
        Rectangle<int> userArea;
 | 
			
		||||
 | 
			
		||||
        /** Represents the area of this display in logical pixels that is not functional for
 | 
			
		||||
            displaying content.
 | 
			
		||||
 | 
			
		||||
            On mobile devices this may be the area covered by display cutouts and notches, where
 | 
			
		||||
            you still want to draw a background but should not position important content.
 | 
			
		||||
        */
 | 
			
		||||
        BorderSize<int> safeAreaInsets;
 | 
			
		||||
 | 
			
		||||
        /** The top-left of this display in physical coordinates. */
 | 
			
		||||
        Point<int> topLeftPhysical;
 | 
			
		||||
 | 
			
		||||
        /** The scale factor of this display.
 | 
			
		||||
 | 
			
		||||
            For higher-resolution displays, or displays with a user-defined scale factor set,
 | 
			
		||||
            this may be a value other than 1.0.
 | 
			
		||||
 | 
			
		||||
            This value is used to convert between physical and logical pixels. For example, a Component
 | 
			
		||||
            with size 10x10 will use 20x20 physical pixels on a display with a scale factor of 2.0.
 | 
			
		||||
        */
 | 
			
		||||
        double scale;
 | 
			
		||||
 | 
			
		||||
        /** The DPI of the display.
 | 
			
		||||
 | 
			
		||||
            This is the number of physical pixels per inch. To get the number of logical
 | 
			
		||||
            pixels per inch, divide this by the Display::scale value.
 | 
			
		||||
        */
 | 
			
		||||
        double dpi;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Converts an integer Rectangle from physical to logical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Rectangle to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<int> physicalToLogical (Rectangle<int> physicalRect,
 | 
			
		||||
                                      const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a floating-point Rectangle from physical to logical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Rectangle to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<float> physicalToLogical (Rectangle<float> physicalRect,
 | 
			
		||||
                                        const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts an integer Rectangle from logical to physical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Rectangle to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<int> logicalToPhysical (Rectangle<int> logicalRect,
 | 
			
		||||
                                      const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a floating-point Rectangle from logical to physical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Rectangle to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<float> logicalToPhysical (Rectangle<float> logicalRect,
 | 
			
		||||
                                        const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a Point from physical to logical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Point to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    template <typename ValueType>
 | 
			
		||||
    Point<ValueType> physicalToLogical (Point<ValueType> physicalPoint,
 | 
			
		||||
                                        const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a Point from logical to physical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Point to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    template <typename ValueType>
 | 
			
		||||
    Point<ValueType> logicalToPhysical (Point<ValueType> logicalPoint,
 | 
			
		||||
                                        const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display containing a given Rectangle (either
 | 
			
		||||
        in logical or physical pixels), or nullptr if there are no connected displays.
 | 
			
		||||
 | 
			
		||||
        If the Rectangle lies outside all the displays then the nearest one will be returned.
 | 
			
		||||
    */
 | 
			
		||||
    const Display* getDisplayForRect (Rectangle<int> rect, bool isPhysical = false) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display containing a given Point (either
 | 
			
		||||
        in logical or physical pixels), or nullptr if there are no connected displays.
 | 
			
		||||
 | 
			
		||||
        If the Point lies outside all the displays then the nearest one will be returned.
 | 
			
		||||
    */
 | 
			
		||||
    const Display* getDisplayForPoint (Point<int> point, bool isPhysical = false) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display acting as the user's main screen, or nullptr
 | 
			
		||||
        if there are no connected displays.
 | 
			
		||||
    */
 | 
			
		||||
    const Display* getPrimaryDisplay() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a RectangleList made up of all the displays in LOGICAL pixels. */
 | 
			
		||||
    RectangleList<int> getRectangleList (bool userAreasOnly) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the smallest bounding box which contains all the displays in LOGICAL pixels. */
 | 
			
		||||
    Rectangle<int> getTotalBounds (bool userAreasOnly) const;
 | 
			
		||||
 | 
			
		||||
    /** An Array containing the Display objects for all of the connected displays. */
 | 
			
		||||
    Array<Display> displays;
 | 
			
		||||
 | 
			
		||||
   #ifndef DOXYGEN
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void refresh();
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    ~Displays() = default;
 | 
			
		||||
 | 
			
		||||
    [[deprecated ("Use the getDisplayForPoint or getDisplayForRect methods instead "
 | 
			
		||||
                 "as they can deal with converting between logical and physical pixels.")]]
 | 
			
		||||
    const Display& getDisplayContaining (Point<int> position) const noexcept;
 | 
			
		||||
 | 
			
		||||
    // These methods have been deprecated - use the methods which return a Display* instead as they will return
 | 
			
		||||
    // nullptr on headless systems with no connected displays
 | 
			
		||||
    [[deprecated]] const Display& findDisplayForRect (Rectangle<int>, bool isPhysical = false) const noexcept;
 | 
			
		||||
    [[deprecated]] const Display& findDisplayForPoint (Point<int>, bool isPhysical = false) const noexcept;
 | 
			
		||||
    [[deprecated]] const Display& getMainDisplay() const noexcept;
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend class Desktop;
 | 
			
		||||
 | 
			
		||||
    void init (Desktop&);
 | 
			
		||||
    void findDisplays (float masterScale);
 | 
			
		||||
 | 
			
		||||
    void updateToLogical();
 | 
			
		||||
 | 
			
		||||
    Display emptyDisplay;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Manages details about connected display devices.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  Displays
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    Displays (Desktop&);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents a connected display device. */
 | 
			
		||||
    struct JUCE_API  Display
 | 
			
		||||
    {
 | 
			
		||||
        /** This will be true if this is the user's main display device. */
 | 
			
		||||
        bool isMain;
 | 
			
		||||
 | 
			
		||||
        /** The total area of this display in logical pixels including any OS-dependent objects
 | 
			
		||||
            like the taskbar, menu bar, etc.
 | 
			
		||||
        */
 | 
			
		||||
        Rectangle<int> totalArea;
 | 
			
		||||
 | 
			
		||||
        /** The total area of this display in logical pixels which isn't covered by OS-dependent
 | 
			
		||||
            objects like the taskbar, menu bar, etc.
 | 
			
		||||
        */
 | 
			
		||||
        Rectangle<int> userArea;
 | 
			
		||||
 | 
			
		||||
        /** Represents the area of this display in logical pixels that is not functional for
 | 
			
		||||
            displaying content.
 | 
			
		||||
 | 
			
		||||
            On mobile devices this may be the area covered by display cutouts and notches, where
 | 
			
		||||
            you still want to draw a background but should not position important content.
 | 
			
		||||
        */
 | 
			
		||||
        BorderSize<int> safeAreaInsets;
 | 
			
		||||
 | 
			
		||||
        /** Represents the area of this display in logical pixels that is obscured by an
 | 
			
		||||
            onscreen keyboard.
 | 
			
		||||
 | 
			
		||||
            This is currently only supported on iOS, and on Android 11+.
 | 
			
		||||
 | 
			
		||||
            This will only return the bounds of the keyboard when it is in 'docked' mode.
 | 
			
		||||
            If the keyboard is floating (e.g. on an iPad using the split keyboard mode),
 | 
			
		||||
            no insets will be reported.
 | 
			
		||||
        */
 | 
			
		||||
        BorderSize<int> keyboardInsets;
 | 
			
		||||
 | 
			
		||||
        /** The top-left of this display in physical coordinates. */
 | 
			
		||||
        Point<int> topLeftPhysical;
 | 
			
		||||
 | 
			
		||||
        /** The scale factor of this display.
 | 
			
		||||
 | 
			
		||||
            For higher-resolution displays, or displays with a user-defined scale factor set,
 | 
			
		||||
            this may be a value other than 1.0.
 | 
			
		||||
 | 
			
		||||
            This value is used to convert between physical and logical pixels. For example, a Component
 | 
			
		||||
            with size 10x10 will use 20x20 physical pixels on a display with a scale factor of 2.0.
 | 
			
		||||
        */
 | 
			
		||||
        double scale;
 | 
			
		||||
 | 
			
		||||
        /** The DPI of the display.
 | 
			
		||||
 | 
			
		||||
            This is the number of physical pixels per inch. To get the number of logical
 | 
			
		||||
            pixels per inch, divide this by the Display::scale value.
 | 
			
		||||
        */
 | 
			
		||||
        double dpi;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Converts an integer Rectangle from physical to logical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Rectangle to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<int> physicalToLogical (Rectangle<int> physicalRect,
 | 
			
		||||
                                      const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a floating-point Rectangle from physical to logical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Rectangle to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<float> physicalToLogical (Rectangle<float> physicalRect,
 | 
			
		||||
                                        const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts an integer Rectangle from logical to physical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Rectangle to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<int> logicalToPhysical (Rectangle<int> logicalRect,
 | 
			
		||||
                                      const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a floating-point Rectangle from logical to physical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Rectangle to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<float> logicalToPhysical (Rectangle<float> logicalRect,
 | 
			
		||||
                                        const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a Point from physical to logical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Point to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    template <typename ValueType>
 | 
			
		||||
    Point<ValueType> physicalToLogical (Point<ValueType> physicalPoint,
 | 
			
		||||
                                        const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a Point from logical to physical pixels.
 | 
			
		||||
 | 
			
		||||
        If useScaleFactorOfDisplay is not null then its scale factor will be used for the conversion
 | 
			
		||||
        regardless of the display that the Point to be converted is on.
 | 
			
		||||
    */
 | 
			
		||||
    template <typename ValueType>
 | 
			
		||||
    Point<ValueType> logicalToPhysical (Point<ValueType> logicalPoint,
 | 
			
		||||
                                        const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display containing a given Rectangle (either
 | 
			
		||||
        in logical or physical pixels), or nullptr if there are no connected displays.
 | 
			
		||||
 | 
			
		||||
        If the Rectangle lies outside all the displays then the nearest one will be returned.
 | 
			
		||||
    */
 | 
			
		||||
    const Display* getDisplayForRect (Rectangle<int> rect, bool isPhysical = false) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display containing a given Point (either
 | 
			
		||||
        in logical or physical pixels), or nullptr if there are no connected displays.
 | 
			
		||||
 | 
			
		||||
        If the Point lies outside all the displays then the nearest one will be returned.
 | 
			
		||||
    */
 | 
			
		||||
    const Display* getDisplayForPoint (Point<int> point, bool isPhysical = false) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display acting as the user's main screen, or nullptr
 | 
			
		||||
        if there are no connected displays.
 | 
			
		||||
    */
 | 
			
		||||
    const Display* getPrimaryDisplay() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a RectangleList made up of all the displays in LOGICAL pixels. */
 | 
			
		||||
    RectangleList<int> getRectangleList (bool userAreasOnly) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the smallest bounding box which contains all the displays in LOGICAL pixels. */
 | 
			
		||||
    Rectangle<int> getTotalBounds (bool userAreasOnly) const;
 | 
			
		||||
 | 
			
		||||
    /** An Array containing the Display objects for all of the connected displays. */
 | 
			
		||||
    Array<Display> displays;
 | 
			
		||||
 | 
			
		||||
   #ifndef DOXYGEN
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void refresh();
 | 
			
		||||
 | 
			
		||||
    [[deprecated ("Use the getDisplayForPoint or getDisplayForRect methods instead "
 | 
			
		||||
                 "as they can deal with converting between logical and physical pixels.")]]
 | 
			
		||||
    const Display& getDisplayContaining (Point<int> position) const noexcept;
 | 
			
		||||
 | 
			
		||||
    // These methods have been deprecated - use the methods which return a Display* instead as they will return
 | 
			
		||||
    // nullptr on headless systems with no connected displays
 | 
			
		||||
    [[deprecated]] const Display& findDisplayForRect (Rectangle<int>, bool isPhysical = false) const noexcept;
 | 
			
		||||
    [[deprecated]] const Display& findDisplayForPoint (Point<int>, bool isPhysical = false) const noexcept;
 | 
			
		||||
    [[deprecated]] const Display& getMainDisplay() const noexcept;
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend class Desktop;
 | 
			
		||||
 | 
			
		||||
    void init (Desktop&);
 | 
			
		||||
    void findDisplays (float masterScale);
 | 
			
		||||
 | 
			
		||||
    void updateToLogical();
 | 
			
		||||
 | 
			
		||||
    Display emptyDisplay;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user