upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
		
							
								
								
									
										335
									
								
								modules/juce_gui_basics/desktop/juce_Desktop.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								modules/juce_gui_basics/desktop/juce_Desktop.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,335 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   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 5 End-User License
 | 
			
		||||
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
 | 
			
		||||
   27th April 2017).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-5-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-5-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())
 | 
			
		||||
{
 | 
			
		||||
    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).
 | 
			
		||||
    WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
 | 
			
		||||
    focusListeners.call ([&] (FocusChangeListener& l) { l.globalFocusChanged (currentFocus.get()); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										416
									
								
								modules/juce_gui_basics/desktop/juce_Desktop.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								modules/juce_gui_basics/desktop/juce_Desktop.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,416 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   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 5 End-User License
 | 
			
		||||
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
 | 
			
		||||
   27th April 2017).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-5-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-5-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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    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 the addGlobalMouseListener()
 | 
			
		||||
        method.
 | 
			
		||||
 | 
			
		||||
        @see addGlobalMouseListener
 | 
			
		||||
    */
 | 
			
		||||
    void removeGlobalMouseListener (MouseListener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a MouseListener that will receive a callback whenever the focused
 | 
			
		||||
        component changes.
 | 
			
		||||
    */
 | 
			
		||||
    void addFocusChangeListener (FocusChangeListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Unregisters a listener that was added with addFocusChangeListener(). */
 | 
			
		||||
    void removeFocusChangeListener (FocusChangeListener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** 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;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    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
 | 
			
		||||
    /** OSX-specific function to check for the "dark" title-bar and menu mode. */
 | 
			
		||||
    static bool isOSXDarkModeActive();
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										380
									
								
								modules/juce_gui_basics/desktop/juce_Displays.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								modules/juce_gui_basics/desktop/juce_Displays.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,380 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   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 5 End-User License
 | 
			
		||||
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
 | 
			
		||||
   27th April 2017).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-5-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-5-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::findDisplayForRect (Rectangle<int> rect, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    int maxArea = -1;
 | 
			
		||||
    Display* retVal = 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;
 | 
			
		||||
            retVal = &display;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *retVal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display& Displays::findDisplayForPoint (Point<int> point, bool isPhysical) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto minDistance = std::numeric_limits<int>::max();
 | 
			
		||||
    Display* retVal = 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;
 | 
			
		||||
            retVal = &display;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *retVal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> Displays::physicalToLogical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto& display = useScaleFactorOfDisplay != nullptr ? *useScaleFactorOfDisplay
 | 
			
		||||
                                                       : findDisplayForRect (rect, true);
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    return ((rect.toFloat() - display.topLeftPhysical.toFloat()) / (display.scale / globalScale)).toNearestInt() + (display.totalArea.getTopLeft() * globalScale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> Displays::logicalToPhysical (Rectangle<int> rect, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto& display = useScaleFactorOfDisplay != nullptr ? *useScaleFactorOfDisplay
 | 
			
		||||
                                                       : findDisplayForRect (rect, false);
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    return ((rect.toFloat() - (display.totalArea.getTopLeft().toFloat() * globalScale)) * (display.scale / globalScale)).toNearestInt() + display.topLeftPhysical;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ValueType>
 | 
			
		||||
Point<ValueType> Displays::physicalToLogical (Point<ValueType> point, const Display* useScaleFactorOfDisplay) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto& display = useScaleFactorOfDisplay != nullptr ? *useScaleFactorOfDisplay
 | 
			
		||||
                                                       : findDisplayForPoint (point.roundToInt(), true);
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    Point<ValueType> logicalTopLeft  (display.totalArea.getX(),       display.totalArea.getY());
 | 
			
		||||
    Point<ValueType> physicalTopLeft (display.topLeftPhysical.getX(), 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
 | 
			
		||||
{
 | 
			
		||||
    auto& display = useScaleFactorOfDisplay != nullptr ? *useScaleFactorOfDisplay
 | 
			
		||||
                                                       : findDisplayForPoint (point.roundToInt(), false);
 | 
			
		||||
 | 
			
		||||
    auto globalScale = Desktop::getInstance().getGlobalScaleFactor();
 | 
			
		||||
 | 
			
		||||
    Point<ValueType> logicalTopLeft  (display.totalArea.getX(),       display.totalArea.getY());
 | 
			
		||||
    Point<ValueType> physicalTopLeft (display.topLeftPhysical.getX(), display.topLeftPhysical.getY());
 | 
			
		||||
 | 
			
		||||
    return ((point - (logicalTopLeft * globalScale)) * (display.scale / globalScale)) + physicalTopLeft;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Displays::Display& Displays::getMainDisplay() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    for (auto& d : displays)
 | 
			
		||||
        if (d.isMain)
 | 
			
		||||
            return d;
 | 
			
		||||
 | 
			
		||||
    // no main display!
 | 
			
		||||
    jassertfalse;
 | 
			
		||||
    return displays.getReference (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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); }
 | 
			
		||||
 | 
			
		||||
// Deprecated method
 | 
			
		||||
const Displays::Display& Displays::getDisplayContaining (Point<int> position) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// 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, const 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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
							
								
								
									
										145
									
								
								modules/juce_gui_basics/desktop/juce_Displays.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								modules/juce_gui_basics/desktop/juce_Displays.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2017 - ROLI Ltd.
 | 
			
		||||
 | 
			
		||||
   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 5 End-User License
 | 
			
		||||
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
 | 
			
		||||
   27th April 2017).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-5-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-5-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 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;
 | 
			
		||||
 | 
			
		||||
        /** 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 a 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>, const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a 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>, const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a Point from physical to logical pixels. */
 | 
			
		||||
    template <typename ValueType>
 | 
			
		||||
    Point<ValueType> physicalToLogical (Point<ValueType>, const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Converts a Point from logical to physical pixels. */
 | 
			
		||||
    template <typename ValueType>
 | 
			
		||||
    Point<ValueType> logicalToPhysical (Point<ValueType>, const Display* useScaleFactorOfDisplay = nullptr) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display containing a given Rectangle (either
 | 
			
		||||
        in logical or physical pixels).
 | 
			
		||||
 | 
			
		||||
        If the Rectangle lies outside all the displays then the nearest one will be returned.
 | 
			
		||||
    */
 | 
			
		||||
    const Display& findDisplayForRect (Rectangle<int>, bool isPhysical = false)  const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display containing a given Point (either
 | 
			
		||||
        in logical or physical pixels).
 | 
			
		||||
 | 
			
		||||
        If the Point lies outside all the displays then the nearest one will be returned.
 | 
			
		||||
    */
 | 
			
		||||
    const Display& findDisplayForPoint (Point<int>, bool isPhysical = false)  const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the Display object representing the display acting as the user's main screen. */
 | 
			
		||||
    const Display& getMainDisplay() 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;
 | 
			
		||||
    // This method has been deprecated - use the findDisplayForPoint() or findDisplayForRect() methods instead
 | 
			
		||||
    // as they can deal with converting between logical and physical pixels
 | 
			
		||||
    JUCE_DEPRECATED (const Display& getDisplayContaining (Point<int> position) const noexcept);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend class Desktop;
 | 
			
		||||
 | 
			
		||||
    void init (Desktop&);
 | 
			
		||||
    void findDisplays (float masterScale);
 | 
			
		||||
 | 
			
		||||
    void updateToLogical();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
		Reference in New Issue
	
	Block a user