git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
348
deps/juce/modules/juce_gui_basics/desktop/juce_Desktop.cpp
vendored
Normal file
348
deps/juce/modules/juce_gui_basics/desktop/juce_Desktop.cpp
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
481
deps/juce/modules/juce_gui_basics/desktop/juce_Desktop.h
vendored
Normal file
481
deps/juce/modules/juce_gui_basics/desktop/juce_Desktop.h
vendored
Normal file
@ -0,0 +1,481 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
438
deps/juce/modules/juce_gui_basics/desktop/juce_Displays.cpp
vendored
Normal file
438
deps/juce/modules/juce_gui_basics/desktop/juce_Displays.cpp
vendored
Normal file
@ -0,0 +1,438 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
194
deps/juce/modules/juce_gui_basics/desktop/juce_Displays.h
vendored
Normal file
194
deps/juce/modules/juce_gui_basics/desktop/juce_Displays.h
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
Reference in New Issue
Block a user