migrating to the latest JUCE version
This commit is contained in:
		@@ -1,207 +1,207 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Models a 1-dimensional position that can be dragged around by the user, and which
 | 
			
		||||
    will then continue moving with a customisable physics behaviour when released.
 | 
			
		||||
 | 
			
		||||
    This is useful for things like scrollable views or objects that can be dragged and
 | 
			
		||||
    thrown around with the mouse/touch, and by writing your own behaviour class, you can
 | 
			
		||||
    customise the trajectory that it follows when released.
 | 
			
		||||
 | 
			
		||||
    The class uses its own Timer to continuously change its value when a drag ends, and
 | 
			
		||||
    Listener objects can be registered to receive callbacks whenever the value changes.
 | 
			
		||||
 | 
			
		||||
    The value is stored as a double, and can be used to represent whatever units you need.
 | 
			
		||||
 | 
			
		||||
    The template parameter Behaviour must be a class that implements various methods to
 | 
			
		||||
    return the physics of the value's movement - you can use the classes provided for this
 | 
			
		||||
    in the AnimatedPositionBehaviours namespace, or write your own custom behaviour.
 | 
			
		||||
 | 
			
		||||
    @see AnimatedPositionBehaviours::ContinuousWithMomentum,
 | 
			
		||||
         AnimatedPositionBehaviours::SnapToPageBoundaries
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
template <typename Behaviour>
 | 
			
		||||
class AnimatedPosition  : private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    AnimatedPosition()
 | 
			
		||||
        :  range (-std::numeric_limits<double>::max(),
 | 
			
		||||
                   std::numeric_limits<double>::max())
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Sets a range within which the value will be constrained. */
 | 
			
		||||
    void setLimits (Range<double> newRange) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        range = newRange;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Called to indicate that the object is now being controlled by a
 | 
			
		||||
        mouse-drag or similar operation.
 | 
			
		||||
 | 
			
		||||
        After calling this method, you should make calls to the drag() method
 | 
			
		||||
        each time the mouse drags the position around, and always be sure to
 | 
			
		||||
        finish with a call to endDrag() when the mouse is released, which allows
 | 
			
		||||
        the position to continue moving freely according to the specified behaviour.
 | 
			
		||||
    */
 | 
			
		||||
    void beginDrag()
 | 
			
		||||
    {
 | 
			
		||||
        grabbedPos = position;
 | 
			
		||||
        releaseVelocity = 0;
 | 
			
		||||
        stopTimer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Called during a mouse-drag operation, to indicate that the mouse has moved.
 | 
			
		||||
        The delta is the difference between the position when beginDrag() was called
 | 
			
		||||
        and the new position that's required.
 | 
			
		||||
    */
 | 
			
		||||
    void drag (double deltaFromStartOfDrag)
 | 
			
		||||
    {
 | 
			
		||||
        moveTo (grabbedPos + deltaFromStartOfDrag);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Called after beginDrag() and drag() to indicate that the drag operation has
 | 
			
		||||
        now finished.
 | 
			
		||||
    */
 | 
			
		||||
    void endDrag()
 | 
			
		||||
    {
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Called outside of a drag operation to cause a nudge in the specified direction.
 | 
			
		||||
        This is intended for use by e.g. mouse-wheel events.
 | 
			
		||||
    */
 | 
			
		||||
    void nudge (double deltaFromCurrentPosition)
 | 
			
		||||
    {
 | 
			
		||||
        startTimerHz (10);
 | 
			
		||||
        moveTo (position + deltaFromCurrentPosition);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the current position. */
 | 
			
		||||
    double getPosition() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return position;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Explicitly sets the position and stops any further movement.
 | 
			
		||||
        This will cause a synchronous call to any listeners if the position actually
 | 
			
		||||
        changes.
 | 
			
		||||
    */
 | 
			
		||||
    void setPosition (double newPosition)
 | 
			
		||||
    {
 | 
			
		||||
        stopTimer();
 | 
			
		||||
        setPositionAndSendChange (newPosition);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Implement this class if you need to receive callbacks when the value of
 | 
			
		||||
        an AnimatedPosition changes.
 | 
			
		||||
        @see AnimatedPosition::addListener, AnimatedPosition::removeListener
 | 
			
		||||
    */
 | 
			
		||||
    class Listener
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~Listener() = default;
 | 
			
		||||
 | 
			
		||||
        /** Called synchronously when an AnimatedPosition changes. */
 | 
			
		||||
        virtual void positionChanged (AnimatedPosition&, double newPosition) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Adds a listener to be called when the value changes. */
 | 
			
		||||
    void addListener (Listener* listener)       { listeners.add (listener); }
 | 
			
		||||
 | 
			
		||||
    /** Removes a previously-registered listener. */
 | 
			
		||||
    void removeListener (Listener* listener)    { listeners.remove (listener); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The behaviour object.
 | 
			
		||||
        This is public to let you tweak any parameters that it provides.
 | 
			
		||||
    */
 | 
			
		||||
    Behaviour behaviour;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    double position = 0.0, grabbedPos = 0.0, releaseVelocity = 0.0;
 | 
			
		||||
    Range<double> range;
 | 
			
		||||
    Time lastUpdate, lastDrag;
 | 
			
		||||
    ListenerList<Listener> listeners;
 | 
			
		||||
 | 
			
		||||
    static double getSpeed (const Time last, double lastPos,
 | 
			
		||||
                            const Time now, double newPos)
 | 
			
		||||
    {
 | 
			
		||||
        auto elapsedSecs = jmax (0.005, (now - last).inSeconds());
 | 
			
		||||
        auto v = (newPos - lastPos) / elapsedSecs;
 | 
			
		||||
        return std::abs (v) > 0.2 ? v : 0.0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void moveTo (double newPos)
 | 
			
		||||
    {
 | 
			
		||||
        auto now = Time::getCurrentTime();
 | 
			
		||||
        releaseVelocity = getSpeed (lastDrag, position, now, newPos);
 | 
			
		||||
        behaviour.releasedWithVelocity (newPos, releaseVelocity);
 | 
			
		||||
        lastDrag = now;
 | 
			
		||||
 | 
			
		||||
        setPositionAndSendChange (newPos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setPositionAndSendChange (double newPosition)
 | 
			
		||||
    {
 | 
			
		||||
        newPosition = range.clipValue (newPosition);
 | 
			
		||||
 | 
			
		||||
        if (position != newPosition)
 | 
			
		||||
        {
 | 
			
		||||
            position = newPosition;
 | 
			
		||||
            listeners.call ([this, newPosition] (Listener& l) { l.positionChanged (*this, newPosition); });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        auto now = Time::getCurrentTime();
 | 
			
		||||
        auto elapsed = jlimit (0.001, 0.020, (now - lastUpdate).inSeconds());
 | 
			
		||||
        lastUpdate = now;
 | 
			
		||||
        auto newPos = behaviour.getNextPosition (position, elapsed);
 | 
			
		||||
 | 
			
		||||
        if (behaviour.isStopped (newPos))
 | 
			
		||||
            stopTimer();
 | 
			
		||||
        else
 | 
			
		||||
            startTimerHz (60);
 | 
			
		||||
 | 
			
		||||
        setPositionAndSendChange (newPos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedPosition)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Models a 1-dimensional position that can be dragged around by the user, and which
 | 
			
		||||
    will then continue moving with a customisable physics behaviour when released.
 | 
			
		||||
 | 
			
		||||
    This is useful for things like scrollable views or objects that can be dragged and
 | 
			
		||||
    thrown around with the mouse/touch, and by writing your own behaviour class, you can
 | 
			
		||||
    customise the trajectory that it follows when released.
 | 
			
		||||
 | 
			
		||||
    The class uses its own Timer to continuously change its value when a drag ends, and
 | 
			
		||||
    Listener objects can be registered to receive callbacks whenever the value changes.
 | 
			
		||||
 | 
			
		||||
    The value is stored as a double, and can be used to represent whatever units you need.
 | 
			
		||||
 | 
			
		||||
    The template parameter Behaviour must be a class that implements various methods to
 | 
			
		||||
    return the physics of the value's movement - you can use the classes provided for this
 | 
			
		||||
    in the AnimatedPositionBehaviours namespace, or write your own custom behaviour.
 | 
			
		||||
 | 
			
		||||
    @see AnimatedPositionBehaviours::ContinuousWithMomentum,
 | 
			
		||||
         AnimatedPositionBehaviours::SnapToPageBoundaries
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
template <typename Behaviour>
 | 
			
		||||
class AnimatedPosition  : private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    AnimatedPosition()
 | 
			
		||||
        :  range (-std::numeric_limits<double>::max(),
 | 
			
		||||
                   std::numeric_limits<double>::max())
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Sets a range within which the value will be constrained. */
 | 
			
		||||
    void setLimits (Range<double> newRange) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        range = newRange;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Called to indicate that the object is now being controlled by a
 | 
			
		||||
        mouse-drag or similar operation.
 | 
			
		||||
 | 
			
		||||
        After calling this method, you should make calls to the drag() method
 | 
			
		||||
        each time the mouse drags the position around, and always be sure to
 | 
			
		||||
        finish with a call to endDrag() when the mouse is released, which allows
 | 
			
		||||
        the position to continue moving freely according to the specified behaviour.
 | 
			
		||||
    */
 | 
			
		||||
    void beginDrag()
 | 
			
		||||
    {
 | 
			
		||||
        grabbedPos = position;
 | 
			
		||||
        releaseVelocity = 0;
 | 
			
		||||
        stopTimer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Called during a mouse-drag operation, to indicate that the mouse has moved.
 | 
			
		||||
        The delta is the difference between the position when beginDrag() was called
 | 
			
		||||
        and the new position that's required.
 | 
			
		||||
    */
 | 
			
		||||
    void drag (double deltaFromStartOfDrag)
 | 
			
		||||
    {
 | 
			
		||||
        moveTo (grabbedPos + deltaFromStartOfDrag);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Called after beginDrag() and drag() to indicate that the drag operation has
 | 
			
		||||
        now finished.
 | 
			
		||||
    */
 | 
			
		||||
    void endDrag()
 | 
			
		||||
    {
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Called outside of a drag operation to cause a nudge in the specified direction.
 | 
			
		||||
        This is intended for use by e.g. mouse-wheel events.
 | 
			
		||||
    */
 | 
			
		||||
    void nudge (double deltaFromCurrentPosition)
 | 
			
		||||
    {
 | 
			
		||||
        startTimerHz (10);
 | 
			
		||||
        moveTo (position + deltaFromCurrentPosition);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the current position. */
 | 
			
		||||
    double getPosition() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return position;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Explicitly sets the position and stops any further movement.
 | 
			
		||||
        This will cause a synchronous call to any listeners if the position actually
 | 
			
		||||
        changes.
 | 
			
		||||
    */
 | 
			
		||||
    void setPosition (double newPosition)
 | 
			
		||||
    {
 | 
			
		||||
        stopTimer();
 | 
			
		||||
        setPositionAndSendChange (newPosition);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Implement this class if you need to receive callbacks when the value of
 | 
			
		||||
        an AnimatedPosition changes.
 | 
			
		||||
        @see AnimatedPosition::addListener, AnimatedPosition::removeListener
 | 
			
		||||
    */
 | 
			
		||||
    class Listener
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~Listener() = default;
 | 
			
		||||
 | 
			
		||||
        /** Called synchronously when an AnimatedPosition changes. */
 | 
			
		||||
        virtual void positionChanged (AnimatedPosition&, double newPosition) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Adds a listener to be called when the value changes. */
 | 
			
		||||
    void addListener (Listener* listener)       { listeners.add (listener); }
 | 
			
		||||
 | 
			
		||||
    /** Removes a previously-registered listener. */
 | 
			
		||||
    void removeListener (Listener* listener)    { listeners.remove (listener); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The behaviour object.
 | 
			
		||||
        This is public to let you tweak any parameters that it provides.
 | 
			
		||||
    */
 | 
			
		||||
    Behaviour behaviour;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    double position = 0.0, grabbedPos = 0.0, releaseVelocity = 0.0;
 | 
			
		||||
    Range<double> range;
 | 
			
		||||
    Time lastUpdate, lastDrag;
 | 
			
		||||
    ListenerList<Listener> listeners;
 | 
			
		||||
 | 
			
		||||
    static double getSpeed (const Time last, double lastPos,
 | 
			
		||||
                            const Time now, double newPos)
 | 
			
		||||
    {
 | 
			
		||||
        auto elapsedSecs = jmax (0.005, (now - last).inSeconds());
 | 
			
		||||
        auto v = (newPos - lastPos) / elapsedSecs;
 | 
			
		||||
        return std::abs (v) > 0.2 ? v : 0.0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void moveTo (double newPos)
 | 
			
		||||
    {
 | 
			
		||||
        auto now = Time::getCurrentTime();
 | 
			
		||||
        releaseVelocity = getSpeed (lastDrag, position, now, newPos);
 | 
			
		||||
        behaviour.releasedWithVelocity (newPos, releaseVelocity);
 | 
			
		||||
        lastDrag = now;
 | 
			
		||||
 | 
			
		||||
        setPositionAndSendChange (newPos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setPositionAndSendChange (double newPosition)
 | 
			
		||||
    {
 | 
			
		||||
        newPosition = range.clipValue (newPosition);
 | 
			
		||||
 | 
			
		||||
        if (position != newPosition)
 | 
			
		||||
        {
 | 
			
		||||
            position = newPosition;
 | 
			
		||||
            listeners.call ([this, newPosition] (Listener& l) { l.positionChanged (*this, newPosition); });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        auto now = Time::getCurrentTime();
 | 
			
		||||
        auto elapsed = jlimit (0.001, 0.020, (now - lastUpdate).inSeconds());
 | 
			
		||||
        lastUpdate = now;
 | 
			
		||||
        auto newPos = behaviour.getNextPosition (position, elapsed);
 | 
			
		||||
 | 
			
		||||
        if (behaviour.isStopped (newPos))
 | 
			
		||||
            stopTimer();
 | 
			
		||||
        else
 | 
			
		||||
            startTimerHz (60);
 | 
			
		||||
 | 
			
		||||
        setPositionAndSendChange (newPos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedPosition)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,156 +1,156 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** Contains classes for different types of physics behaviours - these classes
 | 
			
		||||
    are used as template parameters for the AnimatedPosition class.
 | 
			
		||||
*/
 | 
			
		||||
namespace AnimatedPositionBehaviours
 | 
			
		||||
{
 | 
			
		||||
    /** A non-snapping behaviour that allows the content to be freely flicked in
 | 
			
		||||
        either direction, with momentum based on the velocity at which it was
 | 
			
		||||
        released, and variable friction to make it come to a halt.
 | 
			
		||||
 | 
			
		||||
        This class is intended to be used as a template parameter to the
 | 
			
		||||
        AnimatedPosition class.
 | 
			
		||||
 | 
			
		||||
        @see AnimatedPosition
 | 
			
		||||
 | 
			
		||||
        @tags{GUI}
 | 
			
		||||
    */
 | 
			
		||||
    struct ContinuousWithMomentum
 | 
			
		||||
    {
 | 
			
		||||
        ContinuousWithMomentum() = default;
 | 
			
		||||
 | 
			
		||||
        /** Sets the friction that damps the movement of the value.
 | 
			
		||||
            A typical value is 0.08; higher values indicate more friction.
 | 
			
		||||
        */
 | 
			
		||||
        void setFriction (double newFriction) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            damping = 1.0 - newFriction;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Sets the minimum velocity of the movement. Any velocity that's slower than
 | 
			
		||||
            this will stop the animation. The default is 0.05. */
 | 
			
		||||
        void setMinimumVelocity (double newMinimumVelocityToUse) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            minimumVelocity = newMinimumVelocityToUse;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class. This tells us the position and
 | 
			
		||||
            velocity at which the user is about to release the object.
 | 
			
		||||
            The velocity is measured in units/second.
 | 
			
		||||
        */
 | 
			
		||||
        void releasedWithVelocity (double /*position*/, double releaseVelocity) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            velocity = releaseVelocity;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class to get the new position, after
 | 
			
		||||
            the given time has elapsed.
 | 
			
		||||
        */
 | 
			
		||||
        double getNextPosition (double oldPos, double elapsedSeconds) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            velocity *= damping;
 | 
			
		||||
 | 
			
		||||
            if (std::abs (velocity) < minimumVelocity)
 | 
			
		||||
                velocity = 0;
 | 
			
		||||
 | 
			
		||||
            return oldPos + velocity * elapsedSeconds;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class to check whether the object
 | 
			
		||||
            is now stationary.
 | 
			
		||||
        */
 | 
			
		||||
        bool isStopped (double /*position*/) const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            return velocity == 0.0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        double velocity = 0, damping = 0.92, minimumVelocity = 0.05;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A behaviour that gravitates an AnimatedPosition object towards the nearest
 | 
			
		||||
        integer position when released.
 | 
			
		||||
 | 
			
		||||
        This class is intended to be used as a template parameter to the
 | 
			
		||||
        AnimatedPosition class. It's handy when using an AnimatedPosition to show a
 | 
			
		||||
        series of pages, because it allows the pages can be scrolled smoothly, but when
 | 
			
		||||
        released, snaps back to show a whole page.
 | 
			
		||||
 | 
			
		||||
        @see AnimatedPosition
 | 
			
		||||
 | 
			
		||||
        @tags{GUI}
 | 
			
		||||
    */
 | 
			
		||||
    struct SnapToPageBoundaries
 | 
			
		||||
    {
 | 
			
		||||
        SnapToPageBoundaries() = default;
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class. This tells us the position and
 | 
			
		||||
            velocity at which the user is about to release the object.
 | 
			
		||||
            The velocity is measured in units/second.
 | 
			
		||||
        */
 | 
			
		||||
        void releasedWithVelocity (double position, double releaseVelocity) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            targetSnapPosition = std::floor (position + 0.5);
 | 
			
		||||
 | 
			
		||||
            if (releaseVelocity >  1.0 && targetSnapPosition < position)  ++targetSnapPosition;
 | 
			
		||||
            if (releaseVelocity < -1.0 && targetSnapPosition > position)  --targetSnapPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class to get the new position, after
 | 
			
		||||
            the given time has elapsed.
 | 
			
		||||
        */
 | 
			
		||||
        double getNextPosition (double oldPos, double elapsedSeconds) const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            if (isStopped (oldPos))
 | 
			
		||||
                return targetSnapPosition;
 | 
			
		||||
 | 
			
		||||
            const double snapSpeed = 10.0;
 | 
			
		||||
            const double velocity = (targetSnapPosition - oldPos) * snapSpeed;
 | 
			
		||||
            const double newPos = oldPos + velocity * elapsedSeconds;
 | 
			
		||||
 | 
			
		||||
            return isStopped (newPos) ? targetSnapPosition : newPos;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class to check whether the object
 | 
			
		||||
            is now stationary.
 | 
			
		||||
        */
 | 
			
		||||
        bool isStopped (double position) const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            return std::abs (targetSnapPosition - position) < 0.001;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        double targetSnapPosition = 0.0;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** Contains classes for different types of physics behaviours - these classes
 | 
			
		||||
    are used as template parameters for the AnimatedPosition class.
 | 
			
		||||
*/
 | 
			
		||||
namespace AnimatedPositionBehaviours
 | 
			
		||||
{
 | 
			
		||||
    /** A non-snapping behaviour that allows the content to be freely flicked in
 | 
			
		||||
        either direction, with momentum based on the velocity at which it was
 | 
			
		||||
        released, and variable friction to make it come to a halt.
 | 
			
		||||
 | 
			
		||||
        This class is intended to be used as a template parameter to the
 | 
			
		||||
        AnimatedPosition class.
 | 
			
		||||
 | 
			
		||||
        @see AnimatedPosition
 | 
			
		||||
 | 
			
		||||
        @tags{GUI}
 | 
			
		||||
    */
 | 
			
		||||
    struct ContinuousWithMomentum
 | 
			
		||||
    {
 | 
			
		||||
        ContinuousWithMomentum() = default;
 | 
			
		||||
 | 
			
		||||
        /** Sets the friction that damps the movement of the value.
 | 
			
		||||
            A typical value is 0.08; higher values indicate more friction.
 | 
			
		||||
        */
 | 
			
		||||
        void setFriction (double newFriction) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            damping = 1.0 - newFriction;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Sets the minimum velocity of the movement. Any velocity that's slower than
 | 
			
		||||
            this will stop the animation. The default is 0.05. */
 | 
			
		||||
        void setMinimumVelocity (double newMinimumVelocityToUse) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            minimumVelocity = newMinimumVelocityToUse;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class. This tells us the position and
 | 
			
		||||
            velocity at which the user is about to release the object.
 | 
			
		||||
            The velocity is measured in units/second.
 | 
			
		||||
        */
 | 
			
		||||
        void releasedWithVelocity (double /*position*/, double releaseVelocity) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            velocity = releaseVelocity;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class to get the new position, after
 | 
			
		||||
            the given time has elapsed.
 | 
			
		||||
        */
 | 
			
		||||
        double getNextPosition (double oldPos, double elapsedSeconds) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            velocity *= damping;
 | 
			
		||||
 | 
			
		||||
            if (std::abs (velocity) < minimumVelocity)
 | 
			
		||||
                velocity = 0;
 | 
			
		||||
 | 
			
		||||
            return oldPos + velocity * elapsedSeconds;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class to check whether the object
 | 
			
		||||
            is now stationary.
 | 
			
		||||
        */
 | 
			
		||||
        bool isStopped (double /*position*/) const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            return velocity == 0.0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        double velocity = 0, damping = 0.92, minimumVelocity = 0.05;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A behaviour that gravitates an AnimatedPosition object towards the nearest
 | 
			
		||||
        integer position when released.
 | 
			
		||||
 | 
			
		||||
        This class is intended to be used as a template parameter to the
 | 
			
		||||
        AnimatedPosition class. It's handy when using an AnimatedPosition to show a
 | 
			
		||||
        series of pages, because it allows the pages can be scrolled smoothly, but when
 | 
			
		||||
        released, snaps back to show a whole page.
 | 
			
		||||
 | 
			
		||||
        @see AnimatedPosition
 | 
			
		||||
 | 
			
		||||
        @tags{GUI}
 | 
			
		||||
    */
 | 
			
		||||
    struct SnapToPageBoundaries
 | 
			
		||||
    {
 | 
			
		||||
        SnapToPageBoundaries() = default;
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class. This tells us the position and
 | 
			
		||||
            velocity at which the user is about to release the object.
 | 
			
		||||
            The velocity is measured in units/second.
 | 
			
		||||
        */
 | 
			
		||||
        void releasedWithVelocity (double position, double releaseVelocity) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            targetSnapPosition = std::floor (position + 0.5);
 | 
			
		||||
 | 
			
		||||
            if (releaseVelocity >  1.0 && targetSnapPosition < position)  ++targetSnapPosition;
 | 
			
		||||
            if (releaseVelocity < -1.0 && targetSnapPosition > position)  --targetSnapPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class to get the new position, after
 | 
			
		||||
            the given time has elapsed.
 | 
			
		||||
        */
 | 
			
		||||
        double getNextPosition (double oldPos, double elapsedSeconds) const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            if (isStopped (oldPos))
 | 
			
		||||
                return targetSnapPosition;
 | 
			
		||||
 | 
			
		||||
            const double snapSpeed = 10.0;
 | 
			
		||||
            const double velocity = (targetSnapPosition - oldPos) * snapSpeed;
 | 
			
		||||
            const double newPos = oldPos + velocity * elapsedSeconds;
 | 
			
		||||
 | 
			
		||||
            return isStopped (newPos) ? targetSnapPosition : newPos;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Called by the AnimatedPosition class to check whether the object
 | 
			
		||||
            is now stationary.
 | 
			
		||||
        */
 | 
			
		||||
        bool isStopped (double position) const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            return std::abs (targetSnapPosition - position) < 0.001;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        double targetSnapPosition = 0.0;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,354 +1,354 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class ComponentAnimator::AnimationTask
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    AnimationTask (Component* c) noexcept  : component (c) {}
 | 
			
		||||
 | 
			
		||||
    ~AnimationTask()
 | 
			
		||||
    {
 | 
			
		||||
        proxy.deleteAndZero();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void reset (const Rectangle<int>& finalBounds,
 | 
			
		||||
                float finalAlpha,
 | 
			
		||||
                int millisecondsToSpendMoving,
 | 
			
		||||
                bool useProxyComponent,
 | 
			
		||||
                double startSpd, double endSpd)
 | 
			
		||||
    {
 | 
			
		||||
        msElapsed = 0;
 | 
			
		||||
        msTotal = jmax (1, millisecondsToSpendMoving);
 | 
			
		||||
        lastProgress = 0;
 | 
			
		||||
        destination = finalBounds;
 | 
			
		||||
        destAlpha = finalAlpha;
 | 
			
		||||
 | 
			
		||||
        isMoving = (finalBounds != component->getBounds());
 | 
			
		||||
        isChangingAlpha = (finalAlpha != component->getAlpha());
 | 
			
		||||
 | 
			
		||||
        left    = component->getX();
 | 
			
		||||
        top     = component->getY();
 | 
			
		||||
        right   = component->getRight();
 | 
			
		||||
        bottom  = component->getBottom();
 | 
			
		||||
        alpha   = component->getAlpha();
 | 
			
		||||
 | 
			
		||||
        const double invTotalDistance = 4.0 / (startSpd + endSpd + 2.0);
 | 
			
		||||
        startSpeed = jmax (0.0, startSpd * invTotalDistance);
 | 
			
		||||
        midSpeed = invTotalDistance;
 | 
			
		||||
        endSpeed = jmax (0.0, endSpd * invTotalDistance);
 | 
			
		||||
 | 
			
		||||
        proxy.deleteAndZero();
 | 
			
		||||
 | 
			
		||||
        if (useProxyComponent)
 | 
			
		||||
            proxy = new ProxyComponent (*component);
 | 
			
		||||
 | 
			
		||||
        component->setVisible (! useProxyComponent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool useTimeslice (const int elapsed)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* c = proxy != nullptr ? proxy.getComponent()
 | 
			
		||||
                                       : component.get())
 | 
			
		||||
        {
 | 
			
		||||
            msElapsed += elapsed;
 | 
			
		||||
            double newProgress = msElapsed / (double) msTotal;
 | 
			
		||||
 | 
			
		||||
            if (newProgress >= 0 && newProgress < 1.0)
 | 
			
		||||
            {
 | 
			
		||||
                const WeakReference<AnimationTask> weakRef (this);
 | 
			
		||||
                newProgress = timeToDistance (newProgress);
 | 
			
		||||
                const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
 | 
			
		||||
                jassert (newProgress >= lastProgress);
 | 
			
		||||
                lastProgress = newProgress;
 | 
			
		||||
 | 
			
		||||
                if (delta < 1.0)
 | 
			
		||||
                {
 | 
			
		||||
                    bool stillBusy = false;
 | 
			
		||||
 | 
			
		||||
                    if (isMoving)
 | 
			
		||||
                    {
 | 
			
		||||
                        left   += (destination.getX()      - left)   * delta;
 | 
			
		||||
                        top    += (destination.getY()      - top)    * delta;
 | 
			
		||||
                        right  += (destination.getRight()  - right)  * delta;
 | 
			
		||||
                        bottom += (destination.getBottom() - bottom) * delta;
 | 
			
		||||
 | 
			
		||||
                        const Rectangle<int> newBounds (roundToInt (left),
 | 
			
		||||
                                                        roundToInt (top),
 | 
			
		||||
                                                        roundToInt (right - left),
 | 
			
		||||
                                                        roundToInt (bottom - top));
 | 
			
		||||
 | 
			
		||||
                        if (newBounds != destination)
 | 
			
		||||
                        {
 | 
			
		||||
                            c->setBounds (newBounds);
 | 
			
		||||
                            stillBusy = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Check whether the animation was cancelled/deleted during
 | 
			
		||||
                    // a callback during the setBounds method
 | 
			
		||||
                    if (weakRef.wasObjectDeleted())
 | 
			
		||||
                        return false;
 | 
			
		||||
 | 
			
		||||
                    if (isChangingAlpha)
 | 
			
		||||
                    {
 | 
			
		||||
                        alpha += (destAlpha - alpha) * delta;
 | 
			
		||||
                        c->setAlpha ((float) alpha);
 | 
			
		||||
                        stillBusy = true;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (stillBusy)
 | 
			
		||||
                        return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        moveToFinalDestination();
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void moveToFinalDestination()
 | 
			
		||||
    {
 | 
			
		||||
        if (component != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            const WeakReference<AnimationTask> weakRef (this);
 | 
			
		||||
            component->setAlpha ((float) destAlpha);
 | 
			
		||||
            component->setBounds (destination);
 | 
			
		||||
 | 
			
		||||
            if (! weakRef.wasObjectDeleted())
 | 
			
		||||
                if (proxy != nullptr)
 | 
			
		||||
                    component->setVisible (destAlpha > 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct ProxyComponent  : public Component
 | 
			
		||||
    {
 | 
			
		||||
        ProxyComponent (Component& c)
 | 
			
		||||
        {
 | 
			
		||||
            setWantsKeyboardFocus (false);
 | 
			
		||||
            setBounds (c.getBounds());
 | 
			
		||||
            setTransform (c.getTransform());
 | 
			
		||||
            setAlpha (c.getAlpha());
 | 
			
		||||
            setInterceptsMouseClicks (false, false);
 | 
			
		||||
 | 
			
		||||
            if (auto* parent = c.getParentComponent())
 | 
			
		||||
                parent->addAndMakeVisible (this);
 | 
			
		||||
            else if (c.isOnDesktop() && c.getPeer() != nullptr)
 | 
			
		||||
                addToDesktop (c.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses);
 | 
			
		||||
            else
 | 
			
		||||
                jassertfalse; // seem to be trying to animate a component that's not visible..
 | 
			
		||||
 | 
			
		||||
            auto scale = (float) Desktop::getInstance().getDisplays().getDisplayForRect (getScreenBounds())->scale
 | 
			
		||||
                           * Component::getApproximateScaleFactorForComponent (&c);
 | 
			
		||||
 | 
			
		||||
            image = c.createComponentSnapshot (c.getLocalBounds(), false, scale);
 | 
			
		||||
 | 
			
		||||
            setVisible (true);
 | 
			
		||||
            toBehind (&c);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void paint (Graphics& g) override
 | 
			
		||||
        {
 | 
			
		||||
            g.setOpacity (1.0f);
 | 
			
		||||
            g.drawImageTransformed (image, AffineTransform::scale ((float) getWidth()  / (float) jmax (1, image.getWidth()),
 | 
			
		||||
                                                                   (float) getHeight() / (float) jmax (1, image.getHeight())), false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
 | 
			
		||||
        {
 | 
			
		||||
            return createIgnoredAccessibilityHandler (*this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Image image;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    Component::SafePointer<Component> proxy;
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> destination;
 | 
			
		||||
    double destAlpha;
 | 
			
		||||
 | 
			
		||||
    int msElapsed, msTotal;
 | 
			
		||||
    double startSpeed, midSpeed, endSpeed, lastProgress;
 | 
			
		||||
    double left, top, right, bottom, alpha;
 | 
			
		||||
    bool isMoving, isChangingAlpha;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    double timeToDistance (const double time) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
 | 
			
		||||
                            : 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
 | 
			
		||||
                                + (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_WEAK_REFERENCEABLE (AnimationTask)
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimationTask)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ComponentAnimator::ComponentAnimator()  : lastTime (0) {}
 | 
			
		||||
ComponentAnimator::~ComponentAnimator() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = tasks.size(); --i >= 0;)
 | 
			
		||||
        if (component == tasks.getUnchecked(i)->component.get())
 | 
			
		||||
            return tasks.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::animateComponent (Component* const component,
 | 
			
		||||
                                          const Rectangle<int>& finalBounds,
 | 
			
		||||
                                          const float finalAlpha,
 | 
			
		||||
                                          const int millisecondsToSpendMoving,
 | 
			
		||||
                                          const bool useProxyComponent,
 | 
			
		||||
                                          const double startSpeed,
 | 
			
		||||
                                          const double endSpeed)
 | 
			
		||||
{
 | 
			
		||||
    // the speeds must be 0 or greater!
 | 
			
		||||
    jassert (startSpeed >= 0 && endSpeed >= 0);
 | 
			
		||||
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto* at = findTaskFor (component);
 | 
			
		||||
 | 
			
		||||
        if (at == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            at = new AnimationTask (component);
 | 
			
		||||
            tasks.add (at);
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        at->reset (finalBounds, finalAlpha, millisecondsToSpendMoving,
 | 
			
		||||
                   useProxyComponent, startSpeed, endSpeed);
 | 
			
		||||
 | 
			
		||||
        if (! isTimerRunning())
 | 
			
		||||
        {
 | 
			
		||||
            lastTime = Time::getMillisecondCounter();
 | 
			
		||||
            startTimerHz (50);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (component->isShowing() && millisecondsToTake > 0)
 | 
			
		||||
            animateComponent (component, component->getBounds(), 0.0f, millisecondsToTake, true, 1.0, 1.0);
 | 
			
		||||
 | 
			
		||||
        component->setVisible (false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f))
 | 
			
		||||
    {
 | 
			
		||||
        component->setAlpha (0.0f);
 | 
			
		||||
        component->setVisible (true);
 | 
			
		||||
        animateComponent (component, component->getBounds(), 1.0f, millisecondsToTake, false, 1.0, 1.0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
 | 
			
		||||
{
 | 
			
		||||
    if (tasks.size() > 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (moveComponentsToTheirFinalPositions)
 | 
			
		||||
            for (int i = tasks.size(); --i >= 0;)
 | 
			
		||||
                tasks.getUnchecked(i)->moveToFinalDestination();
 | 
			
		||||
 | 
			
		||||
        tasks.clear();
 | 
			
		||||
        sendChangeMessage();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::cancelAnimation (Component* const component,
 | 
			
		||||
                                         const bool moveComponentToItsFinalPosition)
 | 
			
		||||
{
 | 
			
		||||
    if (auto* at = findTaskFor (component))
 | 
			
		||||
    {
 | 
			
		||||
        if (moveComponentToItsFinalPosition)
 | 
			
		||||
            at->moveToFinalDestination();
 | 
			
		||||
 | 
			
		||||
        tasks.removeObject (at);
 | 
			
		||||
        sendChangeMessage();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
 | 
			
		||||
{
 | 
			
		||||
    jassert (component != nullptr);
 | 
			
		||||
 | 
			
		||||
    if (auto* at = findTaskFor (component))
 | 
			
		||||
        return at->destination;
 | 
			
		||||
 | 
			
		||||
    return component->getBounds();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ComponentAnimator::isAnimating (Component* component) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return findTaskFor (component) != nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ComponentAnimator::isAnimating() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return tasks.size() != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::timerCallback()
 | 
			
		||||
{
 | 
			
		||||
    auto timeNow = Time::getMillisecondCounter();
 | 
			
		||||
 | 
			
		||||
    if (lastTime == 0)
 | 
			
		||||
        lastTime = timeNow;
 | 
			
		||||
 | 
			
		||||
    auto elapsed = (int) (timeNow - lastTime);
 | 
			
		||||
 | 
			
		||||
    for (auto* task : Array<AnimationTask*> (tasks.begin(), tasks.size()))
 | 
			
		||||
    {
 | 
			
		||||
        if (tasks.contains (task) && ! task->useTimeslice (elapsed))
 | 
			
		||||
        {
 | 
			
		||||
            tasks.removeObject (task);
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lastTime = timeNow;
 | 
			
		||||
 | 
			
		||||
    if (tasks.size() == 0)
 | 
			
		||||
        stopTimer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class ComponentAnimator::AnimationTask
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    AnimationTask (Component* c) noexcept  : component (c) {}
 | 
			
		||||
 | 
			
		||||
    ~AnimationTask()
 | 
			
		||||
    {
 | 
			
		||||
        proxy.deleteAndZero();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void reset (const Rectangle<int>& finalBounds,
 | 
			
		||||
                float finalAlpha,
 | 
			
		||||
                int millisecondsToSpendMoving,
 | 
			
		||||
                bool useProxyComponent,
 | 
			
		||||
                double startSpd, double endSpd)
 | 
			
		||||
    {
 | 
			
		||||
        msElapsed = 0;
 | 
			
		||||
        msTotal = jmax (1, millisecondsToSpendMoving);
 | 
			
		||||
        lastProgress = 0;
 | 
			
		||||
        destination = finalBounds;
 | 
			
		||||
        destAlpha = finalAlpha;
 | 
			
		||||
 | 
			
		||||
        isMoving = (finalBounds != component->getBounds());
 | 
			
		||||
        isChangingAlpha = (finalAlpha != component->getAlpha());
 | 
			
		||||
 | 
			
		||||
        left    = component->getX();
 | 
			
		||||
        top     = component->getY();
 | 
			
		||||
        right   = component->getRight();
 | 
			
		||||
        bottom  = component->getBottom();
 | 
			
		||||
        alpha   = component->getAlpha();
 | 
			
		||||
 | 
			
		||||
        const double invTotalDistance = 4.0 / (startSpd + endSpd + 2.0);
 | 
			
		||||
        startSpeed = jmax (0.0, startSpd * invTotalDistance);
 | 
			
		||||
        midSpeed = invTotalDistance;
 | 
			
		||||
        endSpeed = jmax (0.0, endSpd * invTotalDistance);
 | 
			
		||||
 | 
			
		||||
        proxy.deleteAndZero();
 | 
			
		||||
 | 
			
		||||
        if (useProxyComponent)
 | 
			
		||||
            proxy = new ProxyComponent (*component);
 | 
			
		||||
 | 
			
		||||
        component->setVisible (! useProxyComponent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool useTimeslice (const int elapsed)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* c = proxy != nullptr ? proxy.getComponent()
 | 
			
		||||
                                       : component.get())
 | 
			
		||||
        {
 | 
			
		||||
            msElapsed += elapsed;
 | 
			
		||||
            double newProgress = msElapsed / (double) msTotal;
 | 
			
		||||
 | 
			
		||||
            if (newProgress >= 0 && newProgress < 1.0)
 | 
			
		||||
            {
 | 
			
		||||
                const WeakReference<AnimationTask> weakRef (this);
 | 
			
		||||
                newProgress = timeToDistance (newProgress);
 | 
			
		||||
                const double delta = (newProgress - lastProgress) / (1.0 - lastProgress);
 | 
			
		||||
                jassert (newProgress >= lastProgress);
 | 
			
		||||
                lastProgress = newProgress;
 | 
			
		||||
 | 
			
		||||
                if (delta < 1.0)
 | 
			
		||||
                {
 | 
			
		||||
                    bool stillBusy = false;
 | 
			
		||||
 | 
			
		||||
                    if (isMoving)
 | 
			
		||||
                    {
 | 
			
		||||
                        left   += (destination.getX()      - left)   * delta;
 | 
			
		||||
                        top    += (destination.getY()      - top)    * delta;
 | 
			
		||||
                        right  += (destination.getRight()  - right)  * delta;
 | 
			
		||||
                        bottom += (destination.getBottom() - bottom) * delta;
 | 
			
		||||
 | 
			
		||||
                        const Rectangle<int> newBounds (roundToInt (left),
 | 
			
		||||
                                                        roundToInt (top),
 | 
			
		||||
                                                        roundToInt (right - left),
 | 
			
		||||
                                                        roundToInt (bottom - top));
 | 
			
		||||
 | 
			
		||||
                        if (newBounds != destination)
 | 
			
		||||
                        {
 | 
			
		||||
                            c->setBounds (newBounds);
 | 
			
		||||
                            stillBusy = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Check whether the animation was cancelled/deleted during
 | 
			
		||||
                    // a callback during the setBounds method
 | 
			
		||||
                    if (weakRef.wasObjectDeleted())
 | 
			
		||||
                        return false;
 | 
			
		||||
 | 
			
		||||
                    if (isChangingAlpha)
 | 
			
		||||
                    {
 | 
			
		||||
                        alpha += (destAlpha - alpha) * delta;
 | 
			
		||||
                        c->setAlpha ((float) alpha);
 | 
			
		||||
                        stillBusy = true;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (stillBusy)
 | 
			
		||||
                        return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        moveToFinalDestination();
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void moveToFinalDestination()
 | 
			
		||||
    {
 | 
			
		||||
        if (component != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            const WeakReference<AnimationTask> weakRef (this);
 | 
			
		||||
            component->setAlpha ((float) destAlpha);
 | 
			
		||||
            component->setBounds (destination);
 | 
			
		||||
 | 
			
		||||
            if (! weakRef.wasObjectDeleted())
 | 
			
		||||
                if (proxy != nullptr)
 | 
			
		||||
                    component->setVisible (destAlpha > 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct ProxyComponent  : public Component
 | 
			
		||||
    {
 | 
			
		||||
        ProxyComponent (Component& c)
 | 
			
		||||
        {
 | 
			
		||||
            setWantsKeyboardFocus (false);
 | 
			
		||||
            setBounds (c.getBounds());
 | 
			
		||||
            setTransform (c.getTransform());
 | 
			
		||||
            setAlpha (c.getAlpha());
 | 
			
		||||
            setInterceptsMouseClicks (false, false);
 | 
			
		||||
 | 
			
		||||
            if (auto* parent = c.getParentComponent())
 | 
			
		||||
                parent->addAndMakeVisible (this);
 | 
			
		||||
            else if (c.isOnDesktop() && c.getPeer() != nullptr)
 | 
			
		||||
                addToDesktop (c.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses);
 | 
			
		||||
            else
 | 
			
		||||
                jassertfalse; // seem to be trying to animate a component that's not visible..
 | 
			
		||||
 | 
			
		||||
            auto scale = (float) Desktop::getInstance().getDisplays().getDisplayForRect (getScreenBounds())->scale
 | 
			
		||||
                           * Component::getApproximateScaleFactorForComponent (&c);
 | 
			
		||||
 | 
			
		||||
            image = c.createComponentSnapshot (c.getLocalBounds(), false, scale);
 | 
			
		||||
 | 
			
		||||
            setVisible (true);
 | 
			
		||||
            toBehind (&c);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void paint (Graphics& g) override
 | 
			
		||||
        {
 | 
			
		||||
            g.setOpacity (1.0f);
 | 
			
		||||
            g.drawImageTransformed (image, AffineTransform::scale ((float) getWidth()  / (float) jmax (1, image.getWidth()),
 | 
			
		||||
                                                                   (float) getHeight() / (float) jmax (1, image.getHeight())), false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
 | 
			
		||||
        {
 | 
			
		||||
            return createIgnoredAccessibilityHandler (*this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Image image;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    Component::SafePointer<Component> proxy;
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> destination;
 | 
			
		||||
    double destAlpha;
 | 
			
		||||
 | 
			
		||||
    int msElapsed, msTotal;
 | 
			
		||||
    double startSpeed, midSpeed, endSpeed, lastProgress;
 | 
			
		||||
    double left, top, right, bottom, alpha;
 | 
			
		||||
    bool isMoving, isChangingAlpha;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    double timeToDistance (const double time) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        return (time < 0.5) ? time * (startSpeed + time * (midSpeed - startSpeed))
 | 
			
		||||
                            : 0.5 * (startSpeed + 0.5 * (midSpeed - startSpeed))
 | 
			
		||||
                                + (time - 0.5) * (midSpeed + (time - 0.5) * (endSpeed - midSpeed));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_WEAK_REFERENCEABLE (AnimationTask)
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimationTask)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ComponentAnimator::ComponentAnimator()  : lastTime (0) {}
 | 
			
		||||
ComponentAnimator::~ComponentAnimator() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ComponentAnimator::AnimationTask* ComponentAnimator::findTaskFor (Component* const component) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = tasks.size(); --i >= 0;)
 | 
			
		||||
        if (component == tasks.getUnchecked(i)->component.get())
 | 
			
		||||
            return tasks.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::animateComponent (Component* const component,
 | 
			
		||||
                                          const Rectangle<int>& finalBounds,
 | 
			
		||||
                                          const float finalAlpha,
 | 
			
		||||
                                          const int millisecondsToSpendMoving,
 | 
			
		||||
                                          const bool useProxyComponent,
 | 
			
		||||
                                          const double startSpeed,
 | 
			
		||||
                                          const double endSpeed)
 | 
			
		||||
{
 | 
			
		||||
    // the speeds must be 0 or greater!
 | 
			
		||||
    jassert (startSpeed >= 0 && endSpeed >= 0);
 | 
			
		||||
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto* at = findTaskFor (component);
 | 
			
		||||
 | 
			
		||||
        if (at == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            at = new AnimationTask (component);
 | 
			
		||||
            tasks.add (at);
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        at->reset (finalBounds, finalAlpha, millisecondsToSpendMoving,
 | 
			
		||||
                   useProxyComponent, startSpeed, endSpeed);
 | 
			
		||||
 | 
			
		||||
        if (! isTimerRunning())
 | 
			
		||||
        {
 | 
			
		||||
            lastTime = Time::getMillisecondCounter();
 | 
			
		||||
            startTimerHz (50);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::fadeOut (Component* component, int millisecondsToTake)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (component->isShowing() && millisecondsToTake > 0)
 | 
			
		||||
            animateComponent (component, component->getBounds(), 0.0f, millisecondsToTake, true, 1.0, 1.0);
 | 
			
		||||
 | 
			
		||||
        component->setVisible (false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::fadeIn (Component* component, int millisecondsToTake)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr && ! (component->isVisible() && component->getAlpha() == 1.0f))
 | 
			
		||||
    {
 | 
			
		||||
        component->setAlpha (0.0f);
 | 
			
		||||
        component->setVisible (true);
 | 
			
		||||
        animateComponent (component, component->getBounds(), 1.0f, millisecondsToTake, false, 1.0, 1.0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFinalPositions)
 | 
			
		||||
{
 | 
			
		||||
    if (tasks.size() > 0)
 | 
			
		||||
    {
 | 
			
		||||
        if (moveComponentsToTheirFinalPositions)
 | 
			
		||||
            for (int i = tasks.size(); --i >= 0;)
 | 
			
		||||
                tasks.getUnchecked(i)->moveToFinalDestination();
 | 
			
		||||
 | 
			
		||||
        tasks.clear();
 | 
			
		||||
        sendChangeMessage();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::cancelAnimation (Component* const component,
 | 
			
		||||
                                         const bool moveComponentToItsFinalPosition)
 | 
			
		||||
{
 | 
			
		||||
    if (auto* at = findTaskFor (component))
 | 
			
		||||
    {
 | 
			
		||||
        if (moveComponentToItsFinalPosition)
 | 
			
		||||
            at->moveToFinalDestination();
 | 
			
		||||
 | 
			
		||||
        tasks.removeObject (at);
 | 
			
		||||
        sendChangeMessage();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> ComponentAnimator::getComponentDestination (Component* const component)
 | 
			
		||||
{
 | 
			
		||||
    jassert (component != nullptr);
 | 
			
		||||
 | 
			
		||||
    if (auto* at = findTaskFor (component))
 | 
			
		||||
        return at->destination;
 | 
			
		||||
 | 
			
		||||
    return component->getBounds();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ComponentAnimator::isAnimating (Component* component) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return findTaskFor (component) != nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ComponentAnimator::isAnimating() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return tasks.size() != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentAnimator::timerCallback()
 | 
			
		||||
{
 | 
			
		||||
    auto timeNow = Time::getMillisecondCounter();
 | 
			
		||||
 | 
			
		||||
    if (lastTime == 0)
 | 
			
		||||
        lastTime = timeNow;
 | 
			
		||||
 | 
			
		||||
    auto elapsed = (int) (timeNow - lastTime);
 | 
			
		||||
 | 
			
		||||
    for (auto* task : Array<AnimationTask*> (tasks.begin(), tasks.size()))
 | 
			
		||||
    {
 | 
			
		||||
        if (tasks.contains (task) && ! task->useTimeslice (elapsed))
 | 
			
		||||
        {
 | 
			
		||||
            tasks.removeObject (task);
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lastTime = timeNow;
 | 
			
		||||
 | 
			
		||||
    if (tasks.size() == 0)
 | 
			
		||||
        stopTimer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,162 +1,162 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Animates a set of components, moving them to a new position and/or fading their
 | 
			
		||||
    alpha levels.
 | 
			
		||||
 | 
			
		||||
    To animate a component, create a ComponentAnimator instance or (preferably) use the
 | 
			
		||||
    global animator object provided by Desktop::getAnimator(), and call its animateComponent()
 | 
			
		||||
    method to commence the movement.
 | 
			
		||||
 | 
			
		||||
    If you're using your own ComponentAnimator instance, you'll need to make sure it isn't
 | 
			
		||||
    deleted before it finishes moving the components, or they'll be abandoned before reaching their
 | 
			
		||||
    destinations.
 | 
			
		||||
 | 
			
		||||
    It's ok to delete components while they're being animated - the animator will detect this
 | 
			
		||||
    and safely stop using them.
 | 
			
		||||
 | 
			
		||||
    The class is a ChangeBroadcaster and sends a notification when any components
 | 
			
		||||
    start or finish being animated.
 | 
			
		||||
 | 
			
		||||
    @see Desktop::getAnimator
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ComponentAnimator  : public ChangeBroadcaster,
 | 
			
		||||
                                     private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a ComponentAnimator. */
 | 
			
		||||
    ComponentAnimator();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ComponentAnimator() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Starts a component moving from its current position to a specified position.
 | 
			
		||||
 | 
			
		||||
        If the component is already in the middle of an animation, that will be abandoned,
 | 
			
		||||
        and a new animation will begin, moving the component from its current location.
 | 
			
		||||
 | 
			
		||||
        The start and end speed parameters let you apply some acceleration to the component's
 | 
			
		||||
        movement.
 | 
			
		||||
 | 
			
		||||
        @param component            the component to move
 | 
			
		||||
        @param finalBounds          the destination bounds to which the component should move. To leave the
 | 
			
		||||
                                    component in the same place, just pass component->getBounds() for this value
 | 
			
		||||
        @param finalAlpha           the alpha value that the component should have at the end of the animation
 | 
			
		||||
        @param animationDurationMilliseconds    how long the animation should last, in milliseconds
 | 
			
		||||
        @param useProxyComponent    if true, this means the component should be replaced by an internally
 | 
			
		||||
                                    managed temporary component which is a snapshot of the original component.
 | 
			
		||||
                                    This avoids the component having to paint itself as it moves, so may
 | 
			
		||||
                                    be more efficient. This option also allows you to delete the original
 | 
			
		||||
                                    component immediately after starting the animation, because the animation
 | 
			
		||||
                                    can proceed without it. If you use a proxy, the original component will be
 | 
			
		||||
                                    made invisible by this call, and then will become visible again at the end
 | 
			
		||||
                                    of the animation. It'll also mean that the proxy component will be temporarily
 | 
			
		||||
                                    added to the component's parent, so avoid it if this might confuse the parent
 | 
			
		||||
                                    component, or if there's a chance the parent might decide to delete its children.
 | 
			
		||||
        @param startSpeed           a value to indicate the relative start speed of the animation. If this is 0,
 | 
			
		||||
                                    the component will start by accelerating from rest; higher values mean that it
 | 
			
		||||
                                    will have an initial speed greater than zero. If the value is greater than 1, it
 | 
			
		||||
                                    will decelerate towards the middle of its journey. To move the component at a
 | 
			
		||||
                                    constant rate for its entire animation, set both the start and end speeds to 1.0
 | 
			
		||||
        @param endSpeed             a relative speed at which the component should be moving when the animation finishes.
 | 
			
		||||
                                    If this is 0, the component will decelerate to a standstill at its final position;
 | 
			
		||||
                                    higher values mean the component will still be moving when it stops. To move the component
 | 
			
		||||
                                    at a constant rate for its entire animation, set both the start and end speeds to 1.0
 | 
			
		||||
    */
 | 
			
		||||
    void animateComponent (Component* component,
 | 
			
		||||
                           const Rectangle<int>& finalBounds,
 | 
			
		||||
                           float finalAlpha,
 | 
			
		||||
                           int animationDurationMilliseconds,
 | 
			
		||||
                           bool useProxyComponent,
 | 
			
		||||
                           double startSpeed,
 | 
			
		||||
                           double endSpeed);
 | 
			
		||||
 | 
			
		||||
    /** Begins a fade-out of this components alpha level.
 | 
			
		||||
        This is a quick way of invoking animateComponent() with a target alpha value of 0.0f, using
 | 
			
		||||
        a proxy. You're safe to delete the component after calling this method, and this won't
 | 
			
		||||
        interfere with the animation's progress.
 | 
			
		||||
    */
 | 
			
		||||
    void fadeOut (Component* component, int millisecondsToTake);
 | 
			
		||||
 | 
			
		||||
    /** Begins a fade-in of a component.
 | 
			
		||||
        This is a quick way of invoking animateComponent() with a target alpha value of 1.0f.
 | 
			
		||||
    */
 | 
			
		||||
    void fadeIn (Component* component, int millisecondsToTake);
 | 
			
		||||
 | 
			
		||||
    /** Stops a component if it's currently being animated.
 | 
			
		||||
 | 
			
		||||
        If moveComponentToItsFinalPosition is true, then the component will
 | 
			
		||||
        be immediately moved to its destination position and size. If false, it will be
 | 
			
		||||
        left in whatever location it currently occupies.
 | 
			
		||||
    */
 | 
			
		||||
    void cancelAnimation (Component* component,
 | 
			
		||||
                          bool moveComponentToItsFinalPosition);
 | 
			
		||||
 | 
			
		||||
    /** Clears all of the active animations.
 | 
			
		||||
 | 
			
		||||
        If moveComponentsToTheirFinalPositions is true, all the components will
 | 
			
		||||
        be immediately set to their final positions. If false, they will be
 | 
			
		||||
        left in whatever locations they currently occupy.
 | 
			
		||||
    */
 | 
			
		||||
    void cancelAllAnimations (bool moveComponentsToTheirFinalPositions);
 | 
			
		||||
 | 
			
		||||
    /** Returns the destination position for a component.
 | 
			
		||||
 | 
			
		||||
        If the component is being animated, this will return the target position that
 | 
			
		||||
        was specified when animateComponent() was called.
 | 
			
		||||
 | 
			
		||||
        If the specified component isn't currently being animated, this method will just
 | 
			
		||||
        return its current position.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<int> getComponentDestination (Component* component);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the specified component is currently being animated. */
 | 
			
		||||
    bool isAnimating (Component* component) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if any components are currently being animated. */
 | 
			
		||||
    bool isAnimating() const noexcept;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class AnimationTask;
 | 
			
		||||
    OwnedArray<AnimationTask> tasks;
 | 
			
		||||
    uint32 lastTime;
 | 
			
		||||
 | 
			
		||||
    AnimationTask* findTaskFor (Component*) const noexcept;
 | 
			
		||||
    void timerCallback() override;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentAnimator)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Animates a set of components, moving them to a new position and/or fading their
 | 
			
		||||
    alpha levels.
 | 
			
		||||
 | 
			
		||||
    To animate a component, create a ComponentAnimator instance or (preferably) use the
 | 
			
		||||
    global animator object provided by Desktop::getAnimator(), and call its animateComponent()
 | 
			
		||||
    method to commence the movement.
 | 
			
		||||
 | 
			
		||||
    If you're using your own ComponentAnimator instance, you'll need to make sure it isn't
 | 
			
		||||
    deleted before it finishes moving the components, or they'll be abandoned before reaching their
 | 
			
		||||
    destinations.
 | 
			
		||||
 | 
			
		||||
    It's ok to delete components while they're being animated - the animator will detect this
 | 
			
		||||
    and safely stop using them.
 | 
			
		||||
 | 
			
		||||
    The class is a ChangeBroadcaster and sends a notification when any components
 | 
			
		||||
    start or finish being animated.
 | 
			
		||||
 | 
			
		||||
    @see Desktop::getAnimator
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ComponentAnimator  : public ChangeBroadcaster,
 | 
			
		||||
                                     private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a ComponentAnimator. */
 | 
			
		||||
    ComponentAnimator();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ComponentAnimator() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Starts a component moving from its current position to a specified position.
 | 
			
		||||
 | 
			
		||||
        If the component is already in the middle of an animation, that will be abandoned,
 | 
			
		||||
        and a new animation will begin, moving the component from its current location.
 | 
			
		||||
 | 
			
		||||
        The start and end speed parameters let you apply some acceleration to the component's
 | 
			
		||||
        movement.
 | 
			
		||||
 | 
			
		||||
        @param component            the component to move
 | 
			
		||||
        @param finalBounds          the destination bounds to which the component should move. To leave the
 | 
			
		||||
                                    component in the same place, just pass component->getBounds() for this value
 | 
			
		||||
        @param finalAlpha           the alpha value that the component should have at the end of the animation
 | 
			
		||||
        @param animationDurationMilliseconds    how long the animation should last, in milliseconds
 | 
			
		||||
        @param useProxyComponent    if true, this means the component should be replaced by an internally
 | 
			
		||||
                                    managed temporary component which is a snapshot of the original component.
 | 
			
		||||
                                    This avoids the component having to paint itself as it moves, so may
 | 
			
		||||
                                    be more efficient. This option also allows you to delete the original
 | 
			
		||||
                                    component immediately after starting the animation, because the animation
 | 
			
		||||
                                    can proceed without it. If you use a proxy, the original component will be
 | 
			
		||||
                                    made invisible by this call, and then will become visible again at the end
 | 
			
		||||
                                    of the animation. It'll also mean that the proxy component will be temporarily
 | 
			
		||||
                                    added to the component's parent, so avoid it if this might confuse the parent
 | 
			
		||||
                                    component, or if there's a chance the parent might decide to delete its children.
 | 
			
		||||
        @param startSpeed           a value to indicate the relative start speed of the animation. If this is 0,
 | 
			
		||||
                                    the component will start by accelerating from rest; higher values mean that it
 | 
			
		||||
                                    will have an initial speed greater than zero. If the value is greater than 1, it
 | 
			
		||||
                                    will decelerate towards the middle of its journey. To move the component at a
 | 
			
		||||
                                    constant rate for its entire animation, set both the start and end speeds to 1.0
 | 
			
		||||
        @param endSpeed             a relative speed at which the component should be moving when the animation finishes.
 | 
			
		||||
                                    If this is 0, the component will decelerate to a standstill at its final position;
 | 
			
		||||
                                    higher values mean the component will still be moving when it stops. To move the component
 | 
			
		||||
                                    at a constant rate for its entire animation, set both the start and end speeds to 1.0
 | 
			
		||||
    */
 | 
			
		||||
    void animateComponent (Component* component,
 | 
			
		||||
                           const Rectangle<int>& finalBounds,
 | 
			
		||||
                           float finalAlpha,
 | 
			
		||||
                           int animationDurationMilliseconds,
 | 
			
		||||
                           bool useProxyComponent,
 | 
			
		||||
                           double startSpeed,
 | 
			
		||||
                           double endSpeed);
 | 
			
		||||
 | 
			
		||||
    /** Begins a fade-out of this components alpha level.
 | 
			
		||||
        This is a quick way of invoking animateComponent() with a target alpha value of 0.0f, using
 | 
			
		||||
        a proxy. You're safe to delete the component after calling this method, and this won't
 | 
			
		||||
        interfere with the animation's progress.
 | 
			
		||||
    */
 | 
			
		||||
    void fadeOut (Component* component, int millisecondsToTake);
 | 
			
		||||
 | 
			
		||||
    /** Begins a fade-in of a component.
 | 
			
		||||
        This is a quick way of invoking animateComponent() with a target alpha value of 1.0f.
 | 
			
		||||
    */
 | 
			
		||||
    void fadeIn (Component* component, int millisecondsToTake);
 | 
			
		||||
 | 
			
		||||
    /** Stops a component if it's currently being animated.
 | 
			
		||||
 | 
			
		||||
        If moveComponentToItsFinalPosition is true, then the component will
 | 
			
		||||
        be immediately moved to its destination position and size. If false, it will be
 | 
			
		||||
        left in whatever location it currently occupies.
 | 
			
		||||
    */
 | 
			
		||||
    void cancelAnimation (Component* component,
 | 
			
		||||
                          bool moveComponentToItsFinalPosition);
 | 
			
		||||
 | 
			
		||||
    /** Clears all of the active animations.
 | 
			
		||||
 | 
			
		||||
        If moveComponentsToTheirFinalPositions is true, all the components will
 | 
			
		||||
        be immediately set to their final positions. If false, they will be
 | 
			
		||||
        left in whatever locations they currently occupy.
 | 
			
		||||
    */
 | 
			
		||||
    void cancelAllAnimations (bool moveComponentsToTheirFinalPositions);
 | 
			
		||||
 | 
			
		||||
    /** Returns the destination position for a component.
 | 
			
		||||
 | 
			
		||||
        If the component is being animated, this will return the target position that
 | 
			
		||||
        was specified when animateComponent() was called.
 | 
			
		||||
 | 
			
		||||
        If the specified component isn't currently being animated, this method will just
 | 
			
		||||
        return its current position.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<int> getComponentDestination (Component* component);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the specified component is currently being animated. */
 | 
			
		||||
    bool isAnimating (Component* component) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if any components are currently being animated. */
 | 
			
		||||
    bool isAnimating() const noexcept;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class AnimationTask;
 | 
			
		||||
    OwnedArray<AnimationTask> tasks;
 | 
			
		||||
    uint32 lastTime;
 | 
			
		||||
 | 
			
		||||
    AnimationTask* findTaskFor (Component*) const noexcept;
 | 
			
		||||
    void timerCallback() override;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentAnimator)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,296 +1,305 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ComponentBoundsConstrainer::ComponentBoundsConstrainer() noexcept {}
 | 
			
		||||
ComponentBoundsConstrainer::~ComponentBoundsConstrainer() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ComponentBoundsConstrainer::setMinimumWidth  (int minimumWidth) noexcept   { minW = minimumWidth; }
 | 
			
		||||
void ComponentBoundsConstrainer::setMaximumWidth  (int maximumWidth) noexcept   { maxW = maximumWidth; }
 | 
			
		||||
void ComponentBoundsConstrainer::setMinimumHeight (int minimumHeight) noexcept  { minH = minimumHeight; }
 | 
			
		||||
void ComponentBoundsConstrainer::setMaximumHeight (int maximumHeight) noexcept  { maxH = maximumHeight; }
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setMinimumSize (int minimumWidth, int minimumHeight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (maxW >= minimumWidth);
 | 
			
		||||
    jassert (maxH >= minimumHeight);
 | 
			
		||||
    jassert (minimumWidth > 0 && minimumHeight > 0);
 | 
			
		||||
 | 
			
		||||
    minW = minimumWidth;
 | 
			
		||||
    minH = minimumHeight;
 | 
			
		||||
 | 
			
		||||
    if (minW > maxW)  maxW = minW;
 | 
			
		||||
    if (minH > maxH)  maxH = minH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setMaximumSize (int maximumWidth, int maximumHeight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (maximumWidth >= minW);
 | 
			
		||||
    jassert (maximumHeight >= minH);
 | 
			
		||||
    jassert (maximumWidth > 0 && maximumHeight > 0);
 | 
			
		||||
 | 
			
		||||
    maxW = jmax (minW, maximumWidth);
 | 
			
		||||
    maxH = jmax (minH, maximumHeight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setSizeLimits (int minimumWidth,
 | 
			
		||||
                                                int minimumHeight,
 | 
			
		||||
                                                int maximumWidth,
 | 
			
		||||
                                                int maximumHeight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (maximumWidth >= minimumWidth);
 | 
			
		||||
    jassert (maximumHeight >= minimumHeight);
 | 
			
		||||
    jassert (maximumWidth > 0 && maximumHeight > 0);
 | 
			
		||||
    jassert (minimumWidth > 0 && minimumHeight > 0);
 | 
			
		||||
 | 
			
		||||
    minW = jmax (0, minimumWidth);
 | 
			
		||||
    minH = jmax (0, minimumHeight);
 | 
			
		||||
    maxW = jmax (minW, maximumWidth);
 | 
			
		||||
    maxH = jmax (minH, maximumHeight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setMinimumOnscreenAmounts (int minimumWhenOffTheTop,
 | 
			
		||||
                                                            int minimumWhenOffTheLeft,
 | 
			
		||||
                                                            int minimumWhenOffTheBottom,
 | 
			
		||||
                                                            int minimumWhenOffTheRight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    minOffTop    = minimumWhenOffTheTop;
 | 
			
		||||
    minOffLeft   = minimumWhenOffTheLeft;
 | 
			
		||||
    minOffBottom = minimumWhenOffTheBottom;
 | 
			
		||||
    minOffRight  = minimumWhenOffTheRight;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setFixedAspectRatio (double widthOverHeight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    aspectRatio = jmax (0.0, widthOverHeight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double ComponentBoundsConstrainer::getFixedAspectRatio() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return aspectRatio;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setBoundsForComponent (Component* component,
 | 
			
		||||
                                                        Rectangle<int> targetBounds,
 | 
			
		||||
                                                        bool isStretchingTop,
 | 
			
		||||
                                                        bool isStretchingLeft,
 | 
			
		||||
                                                        bool isStretchingBottom,
 | 
			
		||||
                                                        bool isStretchingRight)
 | 
			
		||||
{
 | 
			
		||||
    jassert (component != nullptr);
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> limits, bounds (targetBounds);
 | 
			
		||||
    BorderSize<int> border;
 | 
			
		||||
 | 
			
		||||
    if (auto* parent = component->getParentComponent())
 | 
			
		||||
    {
 | 
			
		||||
        limits.setSize (parent->getWidth(), parent->getHeight());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* peer = component->getPeer())
 | 
			
		||||
            border = peer->getFrameSize();
 | 
			
		||||
 | 
			
		||||
        auto screenBounds = Desktop::getInstance().getDisplays().getDisplayForPoint (targetBounds.getCentre())->userArea;
 | 
			
		||||
 | 
			
		||||
        limits = component->getLocalArea (nullptr, screenBounds) + component->getPosition();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    border.addTo (bounds);
 | 
			
		||||
 | 
			
		||||
    checkBounds (bounds,
 | 
			
		||||
                 border.addedTo (component->getBounds()), limits,
 | 
			
		||||
                 isStretchingTop, isStretchingLeft,
 | 
			
		||||
                 isStretchingBottom, isStretchingRight);
 | 
			
		||||
 | 
			
		||||
    border.subtractFrom (bounds);
 | 
			
		||||
 | 
			
		||||
    applyBoundsToComponent (*component, bounds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::checkComponentBounds (Component* component)
 | 
			
		||||
{
 | 
			
		||||
    setBoundsForComponent (component, component->getBounds(),
 | 
			
		||||
                           false, false, false, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::applyBoundsToComponent (Component& component, Rectangle<int> bounds)
 | 
			
		||||
{
 | 
			
		||||
    if (auto* positioner = component.getPositioner())
 | 
			
		||||
        positioner->applyNewBounds (bounds);
 | 
			
		||||
    else
 | 
			
		||||
        component.setBounds (bounds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ComponentBoundsConstrainer::resizeStart()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::resizeEnd()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ComponentBoundsConstrainer::checkBounds (Rectangle<int>& bounds,
 | 
			
		||||
                                              const Rectangle<int>& old,
 | 
			
		||||
                                              const Rectangle<int>& limits,
 | 
			
		||||
                                              bool isStretchingTop,
 | 
			
		||||
                                              bool isStretchingLeft,
 | 
			
		||||
                                              bool isStretchingBottom,
 | 
			
		||||
                                              bool isStretchingRight)
 | 
			
		||||
{
 | 
			
		||||
    if (isStretchingLeft)
 | 
			
		||||
        bounds.setLeft (jlimit (old.getRight() - maxW, old.getRight() - minW, bounds.getX()));
 | 
			
		||||
    else
 | 
			
		||||
        bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
 | 
			
		||||
 | 
			
		||||
    if (isStretchingTop)
 | 
			
		||||
        bounds.setTop (jlimit (old.getBottom() - maxH, old.getBottom() - minH, bounds.getY()));
 | 
			
		||||
    else
 | 
			
		||||
        bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
 | 
			
		||||
 | 
			
		||||
    if (bounds.isEmpty())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (minOffTop > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int limit = limits.getY() + jmin (minOffTop - bounds.getHeight(), 0);
 | 
			
		||||
 | 
			
		||||
        if (bounds.getY() < limit)
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingTop)
 | 
			
		||||
                bounds.setTop (limits.getY());
 | 
			
		||||
            else
 | 
			
		||||
                bounds.setY (limit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (minOffLeft > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int limit = limits.getX() + jmin (minOffLeft - bounds.getWidth(), 0);
 | 
			
		||||
 | 
			
		||||
        if (bounds.getX() < limit)
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingLeft)
 | 
			
		||||
                bounds.setLeft (limits.getX());
 | 
			
		||||
            else
 | 
			
		||||
                bounds.setX (limit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (minOffBottom > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int limit = limits.getBottom() - jmin (minOffBottom, bounds.getHeight());
 | 
			
		||||
 | 
			
		||||
        if (bounds.getY() > limit)
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingBottom)
 | 
			
		||||
                bounds.setBottom (limits.getBottom());
 | 
			
		||||
            else
 | 
			
		||||
                bounds.setY (limit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (minOffRight > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int limit = limits.getRight() - jmin (minOffRight, bounds.getWidth());
 | 
			
		||||
 | 
			
		||||
        if (bounds.getX() > limit)
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingRight)
 | 
			
		||||
                bounds.setRight (limits.getRight());
 | 
			
		||||
            else
 | 
			
		||||
                bounds.setX (limit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // constrain the aspect ratio if one has been specified..
 | 
			
		||||
    if (aspectRatio > 0.0)
 | 
			
		||||
    {
 | 
			
		||||
        bool adjustWidth;
 | 
			
		||||
 | 
			
		||||
        if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
 | 
			
		||||
        {
 | 
			
		||||
            adjustWidth = true;
 | 
			
		||||
        }
 | 
			
		||||
        else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
 | 
			
		||||
        {
 | 
			
		||||
            adjustWidth = false;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            const double oldRatio = (old.getHeight() > 0) ? std::abs (old.getWidth() / (double) old.getHeight()) : 0.0;
 | 
			
		||||
            const double newRatio = std::abs (bounds.getWidth() / (double) bounds.getHeight());
 | 
			
		||||
 | 
			
		||||
            adjustWidth = (oldRatio > newRatio);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (adjustWidth)
 | 
			
		||||
        {
 | 
			
		||||
            bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
 | 
			
		||||
 | 
			
		||||
            if (bounds.getWidth() > maxW || bounds.getWidth() < minW)
 | 
			
		||||
            {
 | 
			
		||||
                bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
 | 
			
		||||
                bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
 | 
			
		||||
 | 
			
		||||
            if (bounds.getHeight() > maxH || bounds.getHeight() < minH)
 | 
			
		||||
            {
 | 
			
		||||
                bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
 | 
			
		||||
                bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
 | 
			
		||||
        {
 | 
			
		||||
            bounds.setX (old.getX() + (old.getWidth() - bounds.getWidth()) / 2);
 | 
			
		||||
        }
 | 
			
		||||
        else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
 | 
			
		||||
        {
 | 
			
		||||
            bounds.setY (old.getY() + (old.getHeight() - bounds.getHeight()) / 2);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingLeft)
 | 
			
		||||
                bounds.setX (old.getRight() - bounds.getWidth());
 | 
			
		||||
 | 
			
		||||
            if (isStretchingTop)
 | 
			
		||||
                bounds.setY (old.getBottom() - bounds.getHeight());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    jassert (! bounds.isEmpty());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ComponentBoundsConstrainer::ComponentBoundsConstrainer() noexcept {}
 | 
			
		||||
ComponentBoundsConstrainer::~ComponentBoundsConstrainer() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ComponentBoundsConstrainer::setMinimumWidth  (int minimumWidth) noexcept   { minW = minimumWidth; }
 | 
			
		||||
void ComponentBoundsConstrainer::setMaximumWidth  (int maximumWidth) noexcept   { maxW = maximumWidth; }
 | 
			
		||||
void ComponentBoundsConstrainer::setMinimumHeight (int minimumHeight) noexcept  { minH = minimumHeight; }
 | 
			
		||||
void ComponentBoundsConstrainer::setMaximumHeight (int maximumHeight) noexcept  { maxH = maximumHeight; }
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setMinimumSize (int minimumWidth, int minimumHeight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (maxW >= minimumWidth);
 | 
			
		||||
    jassert (maxH >= minimumHeight);
 | 
			
		||||
    jassert (minimumWidth > 0 && minimumHeight > 0);
 | 
			
		||||
 | 
			
		||||
    minW = minimumWidth;
 | 
			
		||||
    minH = minimumHeight;
 | 
			
		||||
 | 
			
		||||
    if (minW > maxW)  maxW = minW;
 | 
			
		||||
    if (minH > maxH)  maxH = minH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setMaximumSize (int maximumWidth, int maximumHeight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (maximumWidth >= minW);
 | 
			
		||||
    jassert (maximumHeight >= minH);
 | 
			
		||||
    jassert (maximumWidth > 0 && maximumHeight > 0);
 | 
			
		||||
 | 
			
		||||
    maxW = jmax (minW, maximumWidth);
 | 
			
		||||
    maxH = jmax (minH, maximumHeight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setSizeLimits (int minimumWidth,
 | 
			
		||||
                                                int minimumHeight,
 | 
			
		||||
                                                int maximumWidth,
 | 
			
		||||
                                                int maximumHeight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    jassert (maximumWidth >= minimumWidth);
 | 
			
		||||
    jassert (maximumHeight >= minimumHeight);
 | 
			
		||||
    jassert (maximumWidth > 0 && maximumHeight > 0);
 | 
			
		||||
    jassert (minimumWidth > 0 && minimumHeight > 0);
 | 
			
		||||
 | 
			
		||||
    minW = jmax (0, minimumWidth);
 | 
			
		||||
    minH = jmax (0, minimumHeight);
 | 
			
		||||
    maxW = jmax (minW, maximumWidth);
 | 
			
		||||
    maxH = jmax (minH, maximumHeight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setMinimumOnscreenAmounts (int minimumWhenOffTheTop,
 | 
			
		||||
                                                            int minimumWhenOffTheLeft,
 | 
			
		||||
                                                            int minimumWhenOffTheBottom,
 | 
			
		||||
                                                            int minimumWhenOffTheRight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    minOffTop    = minimumWhenOffTheTop;
 | 
			
		||||
    minOffLeft   = minimumWhenOffTheLeft;
 | 
			
		||||
    minOffBottom = minimumWhenOffTheBottom;
 | 
			
		||||
    minOffRight  = minimumWhenOffTheRight;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setFixedAspectRatio (double widthOverHeight) noexcept
 | 
			
		||||
{
 | 
			
		||||
    aspectRatio = jmax (0.0, widthOverHeight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double ComponentBoundsConstrainer::getFixedAspectRatio() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return aspectRatio;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::setBoundsForComponent (Component* component,
 | 
			
		||||
                                                        Rectangle<int> targetBounds,
 | 
			
		||||
                                                        bool isStretchingTop,
 | 
			
		||||
                                                        bool isStretchingLeft,
 | 
			
		||||
                                                        bool isStretchingBottom,
 | 
			
		||||
                                                        bool isStretchingRight)
 | 
			
		||||
{
 | 
			
		||||
    jassert (component != nullptr);
 | 
			
		||||
 | 
			
		||||
    auto bounds = targetBounds;
 | 
			
		||||
 | 
			
		||||
    auto limits = [&]() -> Rectangle<int>
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* parent = component->getParentComponent())
 | 
			
		||||
            return { parent->getWidth(), parent->getHeight() };
 | 
			
		||||
 | 
			
		||||
        const auto globalBounds = component->localAreaToGlobal (targetBounds - component->getPosition());
 | 
			
		||||
 | 
			
		||||
        if (auto* display = Desktop::getInstance().getDisplays().getDisplayForPoint (globalBounds.getCentre()))
 | 
			
		||||
            return component->getLocalArea (nullptr, display->userArea) + component->getPosition();
 | 
			
		||||
 | 
			
		||||
        const auto max = std::numeric_limits<int>::max();
 | 
			
		||||
        return { max, max };
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    auto border = [&]() -> BorderSize<int>
 | 
			
		||||
    {
 | 
			
		||||
        if (component->getParentComponent() == nullptr)
 | 
			
		||||
            if (auto* peer = component->getPeer())
 | 
			
		||||
                if (const auto frameSize = peer->getFrameSizeIfPresent())
 | 
			
		||||
                    return *frameSize;
 | 
			
		||||
 | 
			
		||||
        return {};
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    border.addTo (bounds);
 | 
			
		||||
 | 
			
		||||
    checkBounds (bounds,
 | 
			
		||||
                 border.addedTo (component->getBounds()), limits,
 | 
			
		||||
                 isStretchingTop, isStretchingLeft,
 | 
			
		||||
                 isStretchingBottom, isStretchingRight);
 | 
			
		||||
 | 
			
		||||
    border.subtractFrom (bounds);
 | 
			
		||||
 | 
			
		||||
    applyBoundsToComponent (*component, bounds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::checkComponentBounds (Component* component)
 | 
			
		||||
{
 | 
			
		||||
    setBoundsForComponent (component, component->getBounds(),
 | 
			
		||||
                           false, false, false, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::applyBoundsToComponent (Component& component, Rectangle<int> bounds)
 | 
			
		||||
{
 | 
			
		||||
    if (auto* positioner = component.getPositioner())
 | 
			
		||||
        positioner->applyNewBounds (bounds);
 | 
			
		||||
    else
 | 
			
		||||
        component.setBounds (bounds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ComponentBoundsConstrainer::resizeStart()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBoundsConstrainer::resizeEnd()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ComponentBoundsConstrainer::checkBounds (Rectangle<int>& bounds,
 | 
			
		||||
                                              const Rectangle<int>& old,
 | 
			
		||||
                                              const Rectangle<int>& limits,
 | 
			
		||||
                                              bool isStretchingTop,
 | 
			
		||||
                                              bool isStretchingLeft,
 | 
			
		||||
                                              bool isStretchingBottom,
 | 
			
		||||
                                              bool isStretchingRight)
 | 
			
		||||
{
 | 
			
		||||
    if (isStretchingLeft)
 | 
			
		||||
        bounds.setLeft (jlimit (old.getRight() - maxW, old.getRight() - minW, bounds.getX()));
 | 
			
		||||
    else
 | 
			
		||||
        bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
 | 
			
		||||
 | 
			
		||||
    if (isStretchingTop)
 | 
			
		||||
        bounds.setTop (jlimit (old.getBottom() - maxH, old.getBottom() - minH, bounds.getY()));
 | 
			
		||||
    else
 | 
			
		||||
        bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
 | 
			
		||||
 | 
			
		||||
    if (bounds.isEmpty())
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (minOffTop > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int limit = limits.getY() + jmin (minOffTop - bounds.getHeight(), 0);
 | 
			
		||||
 | 
			
		||||
        if (bounds.getY() < limit)
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingTop)
 | 
			
		||||
                bounds.setTop (limits.getY());
 | 
			
		||||
            else
 | 
			
		||||
                bounds.setY (limit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (minOffLeft > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int limit = limits.getX() + jmin (minOffLeft - bounds.getWidth(), 0);
 | 
			
		||||
 | 
			
		||||
        if (bounds.getX() < limit)
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingLeft)
 | 
			
		||||
                bounds.setLeft (limits.getX());
 | 
			
		||||
            else
 | 
			
		||||
                bounds.setX (limit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (minOffBottom > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int limit = limits.getBottom() - jmin (minOffBottom, bounds.getHeight());
 | 
			
		||||
 | 
			
		||||
        if (bounds.getY() > limit)
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingBottom)
 | 
			
		||||
                bounds.setBottom (limits.getBottom());
 | 
			
		||||
            else
 | 
			
		||||
                bounds.setY (limit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (minOffRight > 0)
 | 
			
		||||
    {
 | 
			
		||||
        const int limit = limits.getRight() - jmin (minOffRight, bounds.getWidth());
 | 
			
		||||
 | 
			
		||||
        if (bounds.getX() > limit)
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingRight)
 | 
			
		||||
                bounds.setRight (limits.getRight());
 | 
			
		||||
            else
 | 
			
		||||
                bounds.setX (limit);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // constrain the aspect ratio if one has been specified..
 | 
			
		||||
    if (aspectRatio > 0.0)
 | 
			
		||||
    {
 | 
			
		||||
        bool adjustWidth;
 | 
			
		||||
 | 
			
		||||
        if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
 | 
			
		||||
        {
 | 
			
		||||
            adjustWidth = true;
 | 
			
		||||
        }
 | 
			
		||||
        else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
 | 
			
		||||
        {
 | 
			
		||||
            adjustWidth = false;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            const double oldRatio = (old.getHeight() > 0) ? std::abs (old.getWidth() / (double) old.getHeight()) : 0.0;
 | 
			
		||||
            const double newRatio = std::abs (bounds.getWidth() / (double) bounds.getHeight());
 | 
			
		||||
 | 
			
		||||
            adjustWidth = (oldRatio > newRatio);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (adjustWidth)
 | 
			
		||||
        {
 | 
			
		||||
            bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
 | 
			
		||||
 | 
			
		||||
            if (bounds.getWidth() > maxW || bounds.getWidth() < minW)
 | 
			
		||||
            {
 | 
			
		||||
                bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
 | 
			
		||||
                bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
 | 
			
		||||
 | 
			
		||||
            if (bounds.getHeight() > maxH || bounds.getHeight() < minH)
 | 
			
		||||
            {
 | 
			
		||||
                bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
 | 
			
		||||
                bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
 | 
			
		||||
        {
 | 
			
		||||
            bounds.setX (old.getX() + (old.getWidth() - bounds.getWidth()) / 2);
 | 
			
		||||
        }
 | 
			
		||||
        else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
 | 
			
		||||
        {
 | 
			
		||||
            bounds.setY (old.getY() + (old.getHeight() - bounds.getHeight()) / 2);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (isStretchingLeft)
 | 
			
		||||
                bounds.setX (old.getRight() - bounds.getWidth());
 | 
			
		||||
 | 
			
		||||
            if (isStretchingTop)
 | 
			
		||||
                bounds.setY (old.getBottom() - bounds.getHeight());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    jassert (! bounds.isEmpty());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,197 +1,197 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A class that imposes restrictions on a Component's size or position.
 | 
			
		||||
 | 
			
		||||
    This is used by classes such as ResizableCornerComponent,
 | 
			
		||||
    ResizableBorderComponent and ResizableWindow.
 | 
			
		||||
 | 
			
		||||
    The base class can impose some basic size and position limits, but you can
 | 
			
		||||
    also subclass this for custom uses.
 | 
			
		||||
 | 
			
		||||
    @see ResizableCornerComponent, ResizableBorderComponent, ResizableWindow
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ComponentBoundsConstrainer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** When first created, the object will not impose any restrictions on the components. */
 | 
			
		||||
    ComponentBoundsConstrainer() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~ComponentBoundsConstrainer();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Imposes a minimum width limit. */
 | 
			
		||||
    void setMinimumWidth (int minimumWidth) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current minimum width. */
 | 
			
		||||
    int getMinimumWidth() const noexcept                        { return minW; }
 | 
			
		||||
 | 
			
		||||
    /** Imposes a maximum width limit. */
 | 
			
		||||
    void setMaximumWidth (int maximumWidth) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current maximum width. */
 | 
			
		||||
    int getMaximumWidth() const noexcept                        { return maxW; }
 | 
			
		||||
 | 
			
		||||
    /** Imposes a minimum height limit. */
 | 
			
		||||
    void setMinimumHeight (int minimumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current minimum height. */
 | 
			
		||||
    int getMinimumHeight() const noexcept                       { return minH; }
 | 
			
		||||
 | 
			
		||||
    /** Imposes a maximum height limit. */
 | 
			
		||||
    void setMaximumHeight (int maximumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current maximum height. */
 | 
			
		||||
    int getMaximumHeight() const noexcept                       { return maxH; }
 | 
			
		||||
 | 
			
		||||
    /** Imposes a minimum width and height limit. */
 | 
			
		||||
    void setMinimumSize (int minimumWidth,
 | 
			
		||||
                         int minimumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Imposes a maximum width and height limit. */
 | 
			
		||||
    void setMaximumSize (int maximumWidth,
 | 
			
		||||
                         int maximumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Set all the maximum and minimum dimensions. */
 | 
			
		||||
    void setSizeLimits (int minimumWidth,
 | 
			
		||||
                        int minimumHeight,
 | 
			
		||||
                        int maximumWidth,
 | 
			
		||||
                        int maximumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the amount by which the component is allowed to go off-screen.
 | 
			
		||||
 | 
			
		||||
        The values indicate how many pixels must remain on-screen when dragged off
 | 
			
		||||
        one of its parent's edges, so e.g. if minimumWhenOffTheTop is set to 10, then
 | 
			
		||||
        when the component goes off the top of the screen, its y-position will be
 | 
			
		||||
        clipped so that there are always at least 10 pixels on-screen. In other words,
 | 
			
		||||
        the lowest y-position it can take would be (10 - the component's height).
 | 
			
		||||
 | 
			
		||||
        If you pass 0 or less for one of these amounts, the component is allowed
 | 
			
		||||
        to move beyond that edge completely, with no restrictions at all.
 | 
			
		||||
 | 
			
		||||
        If you pass a very large number (i.e. larger that the dimensions of the
 | 
			
		||||
        component itself), then the component won't be allowed to overlap that
 | 
			
		||||
        edge at all. So e.g. setting minimumWhenOffTheLeft to 0xffffff will mean that
 | 
			
		||||
        the component will bump into the left side of the screen and go no further.
 | 
			
		||||
    */
 | 
			
		||||
    void setMinimumOnscreenAmounts (int minimumWhenOffTheTop,
 | 
			
		||||
                                    int minimumWhenOffTheLeft,
 | 
			
		||||
                                    int minimumWhenOffTheBottom,
 | 
			
		||||
                                    int minimumWhenOffTheRight) noexcept;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
 | 
			
		||||
    int getMinimumWhenOffTheTop() const noexcept        { return minOffTop; }
 | 
			
		||||
    /** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
 | 
			
		||||
    int getMinimumWhenOffTheLeft() const noexcept       { return minOffLeft; }
 | 
			
		||||
    /** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
 | 
			
		||||
    int getMinimumWhenOffTheBottom() const noexcept     { return minOffBottom; }
 | 
			
		||||
    /** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
 | 
			
		||||
    int getMinimumWhenOffTheRight() const noexcept      { return minOffRight; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Specifies a width-to-height ratio that the resizer should always maintain.
 | 
			
		||||
 | 
			
		||||
        If the value is 0, no aspect ratio is enforced. If it's non-zero, the width
 | 
			
		||||
        will always be maintained as this multiple of the height.
 | 
			
		||||
 | 
			
		||||
        @see setResizeLimits
 | 
			
		||||
    */
 | 
			
		||||
    void setFixedAspectRatio (double widthOverHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the aspect ratio that was set with setFixedAspectRatio().
 | 
			
		||||
 | 
			
		||||
        If no aspect ratio is being enforced, this will return 0.
 | 
			
		||||
    */
 | 
			
		||||
    double getFixedAspectRatio() const noexcept;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This callback changes the given coordinates to impose whatever the current
 | 
			
		||||
        constraints are set to be.
 | 
			
		||||
 | 
			
		||||
        @param bounds               the target position that should be examined and adjusted
 | 
			
		||||
        @param previousBounds       the component's current size
 | 
			
		||||
        @param limits               the region in which the component can be positioned
 | 
			
		||||
        @param isStretchingTop      whether the top edge of the component is being resized
 | 
			
		||||
        @param isStretchingLeft     whether the left edge of the component is being resized
 | 
			
		||||
        @param isStretchingBottom   whether the bottom edge of the component is being resized
 | 
			
		||||
        @param isStretchingRight    whether the right edge of the component is being resized
 | 
			
		||||
    */
 | 
			
		||||
    virtual void checkBounds (Rectangle<int>& bounds,
 | 
			
		||||
                              const Rectangle<int>& previousBounds,
 | 
			
		||||
                              const Rectangle<int>& limits,
 | 
			
		||||
                              bool isStretchingTop,
 | 
			
		||||
                              bool isStretchingLeft,
 | 
			
		||||
                              bool isStretchingBottom,
 | 
			
		||||
                              bool isStretchingRight);
 | 
			
		||||
 | 
			
		||||
    /** This callback happens when the resizer is about to start dragging. */
 | 
			
		||||
    virtual void resizeStart();
 | 
			
		||||
 | 
			
		||||
    /** This callback happens when the resizer has finished dragging. */
 | 
			
		||||
    virtual void resizeEnd();
 | 
			
		||||
 | 
			
		||||
    /** Checks the given bounds, and then sets the component to the corrected size. */
 | 
			
		||||
    void setBoundsForComponent (Component* component,
 | 
			
		||||
                                Rectangle<int> bounds,
 | 
			
		||||
                                bool isStretchingTop,
 | 
			
		||||
                                bool isStretchingLeft,
 | 
			
		||||
                                bool isStretchingBottom,
 | 
			
		||||
                                bool isStretchingRight);
 | 
			
		||||
 | 
			
		||||
    /** Performs a check on the current size of a component, and moves or resizes
 | 
			
		||||
        it if it fails the constraints.
 | 
			
		||||
    */
 | 
			
		||||
    void checkComponentBounds (Component* component);
 | 
			
		||||
 | 
			
		||||
    /** Called by setBoundsForComponent() to apply a new constrained size to a
 | 
			
		||||
        component.
 | 
			
		||||
 | 
			
		||||
        By default this just calls setBounds(), but is virtual in case it's needed for
 | 
			
		||||
        extremely cunning purposes.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void applyBoundsToComponent (Component&, Rectangle<int> bounds);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    int minW = 0, maxW = 0x3fffffff, minH = 0, maxH = 0x3fffffff;
 | 
			
		||||
    int minOffTop = 0, minOffLeft = 0, minOffBottom = 0, minOffRight = 0;
 | 
			
		||||
    double aspectRatio = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBoundsConstrainer)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A class that imposes restrictions on a Component's size or position.
 | 
			
		||||
 | 
			
		||||
    This is used by classes such as ResizableCornerComponent,
 | 
			
		||||
    ResizableBorderComponent and ResizableWindow.
 | 
			
		||||
 | 
			
		||||
    The base class can impose some basic size and position limits, but you can
 | 
			
		||||
    also subclass this for custom uses.
 | 
			
		||||
 | 
			
		||||
    @see ResizableCornerComponent, ResizableBorderComponent, ResizableWindow
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ComponentBoundsConstrainer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** When first created, the object will not impose any restrictions on the components. */
 | 
			
		||||
    ComponentBoundsConstrainer() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~ComponentBoundsConstrainer();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Imposes a minimum width limit. */
 | 
			
		||||
    void setMinimumWidth (int minimumWidth) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current minimum width. */
 | 
			
		||||
    int getMinimumWidth() const noexcept                        { return minW; }
 | 
			
		||||
 | 
			
		||||
    /** Imposes a maximum width limit. */
 | 
			
		||||
    void setMaximumWidth (int maximumWidth) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current maximum width. */
 | 
			
		||||
    int getMaximumWidth() const noexcept                        { return maxW; }
 | 
			
		||||
 | 
			
		||||
    /** Imposes a minimum height limit. */
 | 
			
		||||
    void setMinimumHeight (int minimumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current minimum height. */
 | 
			
		||||
    int getMinimumHeight() const noexcept                       { return minH; }
 | 
			
		||||
 | 
			
		||||
    /** Imposes a maximum height limit. */
 | 
			
		||||
    void setMaximumHeight (int maximumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current maximum height. */
 | 
			
		||||
    int getMaximumHeight() const noexcept                       { return maxH; }
 | 
			
		||||
 | 
			
		||||
    /** Imposes a minimum width and height limit. */
 | 
			
		||||
    void setMinimumSize (int minimumWidth,
 | 
			
		||||
                         int minimumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Imposes a maximum width and height limit. */
 | 
			
		||||
    void setMaximumSize (int maximumWidth,
 | 
			
		||||
                         int maximumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Set all the maximum and minimum dimensions. */
 | 
			
		||||
    void setSizeLimits (int minimumWidth,
 | 
			
		||||
                        int minimumHeight,
 | 
			
		||||
                        int maximumWidth,
 | 
			
		||||
                        int maximumHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the amount by which the component is allowed to go off-screen.
 | 
			
		||||
 | 
			
		||||
        The values indicate how many pixels must remain on-screen when dragged off
 | 
			
		||||
        one of its parent's edges, so e.g. if minimumWhenOffTheTop is set to 10, then
 | 
			
		||||
        when the component goes off the top of the screen, its y-position will be
 | 
			
		||||
        clipped so that there are always at least 10 pixels on-screen. In other words,
 | 
			
		||||
        the lowest y-position it can take would be (10 - the component's height).
 | 
			
		||||
 | 
			
		||||
        If you pass 0 or less for one of these amounts, the component is allowed
 | 
			
		||||
        to move beyond that edge completely, with no restrictions at all.
 | 
			
		||||
 | 
			
		||||
        If you pass a very large number (i.e. larger that the dimensions of the
 | 
			
		||||
        component itself), then the component won't be allowed to overlap that
 | 
			
		||||
        edge at all. So e.g. setting minimumWhenOffTheLeft to 0xffffff will mean that
 | 
			
		||||
        the component will bump into the left side of the screen and go no further.
 | 
			
		||||
    */
 | 
			
		||||
    void setMinimumOnscreenAmounts (int minimumWhenOffTheTop,
 | 
			
		||||
                                    int minimumWhenOffTheLeft,
 | 
			
		||||
                                    int minimumWhenOffTheBottom,
 | 
			
		||||
                                    int minimumWhenOffTheRight) noexcept;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
 | 
			
		||||
    int getMinimumWhenOffTheTop() const noexcept        { return minOffTop; }
 | 
			
		||||
    /** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
 | 
			
		||||
    int getMinimumWhenOffTheLeft() const noexcept       { return minOffLeft; }
 | 
			
		||||
    /** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
 | 
			
		||||
    int getMinimumWhenOffTheBottom() const noexcept     { return minOffBottom; }
 | 
			
		||||
    /** Returns the minimum distance the bounds can be off-screen. @see setMinimumOnscreenAmounts */
 | 
			
		||||
    int getMinimumWhenOffTheRight() const noexcept      { return minOffRight; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Specifies a width-to-height ratio that the resizer should always maintain.
 | 
			
		||||
 | 
			
		||||
        If the value is 0, no aspect ratio is enforced. If it's non-zero, the width
 | 
			
		||||
        will always be maintained as this multiple of the height.
 | 
			
		||||
 | 
			
		||||
        @see setResizeLimits
 | 
			
		||||
    */
 | 
			
		||||
    void setFixedAspectRatio (double widthOverHeight) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the aspect ratio that was set with setFixedAspectRatio().
 | 
			
		||||
 | 
			
		||||
        If no aspect ratio is being enforced, this will return 0.
 | 
			
		||||
    */
 | 
			
		||||
    double getFixedAspectRatio() const noexcept;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This callback changes the given coordinates to impose whatever the current
 | 
			
		||||
        constraints are set to be.
 | 
			
		||||
 | 
			
		||||
        @param bounds               the target position that should be examined and adjusted
 | 
			
		||||
        @param previousBounds       the component's current size
 | 
			
		||||
        @param limits               the region in which the component can be positioned
 | 
			
		||||
        @param isStretchingTop      whether the top edge of the component is being resized
 | 
			
		||||
        @param isStretchingLeft     whether the left edge of the component is being resized
 | 
			
		||||
        @param isStretchingBottom   whether the bottom edge of the component is being resized
 | 
			
		||||
        @param isStretchingRight    whether the right edge of the component is being resized
 | 
			
		||||
    */
 | 
			
		||||
    virtual void checkBounds (Rectangle<int>& bounds,
 | 
			
		||||
                              const Rectangle<int>& previousBounds,
 | 
			
		||||
                              const Rectangle<int>& limits,
 | 
			
		||||
                              bool isStretchingTop,
 | 
			
		||||
                              bool isStretchingLeft,
 | 
			
		||||
                              bool isStretchingBottom,
 | 
			
		||||
                              bool isStretchingRight);
 | 
			
		||||
 | 
			
		||||
    /** This callback happens when the resizer is about to start dragging. */
 | 
			
		||||
    virtual void resizeStart();
 | 
			
		||||
 | 
			
		||||
    /** This callback happens when the resizer has finished dragging. */
 | 
			
		||||
    virtual void resizeEnd();
 | 
			
		||||
 | 
			
		||||
    /** Checks the given bounds, and then sets the component to the corrected size. */
 | 
			
		||||
    void setBoundsForComponent (Component* component,
 | 
			
		||||
                                Rectangle<int> bounds,
 | 
			
		||||
                                bool isStretchingTop,
 | 
			
		||||
                                bool isStretchingLeft,
 | 
			
		||||
                                bool isStretchingBottom,
 | 
			
		||||
                                bool isStretchingRight);
 | 
			
		||||
 | 
			
		||||
    /** Performs a check on the current size of a component, and moves or resizes
 | 
			
		||||
        it if it fails the constraints.
 | 
			
		||||
    */
 | 
			
		||||
    void checkComponentBounds (Component* component);
 | 
			
		||||
 | 
			
		||||
    /** Called by setBoundsForComponent() to apply a new constrained size to a
 | 
			
		||||
        component.
 | 
			
		||||
 | 
			
		||||
        By default this just calls setBounds(), but is virtual in case it's needed for
 | 
			
		||||
        extremely cunning purposes.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void applyBoundsToComponent (Component&, Rectangle<int> bounds);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    int minW = 0, maxW = 0x3fffffff, minH = 0, maxH = 0x3fffffff;
 | 
			
		||||
    int minOffTop = 0, minOffLeft = 0, minOffBottom = 0, minOffRight = 0;
 | 
			
		||||
    double aspectRatio = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBoundsConstrainer)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,286 +1,286 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace ComponentBuilderHelpers
 | 
			
		||||
{
 | 
			
		||||
    static String getStateId (const ValueTree& state)
 | 
			
		||||
    {
 | 
			
		||||
        return state [ComponentBuilder::idProperty].toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Component* removeComponentWithID (OwnedArray<Component>& components, const String& compId)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (compId.isNotEmpty());
 | 
			
		||||
 | 
			
		||||
        for (int i = components.size(); --i >= 0;)
 | 
			
		||||
        {
 | 
			
		||||
            Component* const c = components.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
            if (c->getComponentID() == compId)
 | 
			
		||||
                return components.removeAndReturn (i);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Component* findComponentWithID (Component& c, const String& compId)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (compId.isNotEmpty());
 | 
			
		||||
        if (c.getComponentID() == compId)
 | 
			
		||||
            return &c;
 | 
			
		||||
 | 
			
		||||
        for (auto* child : c.getChildren())
 | 
			
		||||
            if (auto* found = findComponentWithID (*child, compId))
 | 
			
		||||
                return found;
 | 
			
		||||
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Component* createNewComponent (ComponentBuilder::TypeHandler& type,
 | 
			
		||||
                                          const ValueTree& state, Component* parent)
 | 
			
		||||
    {
 | 
			
		||||
        Component* const c = type.addNewComponentFromState (state, parent);
 | 
			
		||||
        jassert (c != nullptr && c->getParentComponent() == parent);
 | 
			
		||||
        c->setComponentID (getStateId (state));
 | 
			
		||||
        return c;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void updateComponent (ComponentBuilder& builder, const ValueTree& state)
 | 
			
		||||
    {
 | 
			
		||||
        if (Component* topLevelComp = builder.getManagedComponent())
 | 
			
		||||
        {
 | 
			
		||||
            ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state);
 | 
			
		||||
            const String uid (getStateId (state));
 | 
			
		||||
 | 
			
		||||
            if (type == nullptr || uid.isEmpty())
 | 
			
		||||
            {
 | 
			
		||||
                // ..handle the case where a child of the actual state node has changed.
 | 
			
		||||
                if (state.getParent().isValid())
 | 
			
		||||
                    updateComponent (builder, state.getParent());
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Component* const changedComp = findComponentWithID (*topLevelComp, uid))
 | 
			
		||||
                    type->updateComponentFromState (changedComp, state);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
const Identifier ComponentBuilder::idProperty ("id");
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::ComponentBuilder()
 | 
			
		||||
    : imageProvider (nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::ComponentBuilder (const ValueTree& state_)
 | 
			
		||||
    : state (state_), imageProvider (nullptr)
 | 
			
		||||
{
 | 
			
		||||
    state.addListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::~ComponentBuilder()
 | 
			
		||||
{
 | 
			
		||||
    state.removeListener (this);
 | 
			
		||||
 | 
			
		||||
   #if JUCE_DEBUG
 | 
			
		||||
    // Don't delete the managed component!! The builder owns that component, and will delete
 | 
			
		||||
    // it automatically when it gets deleted.
 | 
			
		||||
    jassert (componentRef.get() == component.get());
 | 
			
		||||
   #endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* ComponentBuilder::getManagedComponent()
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        component.reset (createComponent());
 | 
			
		||||
 | 
			
		||||
       #if JUCE_DEBUG
 | 
			
		||||
        componentRef = component.get();
 | 
			
		||||
       #endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return component.get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* ComponentBuilder::createComponent()
 | 
			
		||||
{
 | 
			
		||||
    jassert (types.size() > 0);  // You need to register all the necessary types before you can load a component!
 | 
			
		||||
 | 
			
		||||
    if (TypeHandler* const type = getHandlerForState (state))
 | 
			
		||||
        return ComponentBuilderHelpers::createNewComponent (*type, state, nullptr);
 | 
			
		||||
 | 
			
		||||
    jassertfalse; // trying to create a component from an unknown type of ValueTree
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type)
 | 
			
		||||
{
 | 
			
		||||
    jassert (type != nullptr);
 | 
			
		||||
 | 
			
		||||
    // Don't try to move your types around! Once a type has been added to a builder, the
 | 
			
		||||
    // builder owns it, and you should leave it alone!
 | 
			
		||||
    jassert (type->builder == nullptr);
 | 
			
		||||
 | 
			
		||||
    types.add (type);
 | 
			
		||||
    type->builder = this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::TypeHandler* ComponentBuilder::getHandlerForState (const ValueTree& s) const
 | 
			
		||||
{
 | 
			
		||||
    const Identifier targetType (s.getType());
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < types.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        TypeHandler* const t = types.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (t->type == targetType)
 | 
			
		||||
            return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ComponentBuilder::getNumHandlers() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return types.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return types [index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::registerStandardComponentTypes()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
 | 
			
		||||
{
 | 
			
		||||
    imageProvider = newImageProvider;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::ImageProvider* ComponentBuilder::getImageProvider() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return imageProvider;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&, int)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree, int, int)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreeParentChanged (ValueTree& tree)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ComponentBuilder::TypeHandler::TypeHandler (const Identifier& valueTreeType)
 | 
			
		||||
   : type (valueTreeType), builder (nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::TypeHandler::~TypeHandler()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder* ComponentBuilder::TypeHandler::getBuilder() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    // A type handler needs to be registered with a ComponentBuilder before using it!
 | 
			
		||||
    jassert (builder != nullptr);
 | 
			
		||||
    return builder;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
 | 
			
		||||
{
 | 
			
		||||
    using namespace ComponentBuilderHelpers;
 | 
			
		||||
 | 
			
		||||
    auto numExistingChildComps = parent.getNumChildComponents();
 | 
			
		||||
 | 
			
		||||
    Array<Component*> componentsInOrder;
 | 
			
		||||
    componentsInOrder.ensureStorageAllocated (numExistingChildComps);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        OwnedArray<Component> existingComponents;
 | 
			
		||||
        existingComponents.ensureStorageAllocated (numExistingChildComps);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numExistingChildComps; ++i)
 | 
			
		||||
            existingComponents.add (parent.getChildComponent (i));
 | 
			
		||||
 | 
			
		||||
        auto newNumChildren = children.getNumChildren();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < newNumChildren; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto childState = children.getChild (i);
 | 
			
		||||
            auto* c = removeComponentWithID (existingComponents, getStateId (childState));
 | 
			
		||||
 | 
			
		||||
            if (c == nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                if (auto* type = getHandlerForState (childState))
 | 
			
		||||
                    c = ComponentBuilderHelpers::createNewComponent (*type, childState, &parent);
 | 
			
		||||
                else
 | 
			
		||||
                    jassertfalse;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (c != nullptr)
 | 
			
		||||
                componentsInOrder.add (c);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // (remaining unused items in existingComponents get deleted here as it goes out of scope)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Make sure the z-order is correct..
 | 
			
		||||
    if (componentsInOrder.size() > 0)
 | 
			
		||||
    {
 | 
			
		||||
        componentsInOrder.getLast()->toFront (false);
 | 
			
		||||
 | 
			
		||||
        for (int i = componentsInOrder.size() - 1; --i >= 0;)
 | 
			
		||||
            componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace ComponentBuilderHelpers
 | 
			
		||||
{
 | 
			
		||||
    static String getStateId (const ValueTree& state)
 | 
			
		||||
    {
 | 
			
		||||
        return state [ComponentBuilder::idProperty].toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Component* removeComponentWithID (OwnedArray<Component>& components, const String& compId)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (compId.isNotEmpty());
 | 
			
		||||
 | 
			
		||||
        for (int i = components.size(); --i >= 0;)
 | 
			
		||||
        {
 | 
			
		||||
            Component* const c = components.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
            if (c->getComponentID() == compId)
 | 
			
		||||
                return components.removeAndReturn (i);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Component* findComponentWithID (Component& c, const String& compId)
 | 
			
		||||
    {
 | 
			
		||||
        jassert (compId.isNotEmpty());
 | 
			
		||||
        if (c.getComponentID() == compId)
 | 
			
		||||
            return &c;
 | 
			
		||||
 | 
			
		||||
        for (auto* child : c.getChildren())
 | 
			
		||||
            if (auto* found = findComponentWithID (*child, compId))
 | 
			
		||||
                return found;
 | 
			
		||||
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Component* createNewComponent (ComponentBuilder::TypeHandler& type,
 | 
			
		||||
                                          const ValueTree& state, Component* parent)
 | 
			
		||||
    {
 | 
			
		||||
        Component* const c = type.addNewComponentFromState (state, parent);
 | 
			
		||||
        jassert (c != nullptr && c->getParentComponent() == parent);
 | 
			
		||||
        c->setComponentID (getStateId (state));
 | 
			
		||||
        return c;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void updateComponent (ComponentBuilder& builder, const ValueTree& state)
 | 
			
		||||
    {
 | 
			
		||||
        if (Component* topLevelComp = builder.getManagedComponent())
 | 
			
		||||
        {
 | 
			
		||||
            ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state);
 | 
			
		||||
            const String uid (getStateId (state));
 | 
			
		||||
 | 
			
		||||
            if (type == nullptr || uid.isEmpty())
 | 
			
		||||
            {
 | 
			
		||||
                // ..handle the case where a child of the actual state node has changed.
 | 
			
		||||
                if (state.getParent().isValid())
 | 
			
		||||
                    updateComponent (builder, state.getParent());
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Component* const changedComp = findComponentWithID (*topLevelComp, uid))
 | 
			
		||||
                    type->updateComponentFromState (changedComp, state);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
const Identifier ComponentBuilder::idProperty ("id");
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::ComponentBuilder()
 | 
			
		||||
    : imageProvider (nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::ComponentBuilder (const ValueTree& state_)
 | 
			
		||||
    : state (state_), imageProvider (nullptr)
 | 
			
		||||
{
 | 
			
		||||
    state.addListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::~ComponentBuilder()
 | 
			
		||||
{
 | 
			
		||||
    state.removeListener (this);
 | 
			
		||||
 | 
			
		||||
   #if JUCE_DEBUG
 | 
			
		||||
    // Don't delete the managed component!! The builder owns that component, and will delete
 | 
			
		||||
    // it automatically when it gets deleted.
 | 
			
		||||
    jassert (componentRef.get() == component.get());
 | 
			
		||||
   #endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* ComponentBuilder::getManagedComponent()
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        component.reset (createComponent());
 | 
			
		||||
 | 
			
		||||
       #if JUCE_DEBUG
 | 
			
		||||
        componentRef = component.get();
 | 
			
		||||
       #endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return component.get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* ComponentBuilder::createComponent()
 | 
			
		||||
{
 | 
			
		||||
    jassert (types.size() > 0);  // You need to register all the necessary types before you can load a component!
 | 
			
		||||
 | 
			
		||||
    if (TypeHandler* const type = getHandlerForState (state))
 | 
			
		||||
        return ComponentBuilderHelpers::createNewComponent (*type, state, nullptr);
 | 
			
		||||
 | 
			
		||||
    jassertfalse; // trying to create a component from an unknown type of ValueTree
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type)
 | 
			
		||||
{
 | 
			
		||||
    jassert (type != nullptr);
 | 
			
		||||
 | 
			
		||||
    // Don't try to move your types around! Once a type has been added to a builder, the
 | 
			
		||||
    // builder owns it, and you should leave it alone!
 | 
			
		||||
    jassert (type->builder == nullptr);
 | 
			
		||||
 | 
			
		||||
    types.add (type);
 | 
			
		||||
    type->builder = this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::TypeHandler* ComponentBuilder::getHandlerForState (const ValueTree& s) const
 | 
			
		||||
{
 | 
			
		||||
    const Identifier targetType (s.getType());
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < types.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        TypeHandler* const t = types.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (t->type == targetType)
 | 
			
		||||
            return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ComponentBuilder::getNumHandlers() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return types.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return types [index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::registerStandardComponentTypes()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::setImageProvider (ImageProvider* newImageProvider) noexcept
 | 
			
		||||
{
 | 
			
		||||
    imageProvider = newImageProvider;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::ImageProvider* ComponentBuilder::getImageProvider() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return imageProvider;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreeChildAdded (ValueTree& tree, ValueTree&)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreeChildRemoved (ValueTree& tree, ValueTree&, int)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreeChildOrderChanged (ValueTree& tree, int, int)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::valueTreeParentChanged (ValueTree& tree)
 | 
			
		||||
{
 | 
			
		||||
    ComponentBuilderHelpers::updateComponent (*this, tree);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ComponentBuilder::TypeHandler::TypeHandler (const Identifier& valueTreeType)
 | 
			
		||||
   : type (valueTreeType), builder (nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder::TypeHandler::~TypeHandler()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentBuilder* ComponentBuilder::TypeHandler::getBuilder() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    // A type handler needs to be registered with a ComponentBuilder before using it!
 | 
			
		||||
    jassert (builder != nullptr);
 | 
			
		||||
    return builder;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
 | 
			
		||||
{
 | 
			
		||||
    using namespace ComponentBuilderHelpers;
 | 
			
		||||
 | 
			
		||||
    auto numExistingChildComps = parent.getNumChildComponents();
 | 
			
		||||
 | 
			
		||||
    Array<Component*> componentsInOrder;
 | 
			
		||||
    componentsInOrder.ensureStorageAllocated (numExistingChildComps);
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        OwnedArray<Component> existingComponents;
 | 
			
		||||
        existingComponents.ensureStorageAllocated (numExistingChildComps);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numExistingChildComps; ++i)
 | 
			
		||||
            existingComponents.add (parent.getChildComponent (i));
 | 
			
		||||
 | 
			
		||||
        auto newNumChildren = children.getNumChildren();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < newNumChildren; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto childState = children.getChild (i);
 | 
			
		||||
            auto* c = removeComponentWithID (existingComponents, getStateId (childState));
 | 
			
		||||
 | 
			
		||||
            if (c == nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                if (auto* type = getHandlerForState (childState))
 | 
			
		||||
                    c = ComponentBuilderHelpers::createNewComponent (*type, childState, &parent);
 | 
			
		||||
                else
 | 
			
		||||
                    jassertfalse;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (c != nullptr)
 | 
			
		||||
                componentsInOrder.add (c);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // (remaining unused items in existingComponents get deleted here as it goes out of scope)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Make sure the z-order is correct..
 | 
			
		||||
    if (componentsInOrder.size() > 0)
 | 
			
		||||
    {
 | 
			
		||||
        componentsInOrder.getLast()->toFront (false);
 | 
			
		||||
 | 
			
		||||
        for (int i = componentsInOrder.size() - 1; --i >= 0;)
 | 
			
		||||
            componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,247 +1,247 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Loads and maintains a tree of Components from a ValueTree that represents them.
 | 
			
		||||
 | 
			
		||||
    To allow the state of a tree of components to be saved as a ValueTree and re-loaded,
 | 
			
		||||
    this class lets you register a set of type-handlers for the different components that
 | 
			
		||||
    are involved, and then uses these types to re-create a set of components from its
 | 
			
		||||
    stored state.
 | 
			
		||||
 | 
			
		||||
    Essentially, to use this, you need to create a ComponentBuilder with your ValueTree,
 | 
			
		||||
    then use registerTypeHandler() to give it a set of type handlers that can cope with
 | 
			
		||||
    all the items in your tree. Then you can call getComponent() to build the component.
 | 
			
		||||
    Once you've got the component you can either take it and delete the ComponentBuilder
 | 
			
		||||
    object, or if you keep the ComponentBuilder around, it'll monitor any changes in the
 | 
			
		||||
    ValueTree and automatically update the component to reflect these changes.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ComponentBuilder  : private ValueTree::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Creates a ComponentBuilder that will use the given state.
 | 
			
		||||
        Once you've created your builder, you should use registerTypeHandler() to register some
 | 
			
		||||
        type handlers for it, and then you can call createComponent() or getManagedComponent()
 | 
			
		||||
        to get the actual component.
 | 
			
		||||
    */
 | 
			
		||||
    explicit ComponentBuilder (const ValueTree& state);
 | 
			
		||||
 | 
			
		||||
    /** Creates a builder that doesn't have a state object. */
 | 
			
		||||
    ComponentBuilder();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ComponentBuilder() override;
 | 
			
		||||
 | 
			
		||||
    /** This is the ValueTree data object that the builder is working with. */
 | 
			
		||||
    ValueTree state;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the builder's component (creating it if necessary).
 | 
			
		||||
 | 
			
		||||
        The first time that this method is called, the builder will attempt to create a component
 | 
			
		||||
        from the ValueTree, so you must have registered some suitable type handlers before calling
 | 
			
		||||
        this. If there's a problem and the component can't be created, this method returns nullptr.
 | 
			
		||||
 | 
			
		||||
        The component that is returned is owned by this ComponentBuilder, so you can put it inside
 | 
			
		||||
        your own parent components, but don't delete it! The ComponentBuilder will delete it automatically
 | 
			
		||||
        when the builder is destroyed. If you want to get a component that you can delete yourself,
 | 
			
		||||
        call createComponent() instead.
 | 
			
		||||
 | 
			
		||||
        The ComponentBuilder will update this component if any changes are made to the ValueTree, so if
 | 
			
		||||
        there's a chance that the tree might change, be careful not to keep any pointers to sub-components,
 | 
			
		||||
        as they may be changed or removed.
 | 
			
		||||
    */
 | 
			
		||||
    Component* getManagedComponent();
 | 
			
		||||
 | 
			
		||||
    /** Creates and returns a new instance of the component that the ValueTree represents.
 | 
			
		||||
        The caller is responsible for using and deleting the object that is returned. Unlike
 | 
			
		||||
        getManagedComponent(), the component that is returned will not be updated by the builder.
 | 
			
		||||
    */
 | 
			
		||||
    Component* createComponent();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
        The class is a base class for objects that manage the loading of a type of component
 | 
			
		||||
        from a ValueTree.
 | 
			
		||||
 | 
			
		||||
        To store and re-load a tree of components as a ValueTree, each component type must have
 | 
			
		||||
        a TypeHandler to represent it.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBuilder::registerTypeHandler(), Drawable::registerDrawableTypeHandlers()
 | 
			
		||||
    */
 | 
			
		||||
    class JUCE_API  TypeHandler
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** Creates a TypeHandler.
 | 
			
		||||
            The valueTreeType must be the type name of the ValueTrees that this handler can parse.
 | 
			
		||||
        */
 | 
			
		||||
        explicit TypeHandler (const Identifier& valueTreeType);
 | 
			
		||||
 | 
			
		||||
        /** Destructor. */
 | 
			
		||||
        virtual ~TypeHandler();
 | 
			
		||||
 | 
			
		||||
        /** Returns the type of the ValueTrees that this handler can parse. */
 | 
			
		||||
        const Identifier type;
 | 
			
		||||
 | 
			
		||||
        /** Returns the builder that this type is registered with. */
 | 
			
		||||
        ComponentBuilder* getBuilder() const noexcept;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** This method must create a new component from the given state, add it to the specified
 | 
			
		||||
            parent component (which may be null), and return it.
 | 
			
		||||
 | 
			
		||||
            The ValueTree will have been pre-checked to make sure that its type matches the type
 | 
			
		||||
            that this handler supports.
 | 
			
		||||
 | 
			
		||||
            There's no need to set the new Component's ID to match that of the state - the builder
 | 
			
		||||
            will take care of that itself.
 | 
			
		||||
        */
 | 
			
		||||
        virtual Component* addNewComponentFromState (const ValueTree& state, Component* parent) = 0;
 | 
			
		||||
 | 
			
		||||
        /** This method must update an existing component from a new ValueTree state.
 | 
			
		||||
 | 
			
		||||
            A component that has been created with addNewComponentFromState() may need to be updated
 | 
			
		||||
            if the ValueTree changes, so this method is used to do that. Your implementation must do
 | 
			
		||||
            whatever's necessary to update the component from the new state provided.
 | 
			
		||||
 | 
			
		||||
            The ValueTree will have been pre-checked to make sure that its type matches the type
 | 
			
		||||
            that this handler supports, and the component will have been created by this type's
 | 
			
		||||
            addNewComponentFromState() method.
 | 
			
		||||
        */
 | 
			
		||||
        virtual void updateComponentFromState (Component* component, const ValueTree& state) = 0;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        friend class ComponentBuilder;
 | 
			
		||||
        ComponentBuilder* builder;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypeHandler)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Adds a type handler that the builder can use when trying to load components.
 | 
			
		||||
        @see Drawable::registerDrawableTypeHandlers()
 | 
			
		||||
    */
 | 
			
		||||
    void registerTypeHandler (TypeHandler* type);
 | 
			
		||||
 | 
			
		||||
    /** Tries to find a registered type handler that can load a component from the given ValueTree. */
 | 
			
		||||
    TypeHandler* getHandlerForState (const ValueTree& state) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of registered type handlers.
 | 
			
		||||
        @see getHandler, registerTypeHandler
 | 
			
		||||
    */
 | 
			
		||||
    int getNumHandlers() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the registered type handlers.
 | 
			
		||||
        @see getNumHandlers, registerTypeHandler
 | 
			
		||||
    */
 | 
			
		||||
    TypeHandler* getHandler (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Registers handlers for various standard juce components. */
 | 
			
		||||
    void registerStandardComponentTypes();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This class is used when references to images need to be stored in ValueTrees.
 | 
			
		||||
 | 
			
		||||
        An instance of an ImageProvider provides a mechanism for converting an Image to/from
 | 
			
		||||
        a reference, which may be a file, URL, ID string, or whatever system is appropriate in
 | 
			
		||||
        your app.
 | 
			
		||||
 | 
			
		||||
        When you're loading components from a ValueTree that may need a way of loading images, you
 | 
			
		||||
        should call ComponentBuilder::setImageProvider() to supply a suitable provider before
 | 
			
		||||
        trying to load the component.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBuilder::setImageProvider()
 | 
			
		||||
    */
 | 
			
		||||
    class JUCE_API  ImageProvider
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        ImageProvider() = default;
 | 
			
		||||
        virtual ~ImageProvider() = default;
 | 
			
		||||
 | 
			
		||||
        /** Retrieves the image associated with this identifier, which could be any
 | 
			
		||||
            kind of string, number, filename, etc.
 | 
			
		||||
 | 
			
		||||
            The image that is returned will be owned by the caller, but it may come
 | 
			
		||||
            from the ImageCache.
 | 
			
		||||
        */
 | 
			
		||||
        virtual Image getImageForIdentifier (const var& imageIdentifier) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns an identifier to be used to refer to a given image.
 | 
			
		||||
            This is used when a reference to an image is stored in a ValueTree.
 | 
			
		||||
        */
 | 
			
		||||
        virtual var getIdentifierForImage (const Image& image) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Gives the builder an ImageProvider object that the type handlers can use when
 | 
			
		||||
        loading images from stored references.
 | 
			
		||||
 | 
			
		||||
        The object that is passed in is not owned by the builder, so the caller must delete
 | 
			
		||||
        it when it is no longer needed, but not while the builder may still be using it. To
 | 
			
		||||
        clear the image provider, just call setImageProvider (nullptr).
 | 
			
		||||
    */
 | 
			
		||||
    void setImageProvider (ImageProvider* newImageProvider) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current image provider that this builder is using, or nullptr if none has been set. */
 | 
			
		||||
    ImageProvider* getImageProvider() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Updates the children of a parent component by updating them from the children of
 | 
			
		||||
        a given ValueTree.
 | 
			
		||||
    */
 | 
			
		||||
    void updateChildComponents (Component& parent, const ValueTree& children);
 | 
			
		||||
 | 
			
		||||
    /** An identifier for the property of the ValueTrees that is used to store a unique ID
 | 
			
		||||
        for that component.
 | 
			
		||||
    */
 | 
			
		||||
    static const Identifier idProperty;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OwnedArray<TypeHandler> types;
 | 
			
		||||
    std::unique_ptr<Component> component;
 | 
			
		||||
    ImageProvider* imageProvider;
 | 
			
		||||
   #if JUCE_DEBUG
 | 
			
		||||
    WeakReference<Component> componentRef;
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
 | 
			
		||||
    void valueTreeChildAdded (ValueTree&, ValueTree&) override;
 | 
			
		||||
    void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
 | 
			
		||||
    void valueTreeChildOrderChanged (ValueTree&, int, int) override;
 | 
			
		||||
    void valueTreeParentChanged (ValueTree&) override;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBuilder)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Loads and maintains a tree of Components from a ValueTree that represents them.
 | 
			
		||||
 | 
			
		||||
    To allow the state of a tree of components to be saved as a ValueTree and re-loaded,
 | 
			
		||||
    this class lets you register a set of type-handlers for the different components that
 | 
			
		||||
    are involved, and then uses these types to re-create a set of components from its
 | 
			
		||||
    stored state.
 | 
			
		||||
 | 
			
		||||
    Essentially, to use this, you need to create a ComponentBuilder with your ValueTree,
 | 
			
		||||
    then use registerTypeHandler() to give it a set of type handlers that can cope with
 | 
			
		||||
    all the items in your tree. Then you can call getComponent() to build the component.
 | 
			
		||||
    Once you've got the component you can either take it and delete the ComponentBuilder
 | 
			
		||||
    object, or if you keep the ComponentBuilder around, it'll monitor any changes in the
 | 
			
		||||
    ValueTree and automatically update the component to reflect these changes.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ComponentBuilder  : private ValueTree::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Creates a ComponentBuilder that will use the given state.
 | 
			
		||||
        Once you've created your builder, you should use registerTypeHandler() to register some
 | 
			
		||||
        type handlers for it, and then you can call createComponent() or getManagedComponent()
 | 
			
		||||
        to get the actual component.
 | 
			
		||||
    */
 | 
			
		||||
    explicit ComponentBuilder (const ValueTree& state);
 | 
			
		||||
 | 
			
		||||
    /** Creates a builder that doesn't have a state object. */
 | 
			
		||||
    ComponentBuilder();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ComponentBuilder() override;
 | 
			
		||||
 | 
			
		||||
    /** This is the ValueTree data object that the builder is working with. */
 | 
			
		||||
    ValueTree state;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the builder's component (creating it if necessary).
 | 
			
		||||
 | 
			
		||||
        The first time that this method is called, the builder will attempt to create a component
 | 
			
		||||
        from the ValueTree, so you must have registered some suitable type handlers before calling
 | 
			
		||||
        this. If there's a problem and the component can't be created, this method returns nullptr.
 | 
			
		||||
 | 
			
		||||
        The component that is returned is owned by this ComponentBuilder, so you can put it inside
 | 
			
		||||
        your own parent components, but don't delete it! The ComponentBuilder will delete it automatically
 | 
			
		||||
        when the builder is destroyed. If you want to get a component that you can delete yourself,
 | 
			
		||||
        call createComponent() instead.
 | 
			
		||||
 | 
			
		||||
        The ComponentBuilder will update this component if any changes are made to the ValueTree, so if
 | 
			
		||||
        there's a chance that the tree might change, be careful not to keep any pointers to sub-components,
 | 
			
		||||
        as they may be changed or removed.
 | 
			
		||||
    */
 | 
			
		||||
    Component* getManagedComponent();
 | 
			
		||||
 | 
			
		||||
    /** Creates and returns a new instance of the component that the ValueTree represents.
 | 
			
		||||
        The caller is responsible for using and deleting the object that is returned. Unlike
 | 
			
		||||
        getManagedComponent(), the component that is returned will not be updated by the builder.
 | 
			
		||||
    */
 | 
			
		||||
    Component* createComponent();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
        The class is a base class for objects that manage the loading of a type of component
 | 
			
		||||
        from a ValueTree.
 | 
			
		||||
 | 
			
		||||
        To store and re-load a tree of components as a ValueTree, each component type must have
 | 
			
		||||
        a TypeHandler to represent it.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBuilder::registerTypeHandler(), Drawable::registerDrawableTypeHandlers()
 | 
			
		||||
    */
 | 
			
		||||
    class JUCE_API  TypeHandler
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** Creates a TypeHandler.
 | 
			
		||||
            The valueTreeType must be the type name of the ValueTrees that this handler can parse.
 | 
			
		||||
        */
 | 
			
		||||
        explicit TypeHandler (const Identifier& valueTreeType);
 | 
			
		||||
 | 
			
		||||
        /** Destructor. */
 | 
			
		||||
        virtual ~TypeHandler();
 | 
			
		||||
 | 
			
		||||
        /** Returns the type of the ValueTrees that this handler can parse. */
 | 
			
		||||
        const Identifier type;
 | 
			
		||||
 | 
			
		||||
        /** Returns the builder that this type is registered with. */
 | 
			
		||||
        ComponentBuilder* getBuilder() const noexcept;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** This method must create a new component from the given state, add it to the specified
 | 
			
		||||
            parent component (which may be null), and return it.
 | 
			
		||||
 | 
			
		||||
            The ValueTree will have been pre-checked to make sure that its type matches the type
 | 
			
		||||
            that this handler supports.
 | 
			
		||||
 | 
			
		||||
            There's no need to set the new Component's ID to match that of the state - the builder
 | 
			
		||||
            will take care of that itself.
 | 
			
		||||
        */
 | 
			
		||||
        virtual Component* addNewComponentFromState (const ValueTree& state, Component* parent) = 0;
 | 
			
		||||
 | 
			
		||||
        /** This method must update an existing component from a new ValueTree state.
 | 
			
		||||
 | 
			
		||||
            A component that has been created with addNewComponentFromState() may need to be updated
 | 
			
		||||
            if the ValueTree changes, so this method is used to do that. Your implementation must do
 | 
			
		||||
            whatever's necessary to update the component from the new state provided.
 | 
			
		||||
 | 
			
		||||
            The ValueTree will have been pre-checked to make sure that its type matches the type
 | 
			
		||||
            that this handler supports, and the component will have been created by this type's
 | 
			
		||||
            addNewComponentFromState() method.
 | 
			
		||||
        */
 | 
			
		||||
        virtual void updateComponentFromState (Component* component, const ValueTree& state) = 0;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        friend class ComponentBuilder;
 | 
			
		||||
        ComponentBuilder* builder;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypeHandler)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Adds a type handler that the builder can use when trying to load components.
 | 
			
		||||
        @see Drawable::registerDrawableTypeHandlers()
 | 
			
		||||
    */
 | 
			
		||||
    void registerTypeHandler (TypeHandler* type);
 | 
			
		||||
 | 
			
		||||
    /** Tries to find a registered type handler that can load a component from the given ValueTree. */
 | 
			
		||||
    TypeHandler* getHandlerForState (const ValueTree& state) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of registered type handlers.
 | 
			
		||||
        @see getHandler, registerTypeHandler
 | 
			
		||||
    */
 | 
			
		||||
    int getNumHandlers() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the registered type handlers.
 | 
			
		||||
        @see getNumHandlers, registerTypeHandler
 | 
			
		||||
    */
 | 
			
		||||
    TypeHandler* getHandler (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Registers handlers for various standard juce components. */
 | 
			
		||||
    void registerStandardComponentTypes();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This class is used when references to images need to be stored in ValueTrees.
 | 
			
		||||
 | 
			
		||||
        An instance of an ImageProvider provides a mechanism for converting an Image to/from
 | 
			
		||||
        a reference, which may be a file, URL, ID string, or whatever system is appropriate in
 | 
			
		||||
        your app.
 | 
			
		||||
 | 
			
		||||
        When you're loading components from a ValueTree that may need a way of loading images, you
 | 
			
		||||
        should call ComponentBuilder::setImageProvider() to supply a suitable provider before
 | 
			
		||||
        trying to load the component.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBuilder::setImageProvider()
 | 
			
		||||
    */
 | 
			
		||||
    class JUCE_API  ImageProvider
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        ImageProvider() = default;
 | 
			
		||||
        virtual ~ImageProvider() = default;
 | 
			
		||||
 | 
			
		||||
        /** Retrieves the image associated with this identifier, which could be any
 | 
			
		||||
            kind of string, number, filename, etc.
 | 
			
		||||
 | 
			
		||||
            The image that is returned will be owned by the caller, but it may come
 | 
			
		||||
            from the ImageCache.
 | 
			
		||||
        */
 | 
			
		||||
        virtual Image getImageForIdentifier (const var& imageIdentifier) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns an identifier to be used to refer to a given image.
 | 
			
		||||
            This is used when a reference to an image is stored in a ValueTree.
 | 
			
		||||
        */
 | 
			
		||||
        virtual var getIdentifierForImage (const Image& image) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Gives the builder an ImageProvider object that the type handlers can use when
 | 
			
		||||
        loading images from stored references.
 | 
			
		||||
 | 
			
		||||
        The object that is passed in is not owned by the builder, so the caller must delete
 | 
			
		||||
        it when it is no longer needed, but not while the builder may still be using it. To
 | 
			
		||||
        clear the image provider, just call setImageProvider (nullptr).
 | 
			
		||||
    */
 | 
			
		||||
    void setImageProvider (ImageProvider* newImageProvider) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current image provider that this builder is using, or nullptr if none has been set. */
 | 
			
		||||
    ImageProvider* getImageProvider() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Updates the children of a parent component by updating them from the children of
 | 
			
		||||
        a given ValueTree.
 | 
			
		||||
    */
 | 
			
		||||
    void updateChildComponents (Component& parent, const ValueTree& children);
 | 
			
		||||
 | 
			
		||||
    /** An identifier for the property of the ValueTrees that is used to store a unique ID
 | 
			
		||||
        for that component.
 | 
			
		||||
    */
 | 
			
		||||
    static const Identifier idProperty;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OwnedArray<TypeHandler> types;
 | 
			
		||||
    std::unique_ptr<Component> component;
 | 
			
		||||
    ImageProvider* imageProvider;
 | 
			
		||||
   #if JUCE_DEBUG
 | 
			
		||||
    WeakReference<Component> componentRef;
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
 | 
			
		||||
    void valueTreeChildAdded (ValueTree&, ValueTree&) override;
 | 
			
		||||
    void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
 | 
			
		||||
    void valueTreeChildOrderChanged (ValueTree&, int, int) override;
 | 
			
		||||
    void valueTreeParentChanged (ValueTree&) override;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBuilder)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,142 +1,142 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ComponentMovementWatcher::ComponentMovementWatcher (Component* const comp)
 | 
			
		||||
    : component (comp),
 | 
			
		||||
      wasShowing (comp->isShowing())
 | 
			
		||||
{
 | 
			
		||||
    jassert (component != nullptr); // can't use this with a null pointer..
 | 
			
		||||
 | 
			
		||||
    component->addComponentListener (this);
 | 
			
		||||
    registerWithParentComps();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentMovementWatcher::~ComponentMovementWatcher()
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
        component->removeComponentListener (this);
 | 
			
		||||
 | 
			
		||||
    unregister();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ComponentMovementWatcher::componentParentHierarchyChanged (Component&)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr && ! reentrant)
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedValueSetter<bool> setter (reentrant, true);
 | 
			
		||||
 | 
			
		||||
        auto* peer = component->getPeer();
 | 
			
		||||
        auto peerID = peer != nullptr ? peer->getUniqueID() : 0;
 | 
			
		||||
 | 
			
		||||
        if (peerID != lastPeerID)
 | 
			
		||||
        {
 | 
			
		||||
            componentPeerChanged();
 | 
			
		||||
 | 
			
		||||
            if (component == nullptr)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            lastPeerID = peerID;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unregister();
 | 
			
		||||
        registerWithParentComps();
 | 
			
		||||
 | 
			
		||||
        componentMovedOrResized (*component, true, true);
 | 
			
		||||
 | 
			
		||||
        if (component != nullptr)
 | 
			
		||||
            componentVisibilityChanged (*component);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::componentMovedOrResized (Component&, bool wasMoved, bool wasResized)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (wasMoved)
 | 
			
		||||
        {
 | 
			
		||||
            Point<int> newPos;
 | 
			
		||||
            auto* top = component->getTopLevelComponent();
 | 
			
		||||
 | 
			
		||||
            if (top != component)
 | 
			
		||||
                newPos = top->getLocalPoint (component, Point<int>());
 | 
			
		||||
            else
 | 
			
		||||
                newPos = top->getPosition();
 | 
			
		||||
 | 
			
		||||
            wasMoved = lastBounds.getPosition() != newPos;
 | 
			
		||||
            lastBounds.setPosition (newPos);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wasResized = (lastBounds.getWidth() != component->getWidth() || lastBounds.getHeight() != component->getHeight());
 | 
			
		||||
        lastBounds.setSize (component->getWidth(), component->getHeight());
 | 
			
		||||
 | 
			
		||||
        if (wasMoved || wasResized)
 | 
			
		||||
            componentMovedOrResized (wasMoved, wasResized);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::componentBeingDeleted (Component& comp)
 | 
			
		||||
{
 | 
			
		||||
    registeredParentComps.removeFirstMatchingValue (&comp);
 | 
			
		||||
 | 
			
		||||
    if (component == &comp)
 | 
			
		||||
        unregister();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::componentVisibilityChanged (Component&)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        const bool isShowingNow = component->isShowing();
 | 
			
		||||
 | 
			
		||||
        if (wasShowing != isShowingNow)
 | 
			
		||||
        {
 | 
			
		||||
            wasShowing = isShowingNow;
 | 
			
		||||
            componentVisibilityChanged();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::registerWithParentComps()
 | 
			
		||||
{
 | 
			
		||||
    for (auto* p = component->getParentComponent(); p != nullptr; p = p->getParentComponent())
 | 
			
		||||
    {
 | 
			
		||||
        p->addComponentListener (this);
 | 
			
		||||
        registeredParentComps.add (p);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::unregister()
 | 
			
		||||
{
 | 
			
		||||
    for (auto* c : registeredParentComps)
 | 
			
		||||
        c->removeComponentListener (this);
 | 
			
		||||
 | 
			
		||||
    registeredParentComps.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ComponentMovementWatcher::ComponentMovementWatcher (Component* const comp)
 | 
			
		||||
    : component (comp),
 | 
			
		||||
      wasShowing (comp->isShowing())
 | 
			
		||||
{
 | 
			
		||||
    jassert (component != nullptr); // can't use this with a null pointer..
 | 
			
		||||
 | 
			
		||||
    component->addComponentListener (this);
 | 
			
		||||
    registerWithParentComps();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentMovementWatcher::~ComponentMovementWatcher()
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
        component->removeComponentListener (this);
 | 
			
		||||
 | 
			
		||||
    unregister();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ComponentMovementWatcher::componentParentHierarchyChanged (Component&)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr && ! reentrant)
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedValueSetter<bool> setter (reentrant, true);
 | 
			
		||||
 | 
			
		||||
        auto* peer = component->getPeer();
 | 
			
		||||
        auto peerID = peer != nullptr ? peer->getUniqueID() : 0;
 | 
			
		||||
 | 
			
		||||
        if (peerID != lastPeerID)
 | 
			
		||||
        {
 | 
			
		||||
            componentPeerChanged();
 | 
			
		||||
 | 
			
		||||
            if (component == nullptr)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            lastPeerID = peerID;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unregister();
 | 
			
		||||
        registerWithParentComps();
 | 
			
		||||
 | 
			
		||||
        componentMovedOrResized (*component, true, true);
 | 
			
		||||
 | 
			
		||||
        if (component != nullptr)
 | 
			
		||||
            componentVisibilityChanged (*component);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::componentMovedOrResized (Component&, bool wasMoved, bool wasResized)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (wasMoved)
 | 
			
		||||
        {
 | 
			
		||||
            Point<int> newPos;
 | 
			
		||||
            auto* top = component->getTopLevelComponent();
 | 
			
		||||
 | 
			
		||||
            if (top != component)
 | 
			
		||||
                newPos = top->getLocalPoint (component, Point<int>());
 | 
			
		||||
            else
 | 
			
		||||
                newPos = top->getPosition();
 | 
			
		||||
 | 
			
		||||
            wasMoved = lastBounds.getPosition() != newPos;
 | 
			
		||||
            lastBounds.setPosition (newPos);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        wasResized = (lastBounds.getWidth() != component->getWidth() || lastBounds.getHeight() != component->getHeight());
 | 
			
		||||
        lastBounds.setSize (component->getWidth(), component->getHeight());
 | 
			
		||||
 | 
			
		||||
        if (wasMoved || wasResized)
 | 
			
		||||
            componentMovedOrResized (wasMoved, wasResized);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::componentBeingDeleted (Component& comp)
 | 
			
		||||
{
 | 
			
		||||
    registeredParentComps.removeFirstMatchingValue (&comp);
 | 
			
		||||
 | 
			
		||||
    if (component == &comp)
 | 
			
		||||
        unregister();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::componentVisibilityChanged (Component&)
 | 
			
		||||
{
 | 
			
		||||
    if (component != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        const bool isShowingNow = component->isShowing();
 | 
			
		||||
 | 
			
		||||
        if (wasShowing != isShowingNow)
 | 
			
		||||
        {
 | 
			
		||||
            wasShowing = isShowingNow;
 | 
			
		||||
            componentVisibilityChanged();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::registerWithParentComps()
 | 
			
		||||
{
 | 
			
		||||
    for (auto* p = component->getParentComponent(); p != nullptr; p = p->getParentComponent())
 | 
			
		||||
    {
 | 
			
		||||
        p->addComponentListener (this);
 | 
			
		||||
        registeredParentComps.add (p);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ComponentMovementWatcher::unregister()
 | 
			
		||||
{
 | 
			
		||||
    for (auto* c : registeredParentComps)
 | 
			
		||||
        c->removeComponentListener (this);
 | 
			
		||||
 | 
			
		||||
    registeredParentComps.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,96 +1,96 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** An object that watches for any movement of a component or any of its parent components.
 | 
			
		||||
 | 
			
		||||
    This makes it easy to check when a component is moved relative to its top-level
 | 
			
		||||
    peer window. The normal Component::moved() method is only called when a component
 | 
			
		||||
    moves relative to its immediate parent, and sometimes you want to know if any of
 | 
			
		||||
    components higher up the tree have moved (which of course will affect the overall
 | 
			
		||||
    position of all their sub-components).
 | 
			
		||||
 | 
			
		||||
    It also includes a callback that lets you know when the top-level peer is changed.
 | 
			
		||||
 | 
			
		||||
    This class is used by specialised components like WebBrowserComponent
 | 
			
		||||
    because they need to keep their custom windows in the right place and respond to
 | 
			
		||||
    changes in the peer.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ComponentMovementWatcher    : public ComponentListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a ComponentMovementWatcher to watch a given target component. */
 | 
			
		||||
    ComponentMovementWatcher (Component* componentToWatch);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ComponentMovementWatcher() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This callback happens when the component that is being watched is moved
 | 
			
		||||
        relative to its top-level peer window, or when it is resized. */
 | 
			
		||||
    virtual void componentMovedOrResized (bool wasMoved, bool wasResized) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This callback happens when the component's top-level peer is changed. */
 | 
			
		||||
    virtual void componentPeerChanged() = 0;
 | 
			
		||||
 | 
			
		||||
    /** This callback happens when the component's visibility state changes, possibly due to
 | 
			
		||||
        one of its parents being made visible or invisible.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void componentVisibilityChanged() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that's being watched. */
 | 
			
		||||
    Component* getComponent() const noexcept         { return component.get(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentParentHierarchyChanged (Component&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentBeingDeleted (Component&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentVisibilityChanged (Component&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    uint32 lastPeerID = 0;
 | 
			
		||||
    Array<Component*> registeredParentComps;
 | 
			
		||||
    bool reentrant = false, wasShowing;
 | 
			
		||||
    Rectangle<int> lastBounds;
 | 
			
		||||
 | 
			
		||||
    void unregister();
 | 
			
		||||
    void registerWithParentComps();
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentMovementWatcher)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** An object that watches for any movement of a component or any of its parent components.
 | 
			
		||||
 | 
			
		||||
    This makes it easy to check when a component is moved relative to its top-level
 | 
			
		||||
    peer window. The normal Component::moved() method is only called when a component
 | 
			
		||||
    moves relative to its immediate parent, and sometimes you want to know if any of
 | 
			
		||||
    components higher up the tree have moved (which of course will affect the overall
 | 
			
		||||
    position of all their sub-components).
 | 
			
		||||
 | 
			
		||||
    It also includes a callback that lets you know when the top-level peer is changed.
 | 
			
		||||
 | 
			
		||||
    This class is used by specialised components like WebBrowserComponent
 | 
			
		||||
    because they need to keep their custom windows in the right place and respond to
 | 
			
		||||
    changes in the peer.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ComponentMovementWatcher    : public ComponentListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a ComponentMovementWatcher to watch a given target component. */
 | 
			
		||||
    ComponentMovementWatcher (Component* componentToWatch);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ComponentMovementWatcher() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This callback happens when the component that is being watched is moved
 | 
			
		||||
        relative to its top-level peer window, or when it is resized. */
 | 
			
		||||
    virtual void componentMovedOrResized (bool wasMoved, bool wasResized) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This callback happens when the component's top-level peer is changed. */
 | 
			
		||||
    virtual void componentPeerChanged() = 0;
 | 
			
		||||
 | 
			
		||||
    /** This callback happens when the component's visibility state changes, possibly due to
 | 
			
		||||
        one of its parents being made visible or invisible.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void componentVisibilityChanged() = 0;
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that's being watched. */
 | 
			
		||||
    Component* getComponent() const noexcept         { return component.get(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentParentHierarchyChanged (Component&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentBeingDeleted (Component&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentVisibilityChanged (Component&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    uint32 lastPeerID = 0;
 | 
			
		||||
    Array<Component*> registeredParentComps;
 | 
			
		||||
    bool reentrant = false, wasShowing;
 | 
			
		||||
    Rectangle<int> lastBounds;
 | 
			
		||||
 | 
			
		||||
    void unregister();
 | 
			
		||||
    void registerWithParentComps();
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentMovementWatcher)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,468 +1,468 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
struct ConcertinaPanel::PanelSizes
 | 
			
		||||
{
 | 
			
		||||
    struct Panel
 | 
			
		||||
    {
 | 
			
		||||
        Panel() = default;
 | 
			
		||||
 | 
			
		||||
        Panel (int sz, int mn, int mx) noexcept
 | 
			
		||||
            : size (sz), minSize (mn), maxSize (mx) {}
 | 
			
		||||
 | 
			
		||||
        int setSize (int newSize) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            jassert (minSize <= maxSize);
 | 
			
		||||
            auto oldSize = size;
 | 
			
		||||
            size = jlimit (minSize, maxSize, newSize);
 | 
			
		||||
            return size - oldSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int expand (int amount) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            amount = jmin (amount, maxSize - size);
 | 
			
		||||
            size += amount;
 | 
			
		||||
            return amount;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int reduce (int amount) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            amount = jmin (amount, size - minSize);
 | 
			
		||||
            size -= amount;
 | 
			
		||||
            return amount;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool canExpand() const noexcept     { return size < maxSize; }
 | 
			
		||||
        bool isMinimised() const noexcept   { return size <= minSize; }
 | 
			
		||||
 | 
			
		||||
        int size, minSize, maxSize;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Array<Panel> sizes;
 | 
			
		||||
 | 
			
		||||
    Panel& get (int index) noexcept               { return sizes.getReference (index); }
 | 
			
		||||
    const Panel& get (int index) const noexcept   { return sizes.getReference (index); }
 | 
			
		||||
 | 
			
		||||
    PanelSizes withMovedPanel (int index, int targetPosition, int totalSpace) const
 | 
			
		||||
    {
 | 
			
		||||
        auto num = sizes.size();
 | 
			
		||||
        totalSpace = jmax (totalSpace, getMinimumSize (0, num));
 | 
			
		||||
        targetPosition = jmax (targetPosition, totalSpace - getMaximumSize (index, num));
 | 
			
		||||
 | 
			
		||||
        PanelSizes newSizes (*this);
 | 
			
		||||
        newSizes.stretchRange (0, index, targetPosition - newSizes.getTotalSize (0, index), stretchLast);
 | 
			
		||||
        newSizes.stretchRange (index, num, totalSpace - newSizes.getTotalSize (0, index) - newSizes.getTotalSize (index, num), stretchFirst);
 | 
			
		||||
        return newSizes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PanelSizes fittedInto (int totalSpace) const
 | 
			
		||||
    {
 | 
			
		||||
        auto newSizes (*this);
 | 
			
		||||
        auto num = newSizes.sizes.size();
 | 
			
		||||
        totalSpace = jmax (totalSpace, getMinimumSize (0, num));
 | 
			
		||||
        newSizes.stretchRange (0, num, totalSpace - newSizes.getTotalSize (0, num), stretchAll);
 | 
			
		||||
        return newSizes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PanelSizes withResizedPanel (int index, int panelHeight, int totalSpace) const
 | 
			
		||||
    {
 | 
			
		||||
        PanelSizes newSizes (*this);
 | 
			
		||||
 | 
			
		||||
        if (totalSpace <= 0)
 | 
			
		||||
        {
 | 
			
		||||
            newSizes.get(index).size = panelHeight;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            auto num = sizes.size();
 | 
			
		||||
            auto minSize = getMinimumSize (0, num);
 | 
			
		||||
            totalSpace = jmax (totalSpace, minSize);
 | 
			
		||||
 | 
			
		||||
            newSizes.get(index).setSize (panelHeight);
 | 
			
		||||
            newSizes.stretchRange (0, index,   totalSpace - newSizes.getTotalSize (0, num), stretchLast);
 | 
			
		||||
            newSizes.stretchRange (index, num, totalSpace - newSizes.getTotalSize (0, num), stretchLast);
 | 
			
		||||
            newSizes = newSizes.fittedInto (totalSpace);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return newSizes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    enum ExpandMode
 | 
			
		||||
    {
 | 
			
		||||
        stretchAll,
 | 
			
		||||
        stretchFirst,
 | 
			
		||||
        stretchLast
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void growRangeFirst (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
 | 
			
		||||
            for (int i = start; i < end && spaceDiff > 0; ++i)
 | 
			
		||||
                spaceDiff -= get (i).expand (spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void growRangeLast (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
 | 
			
		||||
            for (int i = end; --i >= start && spaceDiff > 0;)
 | 
			
		||||
                spaceDiff -= get (i).expand (spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void growRangeAll (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        Array<Panel*> expandableItems;
 | 
			
		||||
 | 
			
		||||
        for (int i = start; i < end; ++i)
 | 
			
		||||
            if (get(i).canExpand() && ! get(i).isMinimised())
 | 
			
		||||
                expandableItems.add (& get(i));
 | 
			
		||||
 | 
			
		||||
        for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
 | 
			
		||||
            for (int i = expandableItems.size(); --i >= 0 && spaceDiff > 0;)
 | 
			
		||||
                spaceDiff -= expandableItems.getUnchecked(i)->expand (spaceDiff / (i + 1));
 | 
			
		||||
 | 
			
		||||
        growRangeLast (start, end, spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void shrinkRangeFirst (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = start; i < end && spaceDiff > 0; ++i)
 | 
			
		||||
            spaceDiff -= get(i).reduce (spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void shrinkRangeLast (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = end; --i >= start && spaceDiff > 0;)
 | 
			
		||||
            spaceDiff -= get(i).reduce (spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void stretchRange (int start, int end, int amountToAdd, ExpandMode expandMode) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (end > start)
 | 
			
		||||
        {
 | 
			
		||||
            if (amountToAdd > 0)
 | 
			
		||||
            {
 | 
			
		||||
                if (expandMode == stretchAll)        growRangeAll   (start, end, amountToAdd);
 | 
			
		||||
                else if (expandMode == stretchFirst) growRangeFirst (start, end, amountToAdd);
 | 
			
		||||
                else if (expandMode == stretchLast)  growRangeLast  (start, end, amountToAdd);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (expandMode == stretchFirst)  shrinkRangeFirst (start, end, -amountToAdd);
 | 
			
		||||
                else                             shrinkRangeLast  (start, end, -amountToAdd);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getTotalSize (int start, int end) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        int tot = 0;
 | 
			
		||||
        while (start < end)  tot += get (start++).size;
 | 
			
		||||
        return tot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getMinimumSize (int start, int end) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        int tot = 0;
 | 
			
		||||
        while (start < end)  tot += get (start++).minSize;
 | 
			
		||||
        return tot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getMaximumSize (int start, int end) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        int tot = 0;
 | 
			
		||||
 | 
			
		||||
        while (start < end)
 | 
			
		||||
        {
 | 
			
		||||
            auto mx = get (start++).maxSize;
 | 
			
		||||
 | 
			
		||||
            if (mx > 0x100000)
 | 
			
		||||
                return mx;
 | 
			
		||||
 | 
			
		||||
            tot += mx;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return tot;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ConcertinaPanel::PanelHolder  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    PanelHolder (Component* comp, bool takeOwnership)
 | 
			
		||||
        : component (comp, takeOwnership)
 | 
			
		||||
    {
 | 
			
		||||
        setRepaintsOnMouseActivity (true);
 | 
			
		||||
        setWantsKeyboardFocus (false);
 | 
			
		||||
        addAndMakeVisible (comp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        if (customHeaderComponent == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            const Rectangle<int> area (getWidth(), getHeaderSize());
 | 
			
		||||
            g.reduceClipRegion (area);
 | 
			
		||||
 | 
			
		||||
            getLookAndFeel().drawConcertinaPanelHeader (g, area, isMouseOver(), isMouseButtonDown(),
 | 
			
		||||
                                                        getPanel(), *component);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto bounds = getLocalBounds();
 | 
			
		||||
        auto headerBounds = bounds.removeFromTop (getHeaderSize());
 | 
			
		||||
 | 
			
		||||
        if (customHeaderComponent != nullptr)
 | 
			
		||||
            customHeaderComponent->setBounds (headerBounds);
 | 
			
		||||
 | 
			
		||||
        component->setBounds (bounds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mouseDown (const MouseEvent&) override
 | 
			
		||||
    {
 | 
			
		||||
        mouseDownY = getY();
 | 
			
		||||
        dragStartSizes = getPanel().getFittedSizes();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mouseDrag (const MouseEvent& e) override
 | 
			
		||||
    {
 | 
			
		||||
        if (e.mouseWasDraggedSinceMouseDown())
 | 
			
		||||
        {
 | 
			
		||||
            auto& panel = getPanel();
 | 
			
		||||
            panel.setLayout (dragStartSizes.withMovedPanel (panel.holders.indexOf (this),
 | 
			
		||||
                                                            mouseDownY + e.getDistanceFromDragStartY(),
 | 
			
		||||
                                                            panel.getHeight()), false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mouseDoubleClick (const MouseEvent&) override
 | 
			
		||||
    {
 | 
			
		||||
        getPanel().panelHeaderDoubleClicked (component);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setCustomHeaderComponent (Component* headerComponent, bool shouldTakeOwnership)
 | 
			
		||||
    {
 | 
			
		||||
        customHeaderComponent.set (headerComponent, shouldTakeOwnership);
 | 
			
		||||
 | 
			
		||||
        if (headerComponent != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            addAndMakeVisible (customHeaderComponent);
 | 
			
		||||
            customHeaderComponent->addMouseListener (this, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    OptionalScopedPointer<Component> component;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    PanelSizes dragStartSizes;
 | 
			
		||||
    int mouseDownY;
 | 
			
		||||
    OptionalScopedPointer<Component> customHeaderComponent;
 | 
			
		||||
 | 
			
		||||
    int getHeaderSize() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        ConcertinaPanel& panel = getPanel();
 | 
			
		||||
        auto ourIndex = panel.holders.indexOf (this);
 | 
			
		||||
        return panel.currentSizes->get(ourIndex).minSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ConcertinaPanel& getPanel() const
 | 
			
		||||
    {
 | 
			
		||||
        auto panel = dynamic_cast<ConcertinaPanel*> (getParentComponent());
 | 
			
		||||
        jassert (panel != nullptr);
 | 
			
		||||
        return *panel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PanelHolder)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ConcertinaPanel::ConcertinaPanel()
 | 
			
		||||
    : currentSizes (new PanelSizes()),
 | 
			
		||||
      headerHeight (20)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConcertinaPanel::~ConcertinaPanel() {}
 | 
			
		||||
 | 
			
		||||
int ConcertinaPanel::getNumPanels() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return holders.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* ConcertinaPanel::getPanel (int index) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (PanelHolder* h = holders[index])
 | 
			
		||||
        return h->component;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::addPanel (int insertIndex, Component* component, bool takeOwnership)
 | 
			
		||||
{
 | 
			
		||||
    jassert (component != nullptr); // can't use a null pointer here!
 | 
			
		||||
    jassert (indexOfComp (component) < 0); // You can't add the same component more than once!
 | 
			
		||||
 | 
			
		||||
    auto holder = new PanelHolder (component, takeOwnership);
 | 
			
		||||
    holders.insert (insertIndex, holder);
 | 
			
		||||
    currentSizes->sizes.insert (insertIndex, PanelSizes::Panel (headerHeight, headerHeight, std::numeric_limits<int>::max()));
 | 
			
		||||
    addAndMakeVisible (holder);
 | 
			
		||||
    resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::removePanel (Component* component)
 | 
			
		||||
{
 | 
			
		||||
    auto index = indexOfComp (component);
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        currentSizes->sizes.remove (index);
 | 
			
		||||
        holders.remove (index);
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ConcertinaPanel::setPanelSize (Component* panelComponent, int height, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    auto index = indexOfComp (panelComponent);
 | 
			
		||||
    jassert (index >= 0); // The specified component doesn't seem to have been added!
 | 
			
		||||
 | 
			
		||||
    height += currentSizes->get(index).minSize;
 | 
			
		||||
    auto oldSize = currentSizes->get(index).size;
 | 
			
		||||
    setLayout (currentSizes->withResizedPanel (index, height, getHeight()), animate);
 | 
			
		||||
    return oldSize != currentSizes->get(index).size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ConcertinaPanel::expandPanelFully (Component* component, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    return setPanelSize (component, getHeight(), animate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::setMaximumPanelSize (Component* component, int maximumSize)
 | 
			
		||||
{
 | 
			
		||||
    auto index = indexOfComp (component);
 | 
			
		||||
    jassert (index >= 0); // The specified component doesn't seem to have been added!
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        currentSizes->get(index).maxSize = currentSizes->get(index).minSize + maximumSize;
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::setPanelHeaderSize (Component* component, int headerSize)
 | 
			
		||||
{
 | 
			
		||||
    auto index = indexOfComp (component);
 | 
			
		||||
    jassert (index >= 0); // The specified component doesn't seem to have been added!
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto oldMin = currentSizes->get (index).minSize;
 | 
			
		||||
 | 
			
		||||
        currentSizes->get (index).minSize = headerSize;
 | 
			
		||||
        currentSizes->get (index).size += headerSize - oldMin;
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::setCustomPanelHeader (Component* component, Component* customComponent, bool takeOwnership)
 | 
			
		||||
{
 | 
			
		||||
    OptionalScopedPointer<Component> optional (customComponent, takeOwnership);
 | 
			
		||||
 | 
			
		||||
    auto index = indexOfComp (component);
 | 
			
		||||
    jassert (index >= 0); // The specified component doesn't seem to have been added!
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
        holders.getUnchecked (index)->setCustomHeaderComponent (optional.release(), takeOwnership);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::resized()
 | 
			
		||||
{
 | 
			
		||||
    applyLayout (getFittedSizes(), false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ConcertinaPanel::indexOfComp (Component* comp) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < holders.size(); ++i)
 | 
			
		||||
        if (holders.getUnchecked(i)->component == comp)
 | 
			
		||||
            return i;
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConcertinaPanel::PanelSizes ConcertinaPanel::getFittedSizes() const
 | 
			
		||||
{
 | 
			
		||||
    return currentSizes->fittedInto (getHeight());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::applyLayout (const PanelSizes& sizes, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    if (! animate)
 | 
			
		||||
        animator.cancelAllAnimations (false);
 | 
			
		||||
 | 
			
		||||
    const int animationDuration = 150;
 | 
			
		||||
    auto w = getWidth();
 | 
			
		||||
    int y = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < holders.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        PanelHolder& p = *holders.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
        auto h = sizes.get (i).size;
 | 
			
		||||
        const Rectangle<int> pos (0, y, w, h);
 | 
			
		||||
 | 
			
		||||
        if (animate)
 | 
			
		||||
            animator.animateComponent (&p, pos, 1.0f, animationDuration, false, 1.0, 1.0);
 | 
			
		||||
        else
 | 
			
		||||
            p.setBounds (pos);
 | 
			
		||||
 | 
			
		||||
        y += h;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::setLayout (const PanelSizes& sizes, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    *currentSizes = sizes;
 | 
			
		||||
    applyLayout (getFittedSizes(), animate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::panelHeaderDoubleClicked (Component* component)
 | 
			
		||||
{
 | 
			
		||||
    if (! expandPanelFully (component, true))
 | 
			
		||||
        setPanelSize (component, 0, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> ConcertinaPanel::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
struct ConcertinaPanel::PanelSizes
 | 
			
		||||
{
 | 
			
		||||
    struct Panel
 | 
			
		||||
    {
 | 
			
		||||
        Panel() = default;
 | 
			
		||||
 | 
			
		||||
        Panel (int sz, int mn, int mx) noexcept
 | 
			
		||||
            : size (sz), minSize (mn), maxSize (mx) {}
 | 
			
		||||
 | 
			
		||||
        int setSize (int newSize) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            jassert (minSize <= maxSize);
 | 
			
		||||
            auto oldSize = size;
 | 
			
		||||
            size = jlimit (minSize, maxSize, newSize);
 | 
			
		||||
            return size - oldSize;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int expand (int amount) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            amount = jmin (amount, maxSize - size);
 | 
			
		||||
            size += amount;
 | 
			
		||||
            return amount;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int reduce (int amount) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            amount = jmin (amount, size - minSize);
 | 
			
		||||
            size -= amount;
 | 
			
		||||
            return amount;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool canExpand() const noexcept     { return size < maxSize; }
 | 
			
		||||
        bool isMinimised() const noexcept   { return size <= minSize; }
 | 
			
		||||
 | 
			
		||||
        int size, minSize, maxSize;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Array<Panel> sizes;
 | 
			
		||||
 | 
			
		||||
    Panel& get (int index) noexcept               { return sizes.getReference (index); }
 | 
			
		||||
    const Panel& get (int index) const noexcept   { return sizes.getReference (index); }
 | 
			
		||||
 | 
			
		||||
    PanelSizes withMovedPanel (int index, int targetPosition, int totalSpace) const
 | 
			
		||||
    {
 | 
			
		||||
        auto num = sizes.size();
 | 
			
		||||
        totalSpace = jmax (totalSpace, getMinimumSize (0, num));
 | 
			
		||||
        targetPosition = jmax (targetPosition, totalSpace - getMaximumSize (index, num));
 | 
			
		||||
 | 
			
		||||
        PanelSizes newSizes (*this);
 | 
			
		||||
        newSizes.stretchRange (0, index, targetPosition - newSizes.getTotalSize (0, index), stretchLast);
 | 
			
		||||
        newSizes.stretchRange (index, num, totalSpace - newSizes.getTotalSize (0, index) - newSizes.getTotalSize (index, num), stretchFirst);
 | 
			
		||||
        return newSizes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PanelSizes fittedInto (int totalSpace) const
 | 
			
		||||
    {
 | 
			
		||||
        auto newSizes (*this);
 | 
			
		||||
        auto num = newSizes.sizes.size();
 | 
			
		||||
        totalSpace = jmax (totalSpace, getMinimumSize (0, num));
 | 
			
		||||
        newSizes.stretchRange (0, num, totalSpace - newSizes.getTotalSize (0, num), stretchAll);
 | 
			
		||||
        return newSizes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PanelSizes withResizedPanel (int index, int panelHeight, int totalSpace) const
 | 
			
		||||
    {
 | 
			
		||||
        PanelSizes newSizes (*this);
 | 
			
		||||
 | 
			
		||||
        if (totalSpace <= 0)
 | 
			
		||||
        {
 | 
			
		||||
            newSizes.get(index).size = panelHeight;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            auto num = sizes.size();
 | 
			
		||||
            auto minSize = getMinimumSize (0, num);
 | 
			
		||||
            totalSpace = jmax (totalSpace, minSize);
 | 
			
		||||
 | 
			
		||||
            newSizes.get(index).setSize (panelHeight);
 | 
			
		||||
            newSizes.stretchRange (0, index,   totalSpace - newSizes.getTotalSize (0, num), stretchLast);
 | 
			
		||||
            newSizes.stretchRange (index, num, totalSpace - newSizes.getTotalSize (0, num), stretchLast);
 | 
			
		||||
            newSizes = newSizes.fittedInto (totalSpace);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return newSizes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    enum ExpandMode
 | 
			
		||||
    {
 | 
			
		||||
        stretchAll,
 | 
			
		||||
        stretchFirst,
 | 
			
		||||
        stretchLast
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void growRangeFirst (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
 | 
			
		||||
            for (int i = start; i < end && spaceDiff > 0; ++i)
 | 
			
		||||
                spaceDiff -= get (i).expand (spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void growRangeLast (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
 | 
			
		||||
            for (int i = end; --i >= start && spaceDiff > 0;)
 | 
			
		||||
                spaceDiff -= get (i).expand (spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void growRangeAll (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        Array<Panel*> expandableItems;
 | 
			
		||||
 | 
			
		||||
        for (int i = start; i < end; ++i)
 | 
			
		||||
            if (get(i).canExpand() && ! get(i).isMinimised())
 | 
			
		||||
                expandableItems.add (& get(i));
 | 
			
		||||
 | 
			
		||||
        for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
 | 
			
		||||
            for (int i = expandableItems.size(); --i >= 0 && spaceDiff > 0;)
 | 
			
		||||
                spaceDiff -= expandableItems.getUnchecked(i)->expand (spaceDiff / (i + 1));
 | 
			
		||||
 | 
			
		||||
        growRangeLast (start, end, spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void shrinkRangeFirst (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = start; i < end && spaceDiff > 0; ++i)
 | 
			
		||||
            spaceDiff -= get(i).reduce (spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void shrinkRangeLast (int start, int end, int spaceDiff) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = end; --i >= start && spaceDiff > 0;)
 | 
			
		||||
            spaceDiff -= get(i).reduce (spaceDiff);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void stretchRange (int start, int end, int amountToAdd, ExpandMode expandMode) noexcept
 | 
			
		||||
    {
 | 
			
		||||
        if (end > start)
 | 
			
		||||
        {
 | 
			
		||||
            if (amountToAdd > 0)
 | 
			
		||||
            {
 | 
			
		||||
                if (expandMode == stretchAll)        growRangeAll   (start, end, amountToAdd);
 | 
			
		||||
                else if (expandMode == stretchFirst) growRangeFirst (start, end, amountToAdd);
 | 
			
		||||
                else if (expandMode == stretchLast)  growRangeLast  (start, end, amountToAdd);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (expandMode == stretchFirst)  shrinkRangeFirst (start, end, -amountToAdd);
 | 
			
		||||
                else                             shrinkRangeLast  (start, end, -amountToAdd);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getTotalSize (int start, int end) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        int tot = 0;
 | 
			
		||||
        while (start < end)  tot += get (start++).size;
 | 
			
		||||
        return tot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getMinimumSize (int start, int end) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        int tot = 0;
 | 
			
		||||
        while (start < end)  tot += get (start++).minSize;
 | 
			
		||||
        return tot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getMaximumSize (int start, int end) const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        int tot = 0;
 | 
			
		||||
 | 
			
		||||
        while (start < end)
 | 
			
		||||
        {
 | 
			
		||||
            auto mx = get (start++).maxSize;
 | 
			
		||||
 | 
			
		||||
            if (mx > 0x100000)
 | 
			
		||||
                return mx;
 | 
			
		||||
 | 
			
		||||
            tot += mx;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return tot;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ConcertinaPanel::PanelHolder  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    PanelHolder (Component* comp, bool takeOwnership)
 | 
			
		||||
        : component (comp, takeOwnership)
 | 
			
		||||
    {
 | 
			
		||||
        setRepaintsOnMouseActivity (true);
 | 
			
		||||
        setWantsKeyboardFocus (false);
 | 
			
		||||
        addAndMakeVisible (comp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        if (customHeaderComponent == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            const Rectangle<int> area (getWidth(), getHeaderSize());
 | 
			
		||||
            g.reduceClipRegion (area);
 | 
			
		||||
 | 
			
		||||
            getLookAndFeel().drawConcertinaPanelHeader (g, area, isMouseOver(), isMouseButtonDown(),
 | 
			
		||||
                                                        getPanel(), *component);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto bounds = getLocalBounds();
 | 
			
		||||
        auto headerBounds = bounds.removeFromTop (getHeaderSize());
 | 
			
		||||
 | 
			
		||||
        if (customHeaderComponent != nullptr)
 | 
			
		||||
            customHeaderComponent->setBounds (headerBounds);
 | 
			
		||||
 | 
			
		||||
        component->setBounds (bounds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mouseDown (const MouseEvent&) override
 | 
			
		||||
    {
 | 
			
		||||
        mouseDownY = getY();
 | 
			
		||||
        dragStartSizes = getPanel().getFittedSizes();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mouseDrag (const MouseEvent& e) override
 | 
			
		||||
    {
 | 
			
		||||
        if (e.mouseWasDraggedSinceMouseDown())
 | 
			
		||||
        {
 | 
			
		||||
            auto& panel = getPanel();
 | 
			
		||||
            panel.setLayout (dragStartSizes.withMovedPanel (panel.holders.indexOf (this),
 | 
			
		||||
                                                            mouseDownY + e.getDistanceFromDragStartY(),
 | 
			
		||||
                                                            panel.getHeight()), false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void mouseDoubleClick (const MouseEvent&) override
 | 
			
		||||
    {
 | 
			
		||||
        getPanel().panelHeaderDoubleClicked (component);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setCustomHeaderComponent (Component* headerComponent, bool shouldTakeOwnership)
 | 
			
		||||
    {
 | 
			
		||||
        customHeaderComponent.set (headerComponent, shouldTakeOwnership);
 | 
			
		||||
 | 
			
		||||
        if (headerComponent != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            addAndMakeVisible (customHeaderComponent);
 | 
			
		||||
            customHeaderComponent->addMouseListener (this, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    OptionalScopedPointer<Component> component;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    PanelSizes dragStartSizes;
 | 
			
		||||
    int mouseDownY;
 | 
			
		||||
    OptionalScopedPointer<Component> customHeaderComponent;
 | 
			
		||||
 | 
			
		||||
    int getHeaderSize() const noexcept
 | 
			
		||||
    {
 | 
			
		||||
        ConcertinaPanel& panel = getPanel();
 | 
			
		||||
        auto ourIndex = panel.holders.indexOf (this);
 | 
			
		||||
        return panel.currentSizes->get(ourIndex).minSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ConcertinaPanel& getPanel() const
 | 
			
		||||
    {
 | 
			
		||||
        auto panel = dynamic_cast<ConcertinaPanel*> (getParentComponent());
 | 
			
		||||
        jassert (panel != nullptr);
 | 
			
		||||
        return *panel;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PanelHolder)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ConcertinaPanel::ConcertinaPanel()
 | 
			
		||||
    : currentSizes (new PanelSizes()),
 | 
			
		||||
      headerHeight (20)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConcertinaPanel::~ConcertinaPanel() {}
 | 
			
		||||
 | 
			
		||||
int ConcertinaPanel::getNumPanels() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return holders.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* ConcertinaPanel::getPanel (int index) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (PanelHolder* h = holders[index])
 | 
			
		||||
        return h->component;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::addPanel (int insertIndex, Component* component, bool takeOwnership)
 | 
			
		||||
{
 | 
			
		||||
    jassert (component != nullptr); // can't use a null pointer here!
 | 
			
		||||
    jassert (indexOfComp (component) < 0); // You can't add the same component more than once!
 | 
			
		||||
 | 
			
		||||
    auto holder = new PanelHolder (component, takeOwnership);
 | 
			
		||||
    holders.insert (insertIndex, holder);
 | 
			
		||||
    currentSizes->sizes.insert (insertIndex, PanelSizes::Panel (headerHeight, headerHeight, std::numeric_limits<int>::max()));
 | 
			
		||||
    addAndMakeVisible (holder);
 | 
			
		||||
    resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::removePanel (Component* component)
 | 
			
		||||
{
 | 
			
		||||
    auto index = indexOfComp (component);
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        currentSizes->sizes.remove (index);
 | 
			
		||||
        holders.remove (index);
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ConcertinaPanel::setPanelSize (Component* panelComponent, int height, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    auto index = indexOfComp (panelComponent);
 | 
			
		||||
    jassert (index >= 0); // The specified component doesn't seem to have been added!
 | 
			
		||||
 | 
			
		||||
    height += currentSizes->get(index).minSize;
 | 
			
		||||
    auto oldSize = currentSizes->get(index).size;
 | 
			
		||||
    setLayout (currentSizes->withResizedPanel (index, height, getHeight()), animate);
 | 
			
		||||
    return oldSize != currentSizes->get(index).size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ConcertinaPanel::expandPanelFully (Component* component, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    return setPanelSize (component, getHeight(), animate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::setMaximumPanelSize (Component* component, int maximumSize)
 | 
			
		||||
{
 | 
			
		||||
    auto index = indexOfComp (component);
 | 
			
		||||
    jassert (index >= 0); // The specified component doesn't seem to have been added!
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        currentSizes->get(index).maxSize = currentSizes->get(index).minSize + maximumSize;
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::setPanelHeaderSize (Component* component, int headerSize)
 | 
			
		||||
{
 | 
			
		||||
    auto index = indexOfComp (component);
 | 
			
		||||
    jassert (index >= 0); // The specified component doesn't seem to have been added!
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto oldMin = currentSizes->get (index).minSize;
 | 
			
		||||
 | 
			
		||||
        currentSizes->get (index).minSize = headerSize;
 | 
			
		||||
        currentSizes->get (index).size += headerSize - oldMin;
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::setCustomPanelHeader (Component* component, Component* customComponent, bool takeOwnership)
 | 
			
		||||
{
 | 
			
		||||
    OptionalScopedPointer<Component> optional (customComponent, takeOwnership);
 | 
			
		||||
 | 
			
		||||
    auto index = indexOfComp (component);
 | 
			
		||||
    jassert (index >= 0); // The specified component doesn't seem to have been added!
 | 
			
		||||
 | 
			
		||||
    if (index >= 0)
 | 
			
		||||
        holders.getUnchecked (index)->setCustomHeaderComponent (optional.release(), takeOwnership);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::resized()
 | 
			
		||||
{
 | 
			
		||||
    applyLayout (getFittedSizes(), false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ConcertinaPanel::indexOfComp (Component* comp) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < holders.size(); ++i)
 | 
			
		||||
        if (holders.getUnchecked(i)->component == comp)
 | 
			
		||||
            return i;
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConcertinaPanel::PanelSizes ConcertinaPanel::getFittedSizes() const
 | 
			
		||||
{
 | 
			
		||||
    return currentSizes->fittedInto (getHeight());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::applyLayout (const PanelSizes& sizes, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    if (! animate)
 | 
			
		||||
        animator.cancelAllAnimations (false);
 | 
			
		||||
 | 
			
		||||
    const int animationDuration = 150;
 | 
			
		||||
    auto w = getWidth();
 | 
			
		||||
    int y = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < holders.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        PanelHolder& p = *holders.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
        auto h = sizes.get (i).size;
 | 
			
		||||
        const Rectangle<int> pos (0, y, w, h);
 | 
			
		||||
 | 
			
		||||
        if (animate)
 | 
			
		||||
            animator.animateComponent (&p, pos, 1.0f, animationDuration, false, 1.0, 1.0);
 | 
			
		||||
        else
 | 
			
		||||
            p.setBounds (pos);
 | 
			
		||||
 | 
			
		||||
        y += h;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::setLayout (const PanelSizes& sizes, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    *currentSizes = sizes;
 | 
			
		||||
    applyLayout (getFittedSizes(), animate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConcertinaPanel::panelHeaderDoubleClicked (Component* component)
 | 
			
		||||
{
 | 
			
		||||
    if (! expandPanelFully (component, true))
 | 
			
		||||
        setPanelSize (component, 0, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> ConcertinaPanel::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,142 +1,142 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A panel which holds a vertical stack of components which can be expanded
 | 
			
		||||
    and contracted.
 | 
			
		||||
 | 
			
		||||
    Each section has its own header bar which can be dragged up and down
 | 
			
		||||
    to resize it, or double-clicked to fully expand that section.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ConcertinaPanel   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Creates an empty concertina panel.
 | 
			
		||||
        You can call addPanel() to add some components to it.
 | 
			
		||||
    */
 | 
			
		||||
    ConcertinaPanel();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ConcertinaPanel() override;
 | 
			
		||||
 | 
			
		||||
    /** Adds a component to the panel.
 | 
			
		||||
        @param insertIndex          the index at which this component will be inserted, or
 | 
			
		||||
                                    -1 to append it to the end of the list.
 | 
			
		||||
        @param component            the component that will be shown
 | 
			
		||||
        @param takeOwnership        if true, then the ConcertinaPanel will take ownership
 | 
			
		||||
                                    of the content component, and will delete it later when
 | 
			
		||||
                                    it's no longer needed. If false, it won't delete it, and
 | 
			
		||||
                                    you must make sure it doesn't get deleted while in use.
 | 
			
		||||
    */
 | 
			
		||||
    void addPanel (int insertIndex, Component* component, bool takeOwnership);
 | 
			
		||||
 | 
			
		||||
    /** Removes one of the panels.
 | 
			
		||||
        If the takeOwnership flag was set when the panel was added, then
 | 
			
		||||
        this will also delete the component.
 | 
			
		||||
    */
 | 
			
		||||
    void removePanel (Component* panelComponent);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of panels.
 | 
			
		||||
        @see getPanel
 | 
			
		||||
    */
 | 
			
		||||
    int getNumPanels() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the panels.
 | 
			
		||||
        @see getNumPanels()
 | 
			
		||||
    */
 | 
			
		||||
    Component* getPanel (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resizes one of the panels.
 | 
			
		||||
        The panelComponent must point to  a valid panel component.
 | 
			
		||||
        If animate is true, the panels will be animated into their new positions;
 | 
			
		||||
        if false, they will just be immediately resized.
 | 
			
		||||
    */
 | 
			
		||||
    bool setPanelSize (Component* panelComponent, int newHeight, bool animate);
 | 
			
		||||
 | 
			
		||||
    /** Attempts to make one of the panels full-height.
 | 
			
		||||
        The panelComponent must point to  a valid panel component.
 | 
			
		||||
        If this component has had a maximum size set, then it will be
 | 
			
		||||
        expanded to that size. Otherwise, it'll fill as much of the total
 | 
			
		||||
        space as possible.
 | 
			
		||||
    */
 | 
			
		||||
    bool expandPanelFully (Component* panelComponent, bool animate);
 | 
			
		||||
 | 
			
		||||
    /** Sets a maximum size for one of the panels. */
 | 
			
		||||
    void setMaximumPanelSize (Component* panelComponent, int maximumSize);
 | 
			
		||||
 | 
			
		||||
    /** Sets the height of the header section for one of the panels. */
 | 
			
		||||
    void setPanelHeaderSize (Component* panelComponent, int headerSize);
 | 
			
		||||
 | 
			
		||||
    /** Sets a custom header Component for one of the panels.
 | 
			
		||||
 | 
			
		||||
        @param panelComponent           the panel component to add the custom header to.
 | 
			
		||||
        @param customHeaderComponent    the custom component to use for the panel header.
 | 
			
		||||
                                        This can be nullptr to clear the custom header component
 | 
			
		||||
                                        and just use the standard LookAndFeel panel.
 | 
			
		||||
        @param takeOwnership            if true, then the PanelHolder will take ownership
 | 
			
		||||
                                        of the custom header component, and will delete it later when
 | 
			
		||||
                                        it's no longer needed. If false, it won't delete it, and
 | 
			
		||||
                                        you must make sure it doesn't get deleted while in use.
 | 
			
		||||
     */
 | 
			
		||||
    void setCustomPanelHeader (Component* panelComponent, Component* customHeaderComponent, bool takeOwnership);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes. */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual void drawConcertinaPanelHeader (Graphics&, const Rectangle<int>& area,
 | 
			
		||||
                                                bool isMouseOver, bool isMouseDown,
 | 
			
		||||
                                                ConcertinaPanel&, Component&) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void resized() override;
 | 
			
		||||
 | 
			
		||||
    class PanelHolder;
 | 
			
		||||
    struct PanelSizes;
 | 
			
		||||
    std::unique_ptr<PanelSizes> currentSizes;
 | 
			
		||||
    OwnedArray<PanelHolder> holders;
 | 
			
		||||
    ComponentAnimator animator;
 | 
			
		||||
    int headerHeight;
 | 
			
		||||
 | 
			
		||||
    int indexOfComp (Component*) const noexcept;
 | 
			
		||||
    PanelSizes getFittedSizes() const;
 | 
			
		||||
    void applyLayout (const PanelSizes&, bool animate);
 | 
			
		||||
    void setLayout (const PanelSizes&, bool animate);
 | 
			
		||||
    void panelHeaderDoubleClicked (Component*);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaPanel)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A panel which holds a vertical stack of components which can be expanded
 | 
			
		||||
    and contracted.
 | 
			
		||||
 | 
			
		||||
    Each section has its own header bar which can be dragged up and down
 | 
			
		||||
    to resize it, or double-clicked to fully expand that section.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ConcertinaPanel   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Creates an empty concertina panel.
 | 
			
		||||
        You can call addPanel() to add some components to it.
 | 
			
		||||
    */
 | 
			
		||||
    ConcertinaPanel();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ConcertinaPanel() override;
 | 
			
		||||
 | 
			
		||||
    /** Adds a component to the panel.
 | 
			
		||||
        @param insertIndex          the index at which this component will be inserted, or
 | 
			
		||||
                                    -1 to append it to the end of the list.
 | 
			
		||||
        @param component            the component that will be shown
 | 
			
		||||
        @param takeOwnership        if true, then the ConcertinaPanel will take ownership
 | 
			
		||||
                                    of the content component, and will delete it later when
 | 
			
		||||
                                    it's no longer needed. If false, it won't delete it, and
 | 
			
		||||
                                    you must make sure it doesn't get deleted while in use.
 | 
			
		||||
    */
 | 
			
		||||
    void addPanel (int insertIndex, Component* component, bool takeOwnership);
 | 
			
		||||
 | 
			
		||||
    /** Removes one of the panels.
 | 
			
		||||
        If the takeOwnership flag was set when the panel was added, then
 | 
			
		||||
        this will also delete the component.
 | 
			
		||||
    */
 | 
			
		||||
    void removePanel (Component* panelComponent);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of panels.
 | 
			
		||||
        @see getPanel
 | 
			
		||||
    */
 | 
			
		||||
    int getNumPanels() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the panels.
 | 
			
		||||
        @see getNumPanels()
 | 
			
		||||
    */
 | 
			
		||||
    Component* getPanel (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Resizes one of the panels.
 | 
			
		||||
        The panelComponent must point to  a valid panel component.
 | 
			
		||||
        If animate is true, the panels will be animated into their new positions;
 | 
			
		||||
        if false, they will just be immediately resized.
 | 
			
		||||
    */
 | 
			
		||||
    bool setPanelSize (Component* panelComponent, int newHeight, bool animate);
 | 
			
		||||
 | 
			
		||||
    /** Attempts to make one of the panels full-height.
 | 
			
		||||
        The panelComponent must point to  a valid panel component.
 | 
			
		||||
        If this component has had a maximum size set, then it will be
 | 
			
		||||
        expanded to that size. Otherwise, it'll fill as much of the total
 | 
			
		||||
        space as possible.
 | 
			
		||||
    */
 | 
			
		||||
    bool expandPanelFully (Component* panelComponent, bool animate);
 | 
			
		||||
 | 
			
		||||
    /** Sets a maximum size for one of the panels. */
 | 
			
		||||
    void setMaximumPanelSize (Component* panelComponent, int maximumSize);
 | 
			
		||||
 | 
			
		||||
    /** Sets the height of the header section for one of the panels. */
 | 
			
		||||
    void setPanelHeaderSize (Component* panelComponent, int headerSize);
 | 
			
		||||
 | 
			
		||||
    /** Sets a custom header Component for one of the panels.
 | 
			
		||||
 | 
			
		||||
        @param panelComponent           the panel component to add the custom header to.
 | 
			
		||||
        @param customHeaderComponent    the custom component to use for the panel header.
 | 
			
		||||
                                        This can be nullptr to clear the custom header component
 | 
			
		||||
                                        and just use the standard LookAndFeel panel.
 | 
			
		||||
        @param takeOwnership            if true, then the PanelHolder will take ownership
 | 
			
		||||
                                        of the custom header component, and will delete it later when
 | 
			
		||||
                                        it's no longer needed. If false, it won't delete it, and
 | 
			
		||||
                                        you must make sure it doesn't get deleted while in use.
 | 
			
		||||
     */
 | 
			
		||||
    void setCustomPanelHeader (Component* panelComponent, Component* customHeaderComponent, bool takeOwnership);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes. */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual void drawConcertinaPanelHeader (Graphics&, const Rectangle<int>& area,
 | 
			
		||||
                                                bool isMouseOver, bool isMouseDown,
 | 
			
		||||
                                                ConcertinaPanel&, Component&) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void resized() override;
 | 
			
		||||
 | 
			
		||||
    class PanelHolder;
 | 
			
		||||
    struct PanelSizes;
 | 
			
		||||
    std::unique_ptr<PanelSizes> currentSizes;
 | 
			
		||||
    OwnedArray<PanelHolder> holders;
 | 
			
		||||
    ComponentAnimator animator;
 | 
			
		||||
    int headerHeight;
 | 
			
		||||
 | 
			
		||||
    int indexOfComp (Component*) const noexcept;
 | 
			
		||||
    PanelSizes getFittedSizes() const;
 | 
			
		||||
    void applyLayout (const PanelSizes&, bool animate);
 | 
			
		||||
    void setLayout (const PanelSizes&, bool animate);
 | 
			
		||||
    void panelHeaderDoubleClicked (Component*);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaPanel)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,146 +1,143 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Represents a FlexBox container, which contains and manages the layout of a set
 | 
			
		||||
    of FlexItem objects.
 | 
			
		||||
 | 
			
		||||
    To use this class, set its parameters appropriately (you can search online for
 | 
			
		||||
    more help on exactly how the FlexBox protocol works!), then add your sub-items
 | 
			
		||||
    to the items array, and call performLayout() in the resized() function of your
 | 
			
		||||
    Component.
 | 
			
		||||
 | 
			
		||||
    @see FlexItem
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  FlexBox  final
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Possible values for the flexDirection property. */
 | 
			
		||||
    enum class Direction
 | 
			
		||||
    {
 | 
			
		||||
        row,                  /**< Set the main axis direction from left to right. */
 | 
			
		||||
        rowReverse,           /**< Set the main axis direction from right to left. */
 | 
			
		||||
        column,               /**< Set the main axis direction from top to bottom. */
 | 
			
		||||
        columnReverse         /**< Set the main axis direction from bottom to top. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the flexWrap property. */
 | 
			
		||||
    enum class Wrap
 | 
			
		||||
    {
 | 
			
		||||
        noWrap,               /**< Items are forced into a single line. */
 | 
			
		||||
        wrap,                 /**< Items are wrapped onto multiple lines from top to bottom. */
 | 
			
		||||
        wrapReverse           /**< Items are wrapped onto multiple lines from bottom to top. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignContent property. */
 | 
			
		||||
    enum class AlignContent
 | 
			
		||||
    {
 | 
			
		||||
        stretch,              /**< Lines of items are stretched from start to end of the cross axis. */
 | 
			
		||||
        flexStart,            /**< Lines of items are aligned towards the start of the cross axis. */
 | 
			
		||||
        flexEnd,              /**< Lines of items are aligned towards the end of the cross axis. */
 | 
			
		||||
        center,               /**< Lines of items are aligned towards the center of the cross axis. */
 | 
			
		||||
        spaceBetween,         /**< Lines of items are evenly spaced along the cross axis with spaces between them. */
 | 
			
		||||
        spaceAround           /**< Lines of items are evenly spaced along the cross axis with spaces around them. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignItems property. */
 | 
			
		||||
    enum class AlignItems
 | 
			
		||||
    {
 | 
			
		||||
        stretch,              /**< Items are stretched from start to end of the cross axis. */
 | 
			
		||||
        flexStart,            /**< Items are aligned towards the start of the cross axis. */
 | 
			
		||||
        flexEnd,              /**< Items are aligned towards the end of the cross axis. */
 | 
			
		||||
        center                /**< Items are aligned towards the center of the cross axis. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the justifyContent property. */
 | 
			
		||||
    enum class JustifyContent
 | 
			
		||||
    {
 | 
			
		||||
        flexStart,            /**< Items are justified towards the start of the main axis. */
 | 
			
		||||
        flexEnd,              /**< Items are justified towards the end of the main axis. */
 | 
			
		||||
        center,               /**< Items are justified towards the center of the main axis. */
 | 
			
		||||
        spaceBetween,         /**< Items are evenly spaced along the main axis with spaces between them. */
 | 
			
		||||
        spaceAround           /**< Items are evenly spaced along the main axis with spaces around them. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty FlexBox container with default parameters. */
 | 
			
		||||
    FlexBox() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an empty FlexBox container with these parameters. */
 | 
			
		||||
    FlexBox (Direction, Wrap, AlignContent, AlignItems, JustifyContent) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an empty FlexBox container with the given content-justification mode. */
 | 
			
		||||
    FlexBox (JustifyContent) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~FlexBox() noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Lays-out the box's items within the given rectangle. */
 | 
			
		||||
    void performLayout (Rectangle<float> targetArea);
 | 
			
		||||
 | 
			
		||||
    /** Lays-out the box's items within the given rectangle. */
 | 
			
		||||
    void performLayout (Rectangle<int> targetArea);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Specifies how flex items are placed in the flex container, and defines the
 | 
			
		||||
        direction of the main axis.
 | 
			
		||||
    */
 | 
			
		||||
    Direction flexDirection = Direction::row;
 | 
			
		||||
 | 
			
		||||
    /** Specifies whether items are forced into a single line or can be wrapped onto multiple lines.
 | 
			
		||||
        If wrapping is allowed, this property also controls the direction in which lines are stacked.
 | 
			
		||||
    */
 | 
			
		||||
    Wrap flexWrap = Wrap::noWrap;
 | 
			
		||||
 | 
			
		||||
    /** Specifies how a flex container's lines are placed within the flex container when
 | 
			
		||||
        there is extra space on the cross-axis.
 | 
			
		||||
        This property has no effect on single line layouts.
 | 
			
		||||
    */
 | 
			
		||||
    AlignContent alignContent = AlignContent::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the alignment of flex items along the cross-axis of each line. */
 | 
			
		||||
    AlignItems alignItems = AlignItems::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Defines how the container distributes space between and around items along the main-axis.
 | 
			
		||||
        The alignment is done after the lengths and auto margins are applied, so that if there is at
 | 
			
		||||
        least one flexible element, with flex-grow different from 0, it will have no effect as there
 | 
			
		||||
        won't be any available space.
 | 
			
		||||
    */
 | 
			
		||||
    JustifyContent justifyContent = JustifyContent::flexStart;
 | 
			
		||||
 | 
			
		||||
    /** The set of items to lay-out. */
 | 
			
		||||
    Array<FlexItem> items;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    JUCE_LEAK_DETECTOR (FlexBox)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Represents a FlexBox container, which contains and manages the layout of a set
 | 
			
		||||
    of FlexItem objects.
 | 
			
		||||
 | 
			
		||||
    To use this class, set its parameters appropriately (you can search online for
 | 
			
		||||
    more help on exactly how the FlexBox protocol works!), then add your sub-items
 | 
			
		||||
    to the items array, and call performLayout() in the resized() function of your
 | 
			
		||||
    Component.
 | 
			
		||||
 | 
			
		||||
    @see FlexItem
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  FlexBox  final
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** Possible values for the flexDirection property. */
 | 
			
		||||
    enum class Direction
 | 
			
		||||
    {
 | 
			
		||||
        row,                  /**< Set the main axis direction from left to right. */
 | 
			
		||||
        rowReverse,           /**< Set the main axis direction from right to left. */
 | 
			
		||||
        column,               /**< Set the main axis direction from top to bottom. */
 | 
			
		||||
        columnReverse         /**< Set the main axis direction from bottom to top. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the flexWrap property. */
 | 
			
		||||
    enum class Wrap
 | 
			
		||||
    {
 | 
			
		||||
        noWrap,               /**< Items are forced into a single line. */
 | 
			
		||||
        wrap,                 /**< Items are wrapped onto multiple lines from top to bottom. */
 | 
			
		||||
        wrapReverse           /**< Items are wrapped onto multiple lines from bottom to top. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignContent property. */
 | 
			
		||||
    enum class AlignContent
 | 
			
		||||
    {
 | 
			
		||||
        stretch,              /**< Lines of items are stretched from start to end of the cross axis. */
 | 
			
		||||
        flexStart,            /**< Lines of items are aligned towards the start of the cross axis. */
 | 
			
		||||
        flexEnd,              /**< Lines of items are aligned towards the end of the cross axis. */
 | 
			
		||||
        center,               /**< Lines of items are aligned towards the center of the cross axis. */
 | 
			
		||||
        spaceBetween,         /**< Lines of items are evenly spaced along the cross axis with spaces between them. */
 | 
			
		||||
        spaceAround           /**< Lines of items are evenly spaced along the cross axis with spaces around them. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignItems property. */
 | 
			
		||||
    enum class AlignItems
 | 
			
		||||
    {
 | 
			
		||||
        stretch,              /**< Items are stretched from start to end of the cross axis. */
 | 
			
		||||
        flexStart,            /**< Items are aligned towards the start of the cross axis. */
 | 
			
		||||
        flexEnd,              /**< Items are aligned towards the end of the cross axis. */
 | 
			
		||||
        center                /**< Items are aligned towards the center of the cross axis. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the justifyContent property. */
 | 
			
		||||
    enum class JustifyContent
 | 
			
		||||
    {
 | 
			
		||||
        flexStart,            /**< Items are justified towards the start of the main axis. */
 | 
			
		||||
        flexEnd,              /**< Items are justified towards the end of the main axis. */
 | 
			
		||||
        center,               /**< Items are justified towards the center of the main axis. */
 | 
			
		||||
        spaceBetween,         /**< Items are evenly spaced along the main axis with spaces between them. */
 | 
			
		||||
        spaceAround           /**< Items are evenly spaced along the main axis with spaces around them. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty FlexBox container with default parameters. */
 | 
			
		||||
    FlexBox() noexcept = default;
 | 
			
		||||
 | 
			
		||||
    /** Creates an empty FlexBox container with these parameters. */
 | 
			
		||||
    FlexBox (Direction, Wrap, AlignContent, AlignItems, JustifyContent) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an empty FlexBox container with the given content-justification mode. */
 | 
			
		||||
    FlexBox (JustifyContent) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Lays-out the box's items within the given rectangle. */
 | 
			
		||||
    void performLayout (Rectangle<float> targetArea);
 | 
			
		||||
 | 
			
		||||
    /** Lays-out the box's items within the given rectangle. */
 | 
			
		||||
    void performLayout (Rectangle<int> targetArea);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Specifies how flex items are placed in the flex container, and defines the
 | 
			
		||||
        direction of the main axis.
 | 
			
		||||
    */
 | 
			
		||||
    Direction flexDirection = Direction::row;
 | 
			
		||||
 | 
			
		||||
    /** Specifies whether items are forced into a single line or can be wrapped onto multiple lines.
 | 
			
		||||
        If wrapping is allowed, this property also controls the direction in which lines are stacked.
 | 
			
		||||
    */
 | 
			
		||||
    Wrap flexWrap = Wrap::noWrap;
 | 
			
		||||
 | 
			
		||||
    /** Specifies how a flex container's lines are placed within the flex container when
 | 
			
		||||
        there is extra space on the cross-axis.
 | 
			
		||||
        This property has no effect on single line layouts.
 | 
			
		||||
    */
 | 
			
		||||
    AlignContent alignContent = AlignContent::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the alignment of flex items along the cross-axis of each line. */
 | 
			
		||||
    AlignItems alignItems = AlignItems::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Defines how the container distributes space between and around items along the main-axis.
 | 
			
		||||
        The alignment is done after the lengths and auto margins are applied, so that if there is at
 | 
			
		||||
        least one flexible element, with flex-grow different from 0, it will have no effect as there
 | 
			
		||||
        won't be any available space.
 | 
			
		||||
    */
 | 
			
		||||
    JustifyContent justifyContent = JustifyContent::flexStart;
 | 
			
		||||
 | 
			
		||||
    /** The set of items to lay-out. */
 | 
			
		||||
    Array<FlexItem> items;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    JUCE_LEAK_DETECTOR (FlexBox)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,177 +1,177 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Describes the properties of an item inside a FlexBox container.
 | 
			
		||||
 | 
			
		||||
    @see FlexBox
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  FlexItem  final
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an item with default parameters, and zero size. */
 | 
			
		||||
    FlexItem() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item with the given size. */
 | 
			
		||||
    FlexItem (float width, float height) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item with the given size and target Component. */
 | 
			
		||||
    FlexItem (float width, float height, Component& targetComponent) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item that represents an embedded FlexBox with a given size. */
 | 
			
		||||
    FlexItem (float width, float height, FlexBox& flexBoxToControl) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item with a given target Component. */
 | 
			
		||||
    FlexItem (Component& componentToControl) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item that represents an embedded FlexBox. This class will not
 | 
			
		||||
        create a copy of the supplied flex box. You need to ensure that the
 | 
			
		||||
        life-time of flexBoxToControl is longer than the FlexItem. */
 | 
			
		||||
    FlexItem (FlexBox& flexBoxToControl) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The item's current bounds. */
 | 
			
		||||
    Rectangle<float> currentBounds;
 | 
			
		||||
 | 
			
		||||
    /** If this is non-null, it represents a Component whose bounds are controlled by this item. */
 | 
			
		||||
    Component* associatedComponent = nullptr;
 | 
			
		||||
 | 
			
		||||
    /** If this is non-null, it represents a FlexBox whose bounds are controlled by this item. */
 | 
			
		||||
    FlexBox* associatedFlexBox = nullptr;
 | 
			
		||||
 | 
			
		||||
    /** Determines the order used to lay out items in their flex container.
 | 
			
		||||
        Elements are laid out in ascending order of thus order value. Elements with the same order value
 | 
			
		||||
        are laid out in the order in which they appear in the array.
 | 
			
		||||
    */
 | 
			
		||||
    int order = 0;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the flex grow factor of this item.
 | 
			
		||||
        This indicates the amount of space inside the flex container the item should take up.
 | 
			
		||||
    */
 | 
			
		||||
    float flexGrow = 0.0f;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the flex shrink factor of the item.
 | 
			
		||||
        This indicates the rate at which the item shrinks if there is insufficient space in
 | 
			
		||||
        the container.
 | 
			
		||||
    */
 | 
			
		||||
    float flexShrink = 1.0f;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the flex-basis of the item.
 | 
			
		||||
        This is the initial main size of a flex item in the direction of flow. It determines the size
 | 
			
		||||
        of the content-box unless specified otherwise using box-sizing.
 | 
			
		||||
    */
 | 
			
		||||
    float flexBasis = 0.0f;
 | 
			
		||||
 | 
			
		||||
    /** Possible value for the alignSelf property */
 | 
			
		||||
    enum class AlignSelf
 | 
			
		||||
    {
 | 
			
		||||
        autoAlign,       /**< Follows the FlexBox container's alignItems property. */
 | 
			
		||||
        flexStart,       /**< Item is aligned towards the start of the cross axis. */
 | 
			
		||||
        flexEnd,         /**< Item is aligned towards the end of the cross axis. */
 | 
			
		||||
        center,          /**< Item is aligned towards the center of the cross axis. */
 | 
			
		||||
        stretch          /**< Item is stretched from start to end of the cross axis. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** This is the align-self property of the item.
 | 
			
		||||
        This determines the alignment of the item along the cross-axis (perpendicular to the direction
 | 
			
		||||
        of flow).
 | 
			
		||||
    */
 | 
			
		||||
    AlignSelf alignSelf = AlignSelf::stretch;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This constant can be used for sizes to indicate that 'auto' mode should be used. */
 | 
			
		||||
    static const int autoValue    = -2;
 | 
			
		||||
    /** This constant can be used for sizes to indicate that no value has been set. */
 | 
			
		||||
    static const int notAssigned  = -1;
 | 
			
		||||
 | 
			
		||||
    float width     = (float) notAssigned;  /**< The item's width. */
 | 
			
		||||
    float minWidth  = 0.0f;                 /**< The item's minimum width */
 | 
			
		||||
    float maxWidth  = (float) notAssigned;  /**< The item's maximum width */
 | 
			
		||||
 | 
			
		||||
    float height    = (float) notAssigned;  /**< The item's height */
 | 
			
		||||
    float minHeight = 0.0f;                 /**< The item's minimum height */
 | 
			
		||||
    float maxHeight = (float) notAssigned;  /**< The item's maximum height */
 | 
			
		||||
 | 
			
		||||
    /** Represents a margin. */
 | 
			
		||||
    struct Margin  final
 | 
			
		||||
    {
 | 
			
		||||
        Margin() noexcept;              /**< Creates a margin of size zero. */
 | 
			
		||||
        Margin (float size) noexcept;   /**< Creates a margin with this size on all sides. */
 | 
			
		||||
        Margin (float top, float right, float bottom, float left) noexcept;   /**< Creates a margin with these sizes. */
 | 
			
		||||
 | 
			
		||||
        float left;   /**< Left margin size */
 | 
			
		||||
        float right;  /**< Right margin size */
 | 
			
		||||
        float top;    /**< Top margin size */
 | 
			
		||||
        float bottom; /**< Bottom margin size */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** The margin to leave around this item. */
 | 
			
		||||
    Margin margin;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns a copy of this object with a new flex-grow value. */
 | 
			
		||||
    FlexItem withFlex (float newFlexGrow) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with new flex-grow and flex-shrink values. */
 | 
			
		||||
    FlexItem withFlex (float newFlexGrow, float newFlexShrink) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with new flex-grow, flex-shrink and flex-basis values. */
 | 
			
		||||
    FlexItem withFlex (float newFlexGrow, float newFlexShrink, float newFlexBasis) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new width. */
 | 
			
		||||
    FlexItem withWidth (float newWidth) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new minimum width. */
 | 
			
		||||
    FlexItem withMinWidth (float newMinWidth) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new maximum width. */
 | 
			
		||||
    FlexItem withMaxWidth (float newMaxWidth) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new height. */
 | 
			
		||||
    FlexItem withHeight (float newHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new minimum height. */
 | 
			
		||||
    FlexItem withMinHeight (float newMinHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new maximum height. */
 | 
			
		||||
    FlexItem withMaxHeight (float newMaxHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new margin. */
 | 
			
		||||
    FlexItem withMargin (Margin) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new order. */
 | 
			
		||||
    FlexItem withOrder (int newOrder) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new alignSelf value. */
 | 
			
		||||
    FlexItem withAlignSelf (AlignSelf newAlignSelf) const noexcept;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Describes the properties of an item inside a FlexBox container.
 | 
			
		||||
 | 
			
		||||
    @see FlexBox
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  FlexItem  final
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an item with default parameters, and zero size. */
 | 
			
		||||
    FlexItem() noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item with the given size. */
 | 
			
		||||
    FlexItem (float width, float height) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item with the given size and target Component. */
 | 
			
		||||
    FlexItem (float width, float height, Component& targetComponent) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item that represents an embedded FlexBox with a given size. */
 | 
			
		||||
    FlexItem (float width, float height, FlexBox& flexBoxToControl) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item with a given target Component. */
 | 
			
		||||
    FlexItem (Component& componentToControl) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Creates an item that represents an embedded FlexBox. This class will not
 | 
			
		||||
        create a copy of the supplied flex box. You need to ensure that the
 | 
			
		||||
        life-time of flexBoxToControl is longer than the FlexItem. */
 | 
			
		||||
    FlexItem (FlexBox& flexBoxToControl) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The item's current bounds. */
 | 
			
		||||
    Rectangle<float> currentBounds;
 | 
			
		||||
 | 
			
		||||
    /** If this is non-null, it represents a Component whose bounds are controlled by this item. */
 | 
			
		||||
    Component* associatedComponent = nullptr;
 | 
			
		||||
 | 
			
		||||
    /** If this is non-null, it represents a FlexBox whose bounds are controlled by this item. */
 | 
			
		||||
    FlexBox* associatedFlexBox = nullptr;
 | 
			
		||||
 | 
			
		||||
    /** Determines the order used to lay out items in their flex container.
 | 
			
		||||
        Elements are laid out in ascending order of thus order value. Elements with the same order value
 | 
			
		||||
        are laid out in the order in which they appear in the array.
 | 
			
		||||
    */
 | 
			
		||||
    int order = 0;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the flex grow factor of this item.
 | 
			
		||||
        This indicates the amount of space inside the flex container the item should take up.
 | 
			
		||||
    */
 | 
			
		||||
    float flexGrow = 0.0f;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the flex shrink factor of the item.
 | 
			
		||||
        This indicates the rate at which the item shrinks if there is insufficient space in
 | 
			
		||||
        the container.
 | 
			
		||||
    */
 | 
			
		||||
    float flexShrink = 1.0f;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the flex-basis of the item.
 | 
			
		||||
        This is the initial main size of a flex item in the direction of flow. It determines the size
 | 
			
		||||
        of the content-box unless specified otherwise using box-sizing.
 | 
			
		||||
    */
 | 
			
		||||
    float flexBasis = 0.0f;
 | 
			
		||||
 | 
			
		||||
    /** Possible value for the alignSelf property */
 | 
			
		||||
    enum class AlignSelf
 | 
			
		||||
    {
 | 
			
		||||
        autoAlign,       /**< Follows the FlexBox container's alignItems property. */
 | 
			
		||||
        flexStart,       /**< Item is aligned towards the start of the cross axis. */
 | 
			
		||||
        flexEnd,         /**< Item is aligned towards the end of the cross axis. */
 | 
			
		||||
        center,          /**< Item is aligned towards the center of the cross axis. */
 | 
			
		||||
        stretch          /**< Item is stretched from start to end of the cross axis. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** This is the align-self property of the item.
 | 
			
		||||
        This determines the alignment of the item along the cross-axis (perpendicular to the direction
 | 
			
		||||
        of flow).
 | 
			
		||||
    */
 | 
			
		||||
    AlignSelf alignSelf = AlignSelf::autoAlign;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This constant can be used for sizes to indicate that 'auto' mode should be used. */
 | 
			
		||||
    static const int autoValue    = -2;
 | 
			
		||||
    /** This constant can be used for sizes to indicate that no value has been set. */
 | 
			
		||||
    static const int notAssigned  = -1;
 | 
			
		||||
 | 
			
		||||
    float width     = (float) notAssigned;  /**< The item's width. */
 | 
			
		||||
    float minWidth  = 0.0f;                 /**< The item's minimum width */
 | 
			
		||||
    float maxWidth  = (float) notAssigned;  /**< The item's maximum width */
 | 
			
		||||
 | 
			
		||||
    float height    = (float) notAssigned;  /**< The item's height */
 | 
			
		||||
    float minHeight = 0.0f;                 /**< The item's minimum height */
 | 
			
		||||
    float maxHeight = (float) notAssigned;  /**< The item's maximum height */
 | 
			
		||||
 | 
			
		||||
    /** Represents a margin. */
 | 
			
		||||
    struct Margin  final
 | 
			
		||||
    {
 | 
			
		||||
        Margin() noexcept;              /**< Creates a margin of size zero. */
 | 
			
		||||
        Margin (float size) noexcept;   /**< Creates a margin with this size on all sides. */
 | 
			
		||||
        Margin (float top, float right, float bottom, float left) noexcept;   /**< Creates a margin with these sizes. */
 | 
			
		||||
 | 
			
		||||
        float left;   /**< Left margin size */
 | 
			
		||||
        float right;  /**< Right margin size */
 | 
			
		||||
        float top;    /**< Top margin size */
 | 
			
		||||
        float bottom; /**< Bottom margin size */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** The margin to leave around this item. */
 | 
			
		||||
    Margin margin;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns a copy of this object with a new flex-grow value. */
 | 
			
		||||
    FlexItem withFlex (float newFlexGrow) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with new flex-grow and flex-shrink values. */
 | 
			
		||||
    FlexItem withFlex (float newFlexGrow, float newFlexShrink) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with new flex-grow, flex-shrink and flex-basis values. */
 | 
			
		||||
    FlexItem withFlex (float newFlexGrow, float newFlexShrink, float newFlexBasis) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new width. */
 | 
			
		||||
    FlexItem withWidth (float newWidth) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new minimum width. */
 | 
			
		||||
    FlexItem withMinWidth (float newMinWidth) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new maximum width. */
 | 
			
		||||
    FlexItem withMaxWidth (float newMaxWidth) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new height. */
 | 
			
		||||
    FlexItem withHeight (float newHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new minimum height. */
 | 
			
		||||
    FlexItem withMinHeight (float newMinHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new maximum height. */
 | 
			
		||||
    FlexItem withMaxHeight (float newMaxHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new margin. */
 | 
			
		||||
    FlexItem withMargin (Margin) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new order. */
 | 
			
		||||
    FlexItem withOrder (int newOrder) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new alignSelf value. */
 | 
			
		||||
    FlexItem withAlignSelf (AlignSelf newAlignSelf) const noexcept;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2709
									
								
								deps/juce/modules/juce_gui_basics/layout/juce_Grid.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2709
									
								
								deps/juce/modules/juce_gui_basics/layout/juce_Grid.cpp
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										458
									
								
								deps/juce/modules/juce_gui_basics/layout/juce_Grid.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										458
									
								
								deps/juce/modules/juce_gui_basics/layout/juce_Grid.h
									
									
									
									
										vendored
									
									
								
							@@ -1,229 +1,229 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Container that handles geometry for grid layouts (fixed columns and rows) using a set of declarative rules.
 | 
			
		||||
 | 
			
		||||
    Implemented from the `CSS Grid Layout` specification as described at:
 | 
			
		||||
    https://css-tricks.com/snippets/css/complete-guide-grid/
 | 
			
		||||
 | 
			
		||||
    @see GridItem
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  Grid  final
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A size in pixels */
 | 
			
		||||
    struct Px  final
 | 
			
		||||
    {
 | 
			
		||||
        explicit Px (float p) : pixels (static_cast<long double>(p)) { /*sta (p >= 0.0f);*/ }
 | 
			
		||||
        explicit Px (int p)   : pixels (static_cast<long double>(p)) { /*sta (p >= 0.0f);*/ }
 | 
			
		||||
        explicit constexpr Px (long double p)        : pixels (p) {}
 | 
			
		||||
        explicit constexpr Px (unsigned long long p) : pixels (static_cast<long double>(p)) {}
 | 
			
		||||
 | 
			
		||||
        long double pixels;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** A fractional ratio integer */
 | 
			
		||||
    struct Fr  final
 | 
			
		||||
    {
 | 
			
		||||
        explicit Fr (int f) : fraction (static_cast<unsigned long long> (f)) {}
 | 
			
		||||
        explicit constexpr Fr (unsigned long long p) : fraction (p) {}
 | 
			
		||||
 | 
			
		||||
        unsigned long long fraction;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents a track. */
 | 
			
		||||
    struct TrackInfo  final
 | 
			
		||||
    {
 | 
			
		||||
        /** Creates a track with auto dimension. */
 | 
			
		||||
        TrackInfo() noexcept;
 | 
			
		||||
 | 
			
		||||
        TrackInfo (Px sizeInPixels) noexcept;
 | 
			
		||||
        TrackInfo (Fr fractionOfFreeSpace) noexcept;
 | 
			
		||||
 | 
			
		||||
        TrackInfo (Px sizeInPixels, const String& endLineNameToUse) noexcept;
 | 
			
		||||
        TrackInfo (Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        TrackInfo (const String& startLineNameToUse, Px sizeInPixels) noexcept;
 | 
			
		||||
        TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace) noexcept;
 | 
			
		||||
 | 
			
		||||
        TrackInfo (const String& startLineNameToUse, Px sizeInPixels, const String& endLineNameToUse) noexcept;
 | 
			
		||||
        TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        bool isAuto() const noexcept { return hasKeyword; }
 | 
			
		||||
        bool isFractional() const noexcept { return isFraction; }
 | 
			
		||||
        bool isPixels() const noexcept { return ! isFraction; }
 | 
			
		||||
        const String& getStartLineName() const noexcept { return startLineName; }
 | 
			
		||||
        const String& getEndLineName() const noexcept { return endLineName; }
 | 
			
		||||
 | 
			
		||||
        /** Get the track's size - which might mean an absolute pixels value or a fractional ratio. */
 | 
			
		||||
        float getSize() const noexcept { return size; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        friend class Grid;
 | 
			
		||||
        float getAbsoluteSize (float relativeFractionalUnit) const;
 | 
			
		||||
 | 
			
		||||
        float size = 0; // Either a fraction or an absolute size in pixels
 | 
			
		||||
        bool isFraction = false;
 | 
			
		||||
        bool hasKeyword = false;
 | 
			
		||||
 | 
			
		||||
        String startLineName, endLineName;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Possible values for the justifyItems property. */
 | 
			
		||||
    enum class JustifyItems : int
 | 
			
		||||
    {
 | 
			
		||||
        start = 0,                /**< Content inside the item is justified towards the left. */
 | 
			
		||||
        end,                      /**< Content inside the item is justified towards the right. */
 | 
			
		||||
        center,                   /**< Content inside the item is justified towards the center. */
 | 
			
		||||
        stretch                   /**< Content inside the item is stretched from left to right. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignItems property. */
 | 
			
		||||
    enum class AlignItems : int
 | 
			
		||||
    {
 | 
			
		||||
        start = 0,                /**< Content inside the item is aligned towards the top. */
 | 
			
		||||
        end,                      /**< Content inside the item is aligned towards the bottom. */
 | 
			
		||||
        center,                   /**< Content inside the item is aligned towards the center. */
 | 
			
		||||
        stretch                   /**< Content inside the item is stretched from top to bottom. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the justifyContent property. */
 | 
			
		||||
    enum class JustifyContent
 | 
			
		||||
    {
 | 
			
		||||
        start,                    /**< Items are justified towards the left of the container. */
 | 
			
		||||
        end,                      /**< Items are justified towards the right of the container. */
 | 
			
		||||
        center,                   /**< Items are justified towards the center of the container. */
 | 
			
		||||
        stretch,                  /**< Items are stretched from left to right of the container. */
 | 
			
		||||
        spaceAround,              /**< Items are evenly spaced along the row with spaces between them. */
 | 
			
		||||
        spaceBetween,             /**< Items are evenly spaced along the row with spaces around them. */
 | 
			
		||||
        spaceEvenly               /**< Items are evenly spaced along the row with even amount of spaces between them. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignContent property. */
 | 
			
		||||
    enum class AlignContent
 | 
			
		||||
    {
 | 
			
		||||
        start,                    /**< Items are aligned towards the top of the container. */
 | 
			
		||||
        end,                      /**< Items are aligned towards the bottom of the container. */
 | 
			
		||||
        center,                   /**< Items are aligned towards the center of the container. */
 | 
			
		||||
        stretch,                  /**< Items are stretched from top to bottom of the container. */
 | 
			
		||||
        spaceAround,              /**< Items are evenly spaced along the column with spaces between them. */
 | 
			
		||||
        spaceBetween,             /**< Items are evenly spaced along the column with spaces around them. */
 | 
			
		||||
        spaceEvenly               /**< Items are evenly spaced along the column with even amount of spaces between them. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the autoFlow property. */
 | 
			
		||||
    enum class AutoFlow
 | 
			
		||||
    {
 | 
			
		||||
        row,                      /**< Fills the grid by adding rows of items. */
 | 
			
		||||
        column,                   /**< Fills the grid by adding columns of items. */
 | 
			
		||||
        rowDense,                 /**< Fills the grid by adding rows of items and attempts to fill in gaps. */
 | 
			
		||||
        columnDense               /**< Fills the grid by adding columns of items and attempts to fill in gaps. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty Grid container with default parameters. */
 | 
			
		||||
    Grid() = default;
 | 
			
		||||
 | 
			
		||||
    /** Destructor */
 | 
			
		||||
    ~Grid() noexcept = default;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Specifies the alignment of content inside the items along the rows. */
 | 
			
		||||
    JustifyItems   justifyItems   = JustifyItems::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the alignment of content inside the items along the columns. */
 | 
			
		||||
    AlignItems     alignItems     = AlignItems::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the alignment of items along the rows. */
 | 
			
		||||
    JustifyContent justifyContent = JustifyContent::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the alignment of items along the columns. */
 | 
			
		||||
    AlignContent   alignContent   = AlignContent::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies how the auto-placement algorithm places items. */
 | 
			
		||||
    AutoFlow       autoFlow       = AutoFlow::row;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The set of column tracks to lay out. */
 | 
			
		||||
    Array<TrackInfo> templateColumns;
 | 
			
		||||
 | 
			
		||||
    /** The set of row tracks to lay out. */
 | 
			
		||||
    Array<TrackInfo> templateRows;
 | 
			
		||||
 | 
			
		||||
    /** Template areas */
 | 
			
		||||
    StringArray templateAreas;
 | 
			
		||||
 | 
			
		||||
    /** The row track for auto dimension. */
 | 
			
		||||
    TrackInfo autoRows;
 | 
			
		||||
 | 
			
		||||
    /** The column track for auto dimension. */
 | 
			
		||||
    TrackInfo autoColumns;
 | 
			
		||||
 | 
			
		||||
    /** The gap in pixels between columns. */
 | 
			
		||||
    Px columnGap { 0 };
 | 
			
		||||
    /** The gap in pixels between rows. */
 | 
			
		||||
    Px rowGap { 0 };
 | 
			
		||||
 | 
			
		||||
    /** Sets the gap between rows and columns in pixels. */
 | 
			
		||||
    void setGap (Px sizeInPixels) noexcept          { rowGap = columnGap = sizeInPixels; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The set of items to lay-out. */
 | 
			
		||||
    Array<GridItem> items;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Lays-out the grid's items within the given rectangle. */
 | 
			
		||||
    void performLayout (Rectangle<int>);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the number of columns. */
 | 
			
		||||
    int getNumberOfColumns() const noexcept         { return templateColumns.size(); }
 | 
			
		||||
    /** Returns the number of rows. */
 | 
			
		||||
    int getNumberOfRows() const noexcept            { return templateRows.size(); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct SizeCalculation;
 | 
			
		||||
    struct PlacementHelpers;
 | 
			
		||||
    struct AutoPlacement;
 | 
			
		||||
    struct BoxAlignment;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr Grid::Px operator"" _px (long double px)          { return Grid::Px { px }; }
 | 
			
		||||
constexpr Grid::Px operator"" _px (unsigned long long px)   { return Grid::Px { px }; }
 | 
			
		||||
constexpr Grid::Fr operator"" _fr (unsigned long long fr)   { return Grid::Fr { fr }; }
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Container that handles geometry for grid layouts (fixed columns and rows) using a set of declarative rules.
 | 
			
		||||
 | 
			
		||||
    Implemented from the `CSS Grid Layout` specification as described at:
 | 
			
		||||
    https://css-tricks.com/snippets/css/complete-guide-grid/
 | 
			
		||||
 | 
			
		||||
    @see GridItem
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  Grid  final
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A size in pixels */
 | 
			
		||||
    struct Px  final
 | 
			
		||||
    {
 | 
			
		||||
        explicit Px (float p) : pixels (static_cast<long double>(p)) { /*sta (p >= 0.0f);*/ }
 | 
			
		||||
        explicit Px (int p)   : pixels (static_cast<long double>(p)) { /*sta (p >= 0.0f);*/ }
 | 
			
		||||
        explicit constexpr Px (long double p)        : pixels (p) {}
 | 
			
		||||
        explicit constexpr Px (unsigned long long p) : pixels (static_cast<long double>(p)) {}
 | 
			
		||||
 | 
			
		||||
        long double pixels;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** A fractional ratio integer */
 | 
			
		||||
    struct Fr  final
 | 
			
		||||
    {
 | 
			
		||||
        explicit Fr (int f) : fraction (static_cast<unsigned long long> (f)) {}
 | 
			
		||||
        explicit constexpr Fr (unsigned long long p) : fraction (p) {}
 | 
			
		||||
 | 
			
		||||
        unsigned long long fraction;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents a track. */
 | 
			
		||||
    struct TrackInfo  final
 | 
			
		||||
    {
 | 
			
		||||
        /** Creates a track with auto dimension. */
 | 
			
		||||
        TrackInfo() noexcept;
 | 
			
		||||
 | 
			
		||||
        TrackInfo (Px sizeInPixels) noexcept;
 | 
			
		||||
        TrackInfo (Fr fractionOfFreeSpace) noexcept;
 | 
			
		||||
 | 
			
		||||
        TrackInfo (Px sizeInPixels, const String& endLineNameToUse) noexcept;
 | 
			
		||||
        TrackInfo (Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        TrackInfo (const String& startLineNameToUse, Px sizeInPixels) noexcept;
 | 
			
		||||
        TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace) noexcept;
 | 
			
		||||
 | 
			
		||||
        TrackInfo (const String& startLineNameToUse, Px sizeInPixels, const String& endLineNameToUse) noexcept;
 | 
			
		||||
        TrackInfo (const String& startLineNameToUse, Fr fractionOfFreeSpace, const String& endLineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        bool isAuto() const noexcept { return hasKeyword; }
 | 
			
		||||
        bool isFractional() const noexcept { return isFraction; }
 | 
			
		||||
        bool isPixels() const noexcept { return ! isFraction; }
 | 
			
		||||
        const String& getStartLineName() const noexcept { return startLineName; }
 | 
			
		||||
        const String& getEndLineName() const noexcept { return endLineName; }
 | 
			
		||||
 | 
			
		||||
        /** Get the track's size - which might mean an absolute pixels value or a fractional ratio. */
 | 
			
		||||
        float getSize() const noexcept { return size; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        friend class Grid;
 | 
			
		||||
        float getAbsoluteSize (float relativeFractionalUnit) const;
 | 
			
		||||
 | 
			
		||||
        float size = 0; // Either a fraction or an absolute size in pixels
 | 
			
		||||
        bool isFraction = false;
 | 
			
		||||
        bool hasKeyword = false;
 | 
			
		||||
 | 
			
		||||
        String startLineName, endLineName;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Possible values for the justifyItems property. */
 | 
			
		||||
    enum class JustifyItems : int
 | 
			
		||||
    {
 | 
			
		||||
        start = 0,                /**< Content inside the item is justified towards the left. */
 | 
			
		||||
        end,                      /**< Content inside the item is justified towards the right. */
 | 
			
		||||
        center,                   /**< Content inside the item is justified towards the center. */
 | 
			
		||||
        stretch                   /**< Content inside the item is stretched from left to right. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignItems property. */
 | 
			
		||||
    enum class AlignItems : int
 | 
			
		||||
    {
 | 
			
		||||
        start = 0,                /**< Content inside the item is aligned towards the top. */
 | 
			
		||||
        end,                      /**< Content inside the item is aligned towards the bottom. */
 | 
			
		||||
        center,                   /**< Content inside the item is aligned towards the center. */
 | 
			
		||||
        stretch                   /**< Content inside the item is stretched from top to bottom. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the justifyContent property. */
 | 
			
		||||
    enum class JustifyContent
 | 
			
		||||
    {
 | 
			
		||||
        start,                    /**< Items are justified towards the left of the container. */
 | 
			
		||||
        end,                      /**< Items are justified towards the right of the container. */
 | 
			
		||||
        center,                   /**< Items are justified towards the center of the container. */
 | 
			
		||||
        stretch,                  /**< Items are stretched from left to right of the container. */
 | 
			
		||||
        spaceAround,              /**< Items are evenly spaced along the row with spaces between them. */
 | 
			
		||||
        spaceBetween,             /**< Items are evenly spaced along the row with spaces around them. */
 | 
			
		||||
        spaceEvenly               /**< Items are evenly spaced along the row with even amount of spaces between them. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignContent property. */
 | 
			
		||||
    enum class AlignContent
 | 
			
		||||
    {
 | 
			
		||||
        start,                    /**< Items are aligned towards the top of the container. */
 | 
			
		||||
        end,                      /**< Items are aligned towards the bottom of the container. */
 | 
			
		||||
        center,                   /**< Items are aligned towards the center of the container. */
 | 
			
		||||
        stretch,                  /**< Items are stretched from top to bottom of the container. */
 | 
			
		||||
        spaceAround,              /**< Items are evenly spaced along the column with spaces between them. */
 | 
			
		||||
        spaceBetween,             /**< Items are evenly spaced along the column with spaces around them. */
 | 
			
		||||
        spaceEvenly               /**< Items are evenly spaced along the column with even amount of spaces between them. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the autoFlow property. */
 | 
			
		||||
    enum class AutoFlow
 | 
			
		||||
    {
 | 
			
		||||
        row,                      /**< Fills the grid by adding rows of items. */
 | 
			
		||||
        column,                   /**< Fills the grid by adding columns of items. */
 | 
			
		||||
        rowDense,                 /**< Fills the grid by adding rows of items and attempts to fill in gaps. */
 | 
			
		||||
        columnDense               /**< Fills the grid by adding columns of items and attempts to fill in gaps. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty Grid container with default parameters. */
 | 
			
		||||
    Grid() = default;
 | 
			
		||||
 | 
			
		||||
    /** Destructor */
 | 
			
		||||
    ~Grid() noexcept = default;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Specifies the alignment of content inside the items along the rows. */
 | 
			
		||||
    JustifyItems   justifyItems   = JustifyItems::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the alignment of content inside the items along the columns. */
 | 
			
		||||
    AlignItems     alignItems     = AlignItems::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the alignment of items along the rows. */
 | 
			
		||||
    JustifyContent justifyContent = JustifyContent::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies the alignment of items along the columns. */
 | 
			
		||||
    AlignContent   alignContent   = AlignContent::stretch;
 | 
			
		||||
 | 
			
		||||
    /** Specifies how the auto-placement algorithm places items. */
 | 
			
		||||
    AutoFlow       autoFlow       = AutoFlow::row;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The set of column tracks to lay out. */
 | 
			
		||||
    Array<TrackInfo> templateColumns;
 | 
			
		||||
 | 
			
		||||
    /** The set of row tracks to lay out. */
 | 
			
		||||
    Array<TrackInfo> templateRows;
 | 
			
		||||
 | 
			
		||||
    /** Template areas */
 | 
			
		||||
    StringArray templateAreas;
 | 
			
		||||
 | 
			
		||||
    /** The row track for auto dimension. */
 | 
			
		||||
    TrackInfo autoRows;
 | 
			
		||||
 | 
			
		||||
    /** The column track for auto dimension. */
 | 
			
		||||
    TrackInfo autoColumns;
 | 
			
		||||
 | 
			
		||||
    /** The gap in pixels between columns. */
 | 
			
		||||
    Px columnGap { 0 };
 | 
			
		||||
    /** The gap in pixels between rows. */
 | 
			
		||||
    Px rowGap { 0 };
 | 
			
		||||
 | 
			
		||||
    /** Sets the gap between rows and columns in pixels. */
 | 
			
		||||
    void setGap (Px sizeInPixels) noexcept          { rowGap = columnGap = sizeInPixels; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The set of items to lay-out. */
 | 
			
		||||
    Array<GridItem> items;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Lays-out the grid's items within the given rectangle. */
 | 
			
		||||
    void performLayout (Rectangle<int>);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the number of columns. */
 | 
			
		||||
    int getNumberOfColumns() const noexcept         { return templateColumns.size(); }
 | 
			
		||||
    /** Returns the number of rows. */
 | 
			
		||||
    int getNumberOfRows() const noexcept            { return templateRows.size(); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct SizeCalculation;
 | 
			
		||||
    struct PlacementHelpers;
 | 
			
		||||
    struct AutoPlacement;
 | 
			
		||||
    struct BoxAlignment;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr Grid::Px operator"" _px (long double px)          { return Grid::Px { px }; }
 | 
			
		||||
constexpr Grid::Px operator"" _px (unsigned long long px)   { return Grid::Px { px }; }
 | 
			
		||||
constexpr Grid::Fr operator"" _fr (unsigned long long fr)   { return Grid::Fr { fr }; }
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,181 +1,179 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property() noexcept : isAuto (true)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (GridItem::Keyword keyword) noexcept : isAuto (keyword == GridItem::Keyword::autoValue)
 | 
			
		||||
{
 | 
			
		||||
    jassert (keyword == GridItem::Keyword::autoValue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (const char* lineNameToUse) noexcept : GridItem::Property (String (lineNameToUse))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (const String& lineNameToUse) noexcept : name (lineNameToUse), number (1)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (int numberToUse) noexcept : number (numberToUse)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (int numberToUse, const String& lineNameToUse) noexcept
 | 
			
		||||
    : name (lineNameToUse), number (numberToUse)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (Span spanToUse) noexcept
 | 
			
		||||
    : name (spanToUse.name), number (spanToUse.number), isSpan (true)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
GridItem::Margin::Margin() noexcept : left(), right(), top(), bottom() {}
 | 
			
		||||
 | 
			
		||||
GridItem::Margin::Margin (int v) noexcept : GridItem::Margin::Margin (static_cast<float> (v)) {}
 | 
			
		||||
 | 
			
		||||
GridItem::Margin::Margin (float v) noexcept : left (v), right (v), top (v), bottom (v) {}
 | 
			
		||||
 | 
			
		||||
GridItem::Margin::Margin (float t, float r, float b, float l) noexcept : left (l), right (r), top (t), bottom (b) {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
GridItem::GridItem() noexcept {}
 | 
			
		||||
GridItem::~GridItem() noexcept {}
 | 
			
		||||
 | 
			
		||||
GridItem::GridItem (Component& componentToUse) noexcept  : associatedComponent (&componentToUse) {}
 | 
			
		||||
GridItem::GridItem (Component* componentToUse) noexcept  : associatedComponent (componentToUse) {}
 | 
			
		||||
 | 
			
		||||
void GridItem::setArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd)
 | 
			
		||||
{
 | 
			
		||||
    column.start = columnStart;
 | 
			
		||||
    column.end = columnEnd;
 | 
			
		||||
    row.start = rowStart;
 | 
			
		||||
    row.end = rowEnd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GridItem::setArea (Property rowStart, Property columnStart)
 | 
			
		||||
{
 | 
			
		||||
    column.start = columnStart;
 | 
			
		||||
    row.start = rowStart;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GridItem::setArea (const String& areaName)
 | 
			
		||||
{
 | 
			
		||||
    area = areaName;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.setArea (rowStart, columnStart, rowEnd, columnEnd);
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withArea (Property rowStart, Property columnStart) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.setArea (rowStart, columnStart);
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withArea (const String& areaName) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.setArea (areaName);
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withRow (StartAndEndProperty newRow) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.row = newRow;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withColumn (StartAndEndProperty newColumn) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.column = newColumn;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withAlignSelf (AlignSelf newAlignSelf) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.alignSelf = newAlignSelf;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withJustifySelf (JustifySelf newJustifySelf) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.justifySelf = newJustifySelf;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withWidth (float newWidth) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.width = newWidth;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withHeight (float newHeight) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.height = newHeight;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withSize (float newWidth, float newHeight) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.width = newWidth;
 | 
			
		||||
    gi.height = newHeight;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withMargin (Margin newHeight) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.margin = newHeight;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withOrder (int newOrder) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.order = newOrder;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property() noexcept : isAuto (true)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (GridItem::Keyword keyword) noexcept : isAuto (keyword == GridItem::Keyword::autoValue)
 | 
			
		||||
{
 | 
			
		||||
    jassert (keyword == GridItem::Keyword::autoValue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (const char* lineNameToUse) noexcept : GridItem::Property (String (lineNameToUse))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (const String& lineNameToUse) noexcept : name (lineNameToUse), number (1)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (int numberToUse) noexcept : number (numberToUse)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (int numberToUse, const String& lineNameToUse) noexcept
 | 
			
		||||
    : name (lineNameToUse), number (numberToUse)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem::Property::Property (Span spanToUse) noexcept
 | 
			
		||||
    : name (spanToUse.name), number (spanToUse.number), isSpan (true)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
GridItem::Margin::Margin() noexcept : left(), right(), top(), bottom() {}
 | 
			
		||||
 | 
			
		||||
GridItem::Margin::Margin (int v) noexcept : GridItem::Margin::Margin (static_cast<float> (v)) {}
 | 
			
		||||
 | 
			
		||||
GridItem::Margin::Margin (float v) noexcept : left (v), right (v), top (v), bottom (v) {}
 | 
			
		||||
 | 
			
		||||
GridItem::Margin::Margin (float t, float r, float b, float l) noexcept : left (l), right (r), top (t), bottom (b) {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
GridItem::GridItem() noexcept = default;
 | 
			
		||||
GridItem::GridItem (Component& componentToUse) noexcept  : associatedComponent (&componentToUse) {}
 | 
			
		||||
GridItem::GridItem (Component* componentToUse) noexcept  : associatedComponent (componentToUse) {}
 | 
			
		||||
 | 
			
		||||
void GridItem::setArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd)
 | 
			
		||||
{
 | 
			
		||||
    column.start = columnStart;
 | 
			
		||||
    column.end = columnEnd;
 | 
			
		||||
    row.start = rowStart;
 | 
			
		||||
    row.end = rowEnd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GridItem::setArea (Property rowStart, Property columnStart)
 | 
			
		||||
{
 | 
			
		||||
    column.start = columnStart;
 | 
			
		||||
    row.start = rowStart;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GridItem::setArea (const String& areaName)
 | 
			
		||||
{
 | 
			
		||||
    area = areaName;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.setArea (rowStart, columnStart, rowEnd, columnEnd);
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withArea (Property rowStart, Property columnStart) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.setArea (rowStart, columnStart);
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withArea (const String& areaName) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.setArea (areaName);
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withRow (StartAndEndProperty newRow) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.row = newRow;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withColumn (StartAndEndProperty newColumn) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.column = newColumn;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withAlignSelf (AlignSelf newAlignSelf) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.alignSelf = newAlignSelf;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withJustifySelf (JustifySelf newJustifySelf) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.justifySelf = newJustifySelf;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withWidth (float newWidth) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.width = newWidth;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withHeight (float newHeight) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.height = newHeight;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withSize (float newWidth, float newHeight) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.width = newWidth;
 | 
			
		||||
    gi.height = newHeight;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withMargin (Margin newHeight) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.margin = newHeight;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GridItem GridItem::withOrder (int newOrder) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto gi = *this;
 | 
			
		||||
    gi.order = newOrder;
 | 
			
		||||
    return gi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,243 +1,240 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Defines an item in a Grid
 | 
			
		||||
    @see Grid
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  GridItem
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    enum class Keyword { autoValue };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents a span. */
 | 
			
		||||
    struct Span
 | 
			
		||||
    {
 | 
			
		||||
        explicit Span (int numberToUse) noexcept : number (numberToUse)
 | 
			
		||||
        {
 | 
			
		||||
            /* Span must be at least one and positive */
 | 
			
		||||
            jassert (numberToUse > 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        explicit Span (int numberToUse, const String& nameToUse) : Span (numberToUse)
 | 
			
		||||
        {
 | 
			
		||||
            /* Name must not be empty */
 | 
			
		||||
            jassert (nameToUse.isNotEmpty());
 | 
			
		||||
            name = nameToUse;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        explicit Span (const String& nameToUse) : name (nameToUse)
 | 
			
		||||
        {
 | 
			
		||||
            /* Name must not be empty */
 | 
			
		||||
            jassert (nameToUse.isNotEmpty());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int number = 1;
 | 
			
		||||
        String name;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents a property. */
 | 
			
		||||
    struct Property
 | 
			
		||||
    {
 | 
			
		||||
        Property() noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (Keyword keyword) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (const char* lineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (const String& lineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (int numberToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (int numberToUse, const String& lineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (Span spanToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        bool hasSpan() const noexcept          { return isSpan && ! isAuto; }
 | 
			
		||||
        bool hasAbsolute() const noexcept      { return ! (isSpan || isAuto);  }
 | 
			
		||||
        bool hasAuto() const noexcept          { return isAuto; }
 | 
			
		||||
        bool hasName() const noexcept          { return name.isNotEmpty(); }
 | 
			
		||||
        const String& getName() const noexcept { return name; }
 | 
			
		||||
        int getNumber() const noexcept         { return number; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        String name;
 | 
			
		||||
        int number = 1; /** Either an absolute line number or number of lines to span across. */
 | 
			
		||||
        bool isSpan = false;
 | 
			
		||||
        bool isAuto = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents start and end properties. */
 | 
			
		||||
    struct StartAndEndProperty { Property start, end; };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Possible values for the justifySelf property. */
 | 
			
		||||
    enum class JustifySelf : int
 | 
			
		||||
    {
 | 
			
		||||
        start = 0,               /**< Content inside the item is justified towards the left. */
 | 
			
		||||
        end,                     /**< Content inside the item is justified towards the right. */
 | 
			
		||||
        center,                  /**< Content inside the item is justified towards the center. */
 | 
			
		||||
        stretch,                 /**< Content inside the item is stretched from left to right. */
 | 
			
		||||
        autoValue                /**< Follows the Grid container's justifyItems property. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignSelf property. */
 | 
			
		||||
    enum class AlignSelf   : int
 | 
			
		||||
    {
 | 
			
		||||
        start = 0,               /**< Content inside the item is aligned towards the top. */
 | 
			
		||||
        end,                     /**< Content inside the item is aligned towards the bottom. */
 | 
			
		||||
        center,                  /**< Content inside the item is aligned towards the center. */
 | 
			
		||||
        stretch,                 /**< Content inside the item is stretched from top to bottom. */
 | 
			
		||||
        autoValue                /**< Follows the Grid container's alignItems property. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Creates an item with default parameters. */
 | 
			
		||||
    GridItem() noexcept;
 | 
			
		||||
    /** Creates an item with a given Component to use. */
 | 
			
		||||
    GridItem (Component& componentToUse) noexcept;
 | 
			
		||||
    /** Creates an item with a given Component to use. */
 | 
			
		||||
    GridItem (Component* componentToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~GridItem() noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** If this is non-null, it represents a Component whose bounds are controlled by this item. */
 | 
			
		||||
    Component* associatedComponent = nullptr;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Determines the order used to lay out items in their grid container. */
 | 
			
		||||
    int order = 0;
 | 
			
		||||
 | 
			
		||||
    /** This is the justify-self property of the item.
 | 
			
		||||
        This determines the alignment of the item along the row.
 | 
			
		||||
    */
 | 
			
		||||
    JustifySelf  justifySelf = JustifySelf::autoValue;
 | 
			
		||||
 | 
			
		||||
    /** This is the align-self property of the item.
 | 
			
		||||
        This determines the alignment of the item along the column.
 | 
			
		||||
    */
 | 
			
		||||
    AlignSelf    alignSelf   = AlignSelf::autoValue;
 | 
			
		||||
 | 
			
		||||
    /** These are the start and end properties of the column. */
 | 
			
		||||
    StartAndEndProperty column = { Keyword::autoValue, Keyword::autoValue };
 | 
			
		||||
 | 
			
		||||
    /** These are the start and end properties of the row. */
 | 
			
		||||
    StartAndEndProperty row    = { Keyword::autoValue, Keyword::autoValue };
 | 
			
		||||
 | 
			
		||||
    /** */
 | 
			
		||||
    String area;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        useDefaultValue = -2, /* TODO: useDefaultValue should be named useAuto */
 | 
			
		||||
        notAssigned = -1
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* TODO: move all of this into a common class that is shared with the FlexItem */
 | 
			
		||||
    float width    = notAssigned;
 | 
			
		||||
    float minWidth = 0.0f;
 | 
			
		||||
    float maxWidth = notAssigned;
 | 
			
		||||
 | 
			
		||||
    float height    = notAssigned;
 | 
			
		||||
    float minHeight = 0.0f;
 | 
			
		||||
    float maxHeight = notAssigned;
 | 
			
		||||
 | 
			
		||||
    /** Represents a margin. */
 | 
			
		||||
    struct Margin
 | 
			
		||||
    {
 | 
			
		||||
        Margin() noexcept;
 | 
			
		||||
        Margin (int size) noexcept;
 | 
			
		||||
        Margin (float size) noexcept;
 | 
			
		||||
        Margin (float top, float right, float bottom, float left) noexcept;   /**< Creates a margin with these sizes. */
 | 
			
		||||
 | 
			
		||||
        float left;
 | 
			
		||||
        float right;
 | 
			
		||||
        float top;
 | 
			
		||||
        float bottom;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** The margin to leave around this item. */
 | 
			
		||||
    Margin margin;
 | 
			
		||||
 | 
			
		||||
    /** The item's current bounds. */
 | 
			
		||||
    Rectangle<float> currentBounds;
 | 
			
		||||
 | 
			
		||||
    /** Short-hand */
 | 
			
		||||
    void setArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd);
 | 
			
		||||
 | 
			
		||||
    /** Short-hand, span of 1 by default */
 | 
			
		||||
    void setArea (Property rowStart, Property columnStart);
 | 
			
		||||
 | 
			
		||||
    /** Short-hand */
 | 
			
		||||
    void setArea (const String& areaName);
 | 
			
		||||
 | 
			
		||||
    /** Short-hand */
 | 
			
		||||
    GridItem withArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Short-hand, span of 1 by default */
 | 
			
		||||
    GridItem withArea (Property rowStart, Property columnStart) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Short-hand */
 | 
			
		||||
    GridItem withArea (const String& areaName)  const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new row property. */
 | 
			
		||||
    GridItem withRow (StartAndEndProperty row) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new column property. */
 | 
			
		||||
    GridItem withColumn (StartAndEndProperty column) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new alignSelf property. */
 | 
			
		||||
    GridItem withAlignSelf (AlignSelf newAlignSelf) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new justifySelf property. */
 | 
			
		||||
    GridItem withJustifySelf (JustifySelf newJustifySelf) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new width. */
 | 
			
		||||
    GridItem withWidth (float newWidth) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new height. */
 | 
			
		||||
    GridItem withHeight (float newHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new size. */
 | 
			
		||||
    GridItem withSize (float newWidth, float newHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new margin. */
 | 
			
		||||
    GridItem withMargin (Margin newMargin) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new order. */
 | 
			
		||||
    GridItem withOrder (int newOrder) const noexcept;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    Defines an item in a Grid
 | 
			
		||||
    @see Grid
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  GridItem
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    enum class Keyword { autoValue };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents a span. */
 | 
			
		||||
    struct Span
 | 
			
		||||
    {
 | 
			
		||||
        explicit Span (int numberToUse) noexcept : number (numberToUse)
 | 
			
		||||
        {
 | 
			
		||||
            /* Span must be at least one and positive */
 | 
			
		||||
            jassert (numberToUse > 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        explicit Span (int numberToUse, const String& nameToUse) : Span (numberToUse)
 | 
			
		||||
        {
 | 
			
		||||
            /* Name must not be empty */
 | 
			
		||||
            jassert (nameToUse.isNotEmpty());
 | 
			
		||||
            name = nameToUse;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        explicit Span (const String& nameToUse) : name (nameToUse)
 | 
			
		||||
        {
 | 
			
		||||
            /* Name must not be empty */
 | 
			
		||||
            jassert (nameToUse.isNotEmpty());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int number = 1;
 | 
			
		||||
        String name;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents a property. */
 | 
			
		||||
    struct Property
 | 
			
		||||
    {
 | 
			
		||||
        Property() noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (Keyword keyword) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (const char* lineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (const String& lineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (int numberToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (int numberToUse, const String& lineNameToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        Property (Span spanToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
        bool hasSpan() const noexcept          { return isSpan && ! isAuto; }
 | 
			
		||||
        bool hasAbsolute() const noexcept      { return ! (isSpan || isAuto);  }
 | 
			
		||||
        bool hasAuto() const noexcept          { return isAuto; }
 | 
			
		||||
        bool hasName() const noexcept          { return name.isNotEmpty(); }
 | 
			
		||||
        const String& getName() const noexcept { return name; }
 | 
			
		||||
        int getNumber() const noexcept         { return number; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        String name;
 | 
			
		||||
        int number = 1; /** Either an absolute line number or number of lines to span across. */
 | 
			
		||||
        bool isSpan = false;
 | 
			
		||||
        bool isAuto = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents start and end properties. */
 | 
			
		||||
    struct StartAndEndProperty { Property start, end; };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Possible values for the justifySelf property. */
 | 
			
		||||
    enum class JustifySelf : int
 | 
			
		||||
    {
 | 
			
		||||
        start = 0,               /**< Content inside the item is justified towards the left. */
 | 
			
		||||
        end,                     /**< Content inside the item is justified towards the right. */
 | 
			
		||||
        center,                  /**< Content inside the item is justified towards the center. */
 | 
			
		||||
        stretch,                 /**< Content inside the item is stretched from left to right. */
 | 
			
		||||
        autoValue                /**< Follows the Grid container's justifyItems property. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Possible values for the alignSelf property. */
 | 
			
		||||
    enum class AlignSelf   : int
 | 
			
		||||
    {
 | 
			
		||||
        start = 0,               /**< Content inside the item is aligned towards the top. */
 | 
			
		||||
        end,                     /**< Content inside the item is aligned towards the bottom. */
 | 
			
		||||
        center,                  /**< Content inside the item is aligned towards the center. */
 | 
			
		||||
        stretch,                 /**< Content inside the item is stretched from top to bottom. */
 | 
			
		||||
        autoValue                /**< Follows the Grid container's alignItems property. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Creates an item with default parameters. */
 | 
			
		||||
    GridItem() noexcept;
 | 
			
		||||
    /** Creates an item with a given Component to use. */
 | 
			
		||||
    GridItem (Component& componentToUse) noexcept;
 | 
			
		||||
    /** Creates an item with a given Component to use. */
 | 
			
		||||
    GridItem (Component* componentToUse) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** If this is non-null, it represents a Component whose bounds are controlled by this item. */
 | 
			
		||||
    Component* associatedComponent = nullptr;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Determines the order used to lay out items in their grid container. */
 | 
			
		||||
    int order = 0;
 | 
			
		||||
 | 
			
		||||
    /** This is the justify-self property of the item.
 | 
			
		||||
        This determines the alignment of the item along the row.
 | 
			
		||||
    */
 | 
			
		||||
    JustifySelf  justifySelf = JustifySelf::autoValue;
 | 
			
		||||
 | 
			
		||||
    /** This is the align-self property of the item.
 | 
			
		||||
        This determines the alignment of the item along the column.
 | 
			
		||||
    */
 | 
			
		||||
    AlignSelf    alignSelf   = AlignSelf::autoValue;
 | 
			
		||||
 | 
			
		||||
    /** These are the start and end properties of the column. */
 | 
			
		||||
    StartAndEndProperty column = { Keyword::autoValue, Keyword::autoValue };
 | 
			
		||||
 | 
			
		||||
    /** These are the start and end properties of the row. */
 | 
			
		||||
    StartAndEndProperty row    = { Keyword::autoValue, Keyword::autoValue };
 | 
			
		||||
 | 
			
		||||
    /** */
 | 
			
		||||
    String area;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        useDefaultValue = -2, /* TODO: useDefaultValue should be named useAuto */
 | 
			
		||||
        notAssigned = -1
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* TODO: move all of this into a common class that is shared with the FlexItem */
 | 
			
		||||
    float width    = notAssigned;
 | 
			
		||||
    float minWidth = 0.0f;
 | 
			
		||||
    float maxWidth = notAssigned;
 | 
			
		||||
 | 
			
		||||
    float height    = notAssigned;
 | 
			
		||||
    float minHeight = 0.0f;
 | 
			
		||||
    float maxHeight = notAssigned;
 | 
			
		||||
 | 
			
		||||
    /** Represents a margin. */
 | 
			
		||||
    struct Margin
 | 
			
		||||
    {
 | 
			
		||||
        Margin() noexcept;
 | 
			
		||||
        Margin (int size) noexcept;
 | 
			
		||||
        Margin (float size) noexcept;
 | 
			
		||||
        Margin (float top, float right, float bottom, float left) noexcept;   /**< Creates a margin with these sizes. */
 | 
			
		||||
 | 
			
		||||
        float left;
 | 
			
		||||
        float right;
 | 
			
		||||
        float top;
 | 
			
		||||
        float bottom;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** The margin to leave around this item. */
 | 
			
		||||
    Margin margin;
 | 
			
		||||
 | 
			
		||||
    /** The item's current bounds. */
 | 
			
		||||
    Rectangle<float> currentBounds;
 | 
			
		||||
 | 
			
		||||
    /** Short-hand */
 | 
			
		||||
    void setArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd);
 | 
			
		||||
 | 
			
		||||
    /** Short-hand, span of 1 by default */
 | 
			
		||||
    void setArea (Property rowStart, Property columnStart);
 | 
			
		||||
 | 
			
		||||
    /** Short-hand */
 | 
			
		||||
    void setArea (const String& areaName);
 | 
			
		||||
 | 
			
		||||
    /** Short-hand */
 | 
			
		||||
    GridItem withArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Short-hand, span of 1 by default */
 | 
			
		||||
    GridItem withArea (Property rowStart, Property columnStart) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Short-hand */
 | 
			
		||||
    GridItem withArea (const String& areaName)  const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new row property. */
 | 
			
		||||
    GridItem withRow (StartAndEndProperty row) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new column property. */
 | 
			
		||||
    GridItem withColumn (StartAndEndProperty column) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new alignSelf property. */
 | 
			
		||||
    GridItem withAlignSelf (AlignSelf newAlignSelf) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new justifySelf property. */
 | 
			
		||||
    GridItem withJustifySelf (JustifySelf newJustifySelf) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new width. */
 | 
			
		||||
    GridItem withWidth (float newWidth) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new height. */
 | 
			
		||||
    GridItem withHeight (float newHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new size. */
 | 
			
		||||
    GridItem withSize (float newWidth, float newHeight) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new margin. */
 | 
			
		||||
    GridItem withMargin (Margin newMargin) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns a copy of this object with a new order. */
 | 
			
		||||
    GridItem withOrder (int newOrder) const noexcept;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,78 +1,78 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
GroupComponent::GroupComponent (const String& name,
 | 
			
		||||
                                const String& labelText)
 | 
			
		||||
    : Component (name),
 | 
			
		||||
      text (labelText),
 | 
			
		||||
      justification (Justification::left)
 | 
			
		||||
{
 | 
			
		||||
    setInterceptsMouseClicks  (false, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GroupComponent::~GroupComponent() {}
 | 
			
		||||
 | 
			
		||||
void GroupComponent::setText (const String& newText)
 | 
			
		||||
{
 | 
			
		||||
    if (text != newText)
 | 
			
		||||
    {
 | 
			
		||||
        text = newText;
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String GroupComponent::getText() const
 | 
			
		||||
{
 | 
			
		||||
    return text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroupComponent::setTextLabelPosition (Justification newJustification)
 | 
			
		||||
{
 | 
			
		||||
    if (justification != newJustification)
 | 
			
		||||
    {
 | 
			
		||||
        justification = newJustification;
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroupComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawGroupComponentOutline (g, getWidth(), getHeight(),
 | 
			
		||||
                                                text, justification, *this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroupComponent::enablementChanged()    { repaint(); }
 | 
			
		||||
void GroupComponent::colourChanged()        { repaint(); }
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> GroupComponent::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
GroupComponent::GroupComponent (const String& name,
 | 
			
		||||
                                const String& labelText)
 | 
			
		||||
    : Component (name),
 | 
			
		||||
      text (labelText),
 | 
			
		||||
      justification (Justification::left)
 | 
			
		||||
{
 | 
			
		||||
    setInterceptsMouseClicks  (false, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GroupComponent::~GroupComponent() {}
 | 
			
		||||
 | 
			
		||||
void GroupComponent::setText (const String& newText)
 | 
			
		||||
{
 | 
			
		||||
    if (text != newText)
 | 
			
		||||
    {
 | 
			
		||||
        text = newText;
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String GroupComponent::getText() const
 | 
			
		||||
{
 | 
			
		||||
    return text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroupComponent::setTextLabelPosition (Justification newJustification)
 | 
			
		||||
{
 | 
			
		||||
    if (justification != newJustification)
 | 
			
		||||
    {
 | 
			
		||||
        justification = newJustification;
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroupComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawGroupComponentOutline (g, getWidth(), getHeight(),
 | 
			
		||||
                                                text, justification, *this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GroupComponent::enablementChanged()    { repaint(); }
 | 
			
		||||
void GroupComponent::colourChanged()        { repaint(); }
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> GroupComponent::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,111 +1,111 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that draws an outline around itself and has an optional title at
 | 
			
		||||
    the top, for drawing an outline around a group of controls.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  GroupComponent    : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a GroupComponent.
 | 
			
		||||
 | 
			
		||||
        @param componentName    the name to give the component
 | 
			
		||||
        @param labelText        the text to show at the top of the outline
 | 
			
		||||
    */
 | 
			
		||||
    GroupComponent (const String& componentName = {},
 | 
			
		||||
                    const String& labelText = {});
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~GroupComponent() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the text that's shown at the top of the component. */
 | 
			
		||||
    void setText (const String& newText);
 | 
			
		||||
 | 
			
		||||
    /** Returns the currently displayed text label. */
 | 
			
		||||
    String getText() const;
 | 
			
		||||
 | 
			
		||||
    /** Sets the positioning of the text label.
 | 
			
		||||
        (The default is Justification::left)
 | 
			
		||||
        @see getTextLabelPosition
 | 
			
		||||
    */
 | 
			
		||||
    void setTextLabelPosition (Justification justification);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current text label position.
 | 
			
		||||
        @see setTextLabelPosition
 | 
			
		||||
    */
 | 
			
		||||
    Justification getTextLabelPosition() const noexcept           { return justification; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the component.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        outlineColourId     = 0x1005400,    /**< The colour to use for drawing the line around the edge. */
 | 
			
		||||
        textColourId        = 0x1005410     /**< The colour to use to draw the text label. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes. */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual void drawGroupComponentOutline (Graphics&, int w, int h, const String& text,
 | 
			
		||||
                                                const Justification&, GroupComponent&) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void enablementChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void colourChanged() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
 | 
			
		||||
    String text;
 | 
			
		||||
    Justification justification;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE (GroupComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that draws an outline around itself and has an optional title at
 | 
			
		||||
    the top, for drawing an outline around a group of controls.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  GroupComponent    : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a GroupComponent.
 | 
			
		||||
 | 
			
		||||
        @param componentName    the name to give the component
 | 
			
		||||
        @param labelText        the text to show at the top of the outline
 | 
			
		||||
    */
 | 
			
		||||
    GroupComponent (const String& componentName = {},
 | 
			
		||||
                    const String& labelText = {});
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~GroupComponent() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the text that's shown at the top of the component. */
 | 
			
		||||
    void setText (const String& newText);
 | 
			
		||||
 | 
			
		||||
    /** Returns the currently displayed text label. */
 | 
			
		||||
    String getText() const;
 | 
			
		||||
 | 
			
		||||
    /** Sets the positioning of the text label.
 | 
			
		||||
        (The default is Justification::left)
 | 
			
		||||
        @see getTextLabelPosition
 | 
			
		||||
    */
 | 
			
		||||
    void setTextLabelPosition (Justification justification);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current text label position.
 | 
			
		||||
        @see setTextLabelPosition
 | 
			
		||||
    */
 | 
			
		||||
    Justification getTextLabelPosition() const noexcept           { return justification; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the component.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        outlineColourId     = 0x1005400,    /**< The colour to use for drawing the line around the edge. */
 | 
			
		||||
        textColourId        = 0x1005410     /**< The colour to use to draw the text label. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes. */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual void drawGroupComponentOutline (Graphics&, int w, int h, const String& text,
 | 
			
		||||
                                                const Justification&, GroupComponent&) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void enablementChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void colourChanged() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
 | 
			
		||||
    String text;
 | 
			
		||||
    Justification justification;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE (GroupComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,376 +1,381 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class MultiDocumentPanel;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    This is a derivative of DocumentWindow that is used inside a MultiDocumentPanel
 | 
			
		||||
    component.
 | 
			
		||||
 | 
			
		||||
    It's like a normal DocumentWindow but has some extra functionality to make sure
 | 
			
		||||
    everything works nicely inside a MultiDocumentPanel.
 | 
			
		||||
 | 
			
		||||
    @see MultiDocumentPanel
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MultiDocumentPanelWindow  : public DocumentWindow
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
    */
 | 
			
		||||
    MultiDocumentPanelWindow (Colour backgroundColour);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~MultiDocumentPanelWindow() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void maximiseButtonPressed() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void closeButtonPressed() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void activeWindowStatusChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void broughtToFront() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void updateOrder();
 | 
			
		||||
    MultiDocumentPanel* getOwner() const noexcept;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiDocumentPanelWindow)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that contains a set of other components either in floating windows
 | 
			
		||||
    or tabs.
 | 
			
		||||
 | 
			
		||||
    This acts as a panel that can be used to hold a set of open document windows, with
 | 
			
		||||
    different layout modes.
 | 
			
		||||
 | 
			
		||||
    Use addDocument() and closeDocument() to add or remove components from the
 | 
			
		||||
    panel - never use any of the Component methods to access the panel's child
 | 
			
		||||
    components directly, as these are managed internally.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MultiDocumentPanel  : public Component,
 | 
			
		||||
                                      private ComponentListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty panel.
 | 
			
		||||
 | 
			
		||||
        Use addDocument() and closeDocument() to add or remove components from the
 | 
			
		||||
        panel - never use any of the Component methods to access the panel's child
 | 
			
		||||
        components directly, as these are managed internally.
 | 
			
		||||
    */
 | 
			
		||||
    MultiDocumentPanel();
 | 
			
		||||
 | 
			
		||||
    /** Destructor.
 | 
			
		||||
 | 
			
		||||
        When deleted, this will call closeAllDocuments (false) to make sure all its
 | 
			
		||||
        components are deleted. If you need to make sure all documents are saved
 | 
			
		||||
        before closing, then you should call closeAllDocuments (true) and check that
 | 
			
		||||
        it returns true before deleting the panel.
 | 
			
		||||
    */
 | 
			
		||||
    ~MultiDocumentPanel() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
   #if JUCE_MODAL_LOOPS_PERMITTED
 | 
			
		||||
    /** Tries to close all the documents.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is true, then the tryToCloseDocument() method will
 | 
			
		||||
        be called for each open document, and any of these calls fails, this method
 | 
			
		||||
        will stop and return false, leaving some documents still open.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is false, then all documents will be closed
 | 
			
		||||
        unconditionally.
 | 
			
		||||
 | 
			
		||||
        @see closeDocument
 | 
			
		||||
    */
 | 
			
		||||
    bool closeAllDocuments (bool checkItsOkToCloseFirst);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    /** Tries to close all the documents.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is true, then the tryToCloseDocumentAsync() method will
 | 
			
		||||
        be called for each open document, and any of these calls fails, this method
 | 
			
		||||
        will stop and provide an argument of false to the callback, leaving some documents
 | 
			
		||||
        still open.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is false, then all documents will be closed
 | 
			
		||||
        unconditionally.
 | 
			
		||||
 | 
			
		||||
        @see closeDocument
 | 
			
		||||
    */
 | 
			
		||||
    void closeAllDocumentsAsync (bool checkItsOkToCloseFirst,
 | 
			
		||||
                                 std::function<void (bool)> callback);
 | 
			
		||||
 | 
			
		||||
    /** Adds a document component to the panel.
 | 
			
		||||
 | 
			
		||||
        If the number of documents would exceed the limit set by setMaximumNumDocuments() then
 | 
			
		||||
        this will fail and return false. (If it does fail, the component passed-in will not be
 | 
			
		||||
        deleted, even if deleteWhenRemoved was set to true).
 | 
			
		||||
 | 
			
		||||
        The MultiDocumentPanel will deal with creating a window border to go around your component,
 | 
			
		||||
        so just pass in the bare content component here, no need to give it a ResizableWindow
 | 
			
		||||
        or DocumentWindow.
 | 
			
		||||
 | 
			
		||||
        @param component            the component to add
 | 
			
		||||
        @param backgroundColour     the background colour to use to fill the component's
 | 
			
		||||
                                    window or tab
 | 
			
		||||
        @param deleteWhenRemoved    if true, then when the component is removed by closeDocument()
 | 
			
		||||
                                    or closeAllDocuments(), then it will be deleted. If false, then
 | 
			
		||||
                                    the caller must handle the component's deletion
 | 
			
		||||
    */
 | 
			
		||||
    bool addDocument (Component* component,
 | 
			
		||||
                      Colour backgroundColour,
 | 
			
		||||
                      bool deleteWhenRemoved);
 | 
			
		||||
 | 
			
		||||
   #if JUCE_MODAL_LOOPS_PERMITTED
 | 
			
		||||
    /** Closes one of the documents.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is true, then the tryToCloseDocument() method will
 | 
			
		||||
        be called, and if it fails, this method will return false without closing the
 | 
			
		||||
        document.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is false, then the documents will be closed
 | 
			
		||||
        unconditionally.
 | 
			
		||||
 | 
			
		||||
        The component will be deleted if the deleteWhenRemoved parameter was set to
 | 
			
		||||
        true when it was added with addDocument.
 | 
			
		||||
 | 
			
		||||
        @see addDocument, closeAllDocuments
 | 
			
		||||
    */
 | 
			
		||||
    bool closeDocument (Component* component,
 | 
			
		||||
                        bool checkItsOkToCloseFirst);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    /** Closes one of the documents.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is true, then the tryToCloseDocumentAsync() method will
 | 
			
		||||
        be called, and if it fails, this method will call the callback with a false
 | 
			
		||||
        argument without closing the document.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is false, then the documents will be closed
 | 
			
		||||
        unconditionally.
 | 
			
		||||
 | 
			
		||||
        The component will be deleted if the deleteWhenRemoved parameter was set to
 | 
			
		||||
        true when it was added with addDocument.
 | 
			
		||||
 | 
			
		||||
        @see addDocument, closeAllDocuments
 | 
			
		||||
    */
 | 
			
		||||
    void closeDocumentAsync (Component* component,
 | 
			
		||||
                             bool checkItsOkToCloseFirst,
 | 
			
		||||
                             std::function<void (bool)> callback);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of open document windows.
 | 
			
		||||
 | 
			
		||||
        @see getDocument
 | 
			
		||||
    */
 | 
			
		||||
    int getNumDocuments() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the open documents.
 | 
			
		||||
 | 
			
		||||
        The order of the documents in this array may change when they are added, removed
 | 
			
		||||
        or moved around.
 | 
			
		||||
 | 
			
		||||
        @see getNumDocuments
 | 
			
		||||
    */
 | 
			
		||||
    Component* getDocument (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the document component that is currently focused or on top.
 | 
			
		||||
 | 
			
		||||
        If currently using floating windows, then this will be the component in the currently
 | 
			
		||||
        active window, or the top component if none are active.
 | 
			
		||||
 | 
			
		||||
        If it's currently in tabbed mode, then it'll return the component in the active tab.
 | 
			
		||||
 | 
			
		||||
        @see setActiveDocument
 | 
			
		||||
    */
 | 
			
		||||
    Component* getActiveDocument() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Makes one of the components active and brings it to the top.
 | 
			
		||||
 | 
			
		||||
        @see getActiveDocument
 | 
			
		||||
    */
 | 
			
		||||
    void setActiveDocument (Component* component);
 | 
			
		||||
 | 
			
		||||
    /** Callback which gets invoked when the currently-active document changes. */
 | 
			
		||||
    virtual void activeDocumentChanged();
 | 
			
		||||
 | 
			
		||||
    /** Sets a limit on how many windows can be open at once.
 | 
			
		||||
 | 
			
		||||
        If this is zero or less there's no limit (the default). addDocument() will fail
 | 
			
		||||
        if this number is exceeded.
 | 
			
		||||
    */
 | 
			
		||||
    void setMaximumNumDocuments (int maximumNumDocuments);
 | 
			
		||||
 | 
			
		||||
    /** Sets an option to make the document fullscreen if there's only one document open.
 | 
			
		||||
 | 
			
		||||
        If set to true, then if there's only one document, it'll fill the whole of this
 | 
			
		||||
        component without tabs or a window border. If false, then tabs or a window
 | 
			
		||||
        will always be shown, even if there's only one document. If there's more than
 | 
			
		||||
        one document open, then this option makes no difference.
 | 
			
		||||
    */
 | 
			
		||||
    void useFullscreenWhenOneDocument (bool shouldUseTabs);
 | 
			
		||||
 | 
			
		||||
    /** Returns the result of the last time useFullscreenWhenOneDocument() was called.
 | 
			
		||||
    */
 | 
			
		||||
    bool isFullscreenWhenOneDocument() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The different layout modes available. */
 | 
			
		||||
    enum LayoutMode
 | 
			
		||||
    {
 | 
			
		||||
        FloatingWindows,            /**< In this mode, there are overlapping DocumentWindow components for each document. */
 | 
			
		||||
        MaximisedWindowsWithTabs    /**< In this mode, a TabbedComponent is used to show one document at a time. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Changes the panel's mode.
 | 
			
		||||
 | 
			
		||||
        @see LayoutMode, getLayoutMode
 | 
			
		||||
    */
 | 
			
		||||
    void setLayoutMode (LayoutMode newLayoutMode);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current layout mode. */
 | 
			
		||||
    LayoutMode getLayoutMode() const noexcept                           { return mode; }
 | 
			
		||||
 | 
			
		||||
    /** Sets the background colour for the whole panel.
 | 
			
		||||
 | 
			
		||||
        Each document has its own background colour, but this is the one used to fill the areas
 | 
			
		||||
        behind them.
 | 
			
		||||
    */
 | 
			
		||||
    void setBackgroundColour (Colour newBackgroundColour);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current background colour.
 | 
			
		||||
 | 
			
		||||
        @see setBackgroundColour
 | 
			
		||||
    */
 | 
			
		||||
    Colour getBackgroundColour() const noexcept                         { return backgroundColour; }
 | 
			
		||||
 | 
			
		||||
    /** If the panel is being used in tabbed mode, this returns the TabbedComponent that's involved. */
 | 
			
		||||
    TabbedComponent* getCurrentTabbedComponent() const noexcept         { return tabComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
   #if JUCE_MODAL_LOOPS_PERMITTED
 | 
			
		||||
    /** A subclass must override this to say whether its currently ok for a document
 | 
			
		||||
        to be closed.
 | 
			
		||||
 | 
			
		||||
        This method is called by closeDocument() and closeAllDocuments() to indicate that
 | 
			
		||||
        a document should be saved if possible, ready for it to be closed.
 | 
			
		||||
 | 
			
		||||
        If this method returns true, then it means the document is ok and can be closed.
 | 
			
		||||
 | 
			
		||||
        If it returns false, then it means that the closeDocument() method should stop
 | 
			
		||||
        and not close.
 | 
			
		||||
 | 
			
		||||
        Normally, you'd use this method to ask the user if they want to save any changes,
 | 
			
		||||
        then return true if the save operation went ok. If the user cancelled the save
 | 
			
		||||
        operation you could return false here to abort the close operation.
 | 
			
		||||
 | 
			
		||||
        If your component is based on the FileBasedDocument class, then you'd probably want
 | 
			
		||||
        to call FileBasedDocument::saveIfNeededAndUserAgrees() and return true if this returned
 | 
			
		||||
        FileBasedDocument::savedOk
 | 
			
		||||
 | 
			
		||||
        @see closeDocument, FileBasedDocument::saveIfNeededAndUserAgrees()
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool tryToCloseDocument (Component* component);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    /** A subclass must override this to say whether its currently ok for a document
 | 
			
		||||
        to be closed.
 | 
			
		||||
 | 
			
		||||
        This method is called by closeDocumentAsync() and closeAllDocumentsAsync()
 | 
			
		||||
        to indicate that a document should be saved if possible, ready for it to be closed.
 | 
			
		||||
 | 
			
		||||
        If the callback is called with a true argument, then it means the document is ok
 | 
			
		||||
        and can be closed.
 | 
			
		||||
 | 
			
		||||
        If the callback is called with a false argument, then it means that the
 | 
			
		||||
        closeDocumentAsync() method should stop and not close.
 | 
			
		||||
 | 
			
		||||
        Normally, you'd use this method to ask the user if they want to save any changes,
 | 
			
		||||
        then return true if the save operation went ok. If the user cancelled the save
 | 
			
		||||
        operation you could give a value of false to the callback to abort the close operation.
 | 
			
		||||
 | 
			
		||||
        If your component is based on the FileBasedDocument class, then you'd probably want
 | 
			
		||||
        to call FileBasedDocument::saveIfNeededAndUserAgreesAsync() and call the calback with
 | 
			
		||||
        true if this returned FileBasedDocument::savedOk.
 | 
			
		||||
 | 
			
		||||
        @see closeDocumentAsync, FileBasedDocument::saveIfNeededAndUserAgreesAsync()
 | 
			
		||||
    */
 | 
			
		||||
    virtual void tryToCloseDocumentAsync (Component* component, std::function<void (bool)> callback) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Creates a new window to be used for a document.
 | 
			
		||||
 | 
			
		||||
        The default implementation of this just returns a basic MultiDocumentPanelWindow object,
 | 
			
		||||
        but you might want to override it to return a custom component.
 | 
			
		||||
    */
 | 
			
		||||
    virtual MultiDocumentPanelWindow* createNewDocumentWindow();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentNameChanged (Component&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void closeDocumentInternal (Component*);
 | 
			
		||||
    static void closeLastDocumentRecursive (SafePointer<MultiDocumentPanel>,
 | 
			
		||||
                                            bool,
 | 
			
		||||
                                            std::function<void (bool)>);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct TabbedComponentInternal;
 | 
			
		||||
    friend class MultiDocumentPanelWindow;
 | 
			
		||||
 | 
			
		||||
    Component* getContainerComp (Component*) const;
 | 
			
		||||
    void updateOrder();
 | 
			
		||||
    void addWindow (Component*);
 | 
			
		||||
 | 
			
		||||
    LayoutMode mode = MaximisedWindowsWithTabs;
 | 
			
		||||
    Array<Component*> components;
 | 
			
		||||
    std::unique_ptr<TabbedComponent> tabComponent;
 | 
			
		||||
    Colour backgroundColour { Colours::lightblue };
 | 
			
		||||
    int maximumNumDocuments = 0, numDocsBeforeTabsUsed = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiDocumentPanel)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class MultiDocumentPanel;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    This is a derivative of DocumentWindow that is used inside a MultiDocumentPanel
 | 
			
		||||
    component.
 | 
			
		||||
 | 
			
		||||
    It's like a normal DocumentWindow but has some extra functionality to make sure
 | 
			
		||||
    everything works nicely inside a MultiDocumentPanel.
 | 
			
		||||
 | 
			
		||||
    @see MultiDocumentPanel
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MultiDocumentPanelWindow  : public DocumentWindow
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
    */
 | 
			
		||||
    MultiDocumentPanelWindow (Colour backgroundColour);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~MultiDocumentPanelWindow() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void maximiseButtonPressed() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void closeButtonPressed() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void activeWindowStatusChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void broughtToFront() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void updateActiveDocument();
 | 
			
		||||
    MultiDocumentPanel* getOwner() const noexcept;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiDocumentPanelWindow)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that contains a set of other components either in floating windows
 | 
			
		||||
    or tabs.
 | 
			
		||||
 | 
			
		||||
    This acts as a panel that can be used to hold a set of open document windows, with
 | 
			
		||||
    different layout modes.
 | 
			
		||||
 | 
			
		||||
    Use addDocument() and closeDocument() to add or remove components from the
 | 
			
		||||
    panel - never use any of the Component methods to access the panel's child
 | 
			
		||||
    components directly, as these are managed internally.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  MultiDocumentPanel  : public Component,
 | 
			
		||||
                                      private ComponentListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty panel.
 | 
			
		||||
 | 
			
		||||
        Use addDocument() and closeDocument() to add or remove components from the
 | 
			
		||||
        panel - never use any of the Component methods to access the panel's child
 | 
			
		||||
        components directly, as these are managed internally.
 | 
			
		||||
    */
 | 
			
		||||
    MultiDocumentPanel();
 | 
			
		||||
 | 
			
		||||
    /** Destructor.
 | 
			
		||||
 | 
			
		||||
        When deleted, this will call close all open documents to make sure all its
 | 
			
		||||
        components are deleted. If you need to make sure all documents are saved
 | 
			
		||||
        before closing, then you should call closeAllDocumentsAsync() with
 | 
			
		||||
        checkItsOkToCloseFirst == true and check the provided callback result is true
 | 
			
		||||
        before deleting the panel.
 | 
			
		||||
    */
 | 
			
		||||
    ~MultiDocumentPanel() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
   #if JUCE_MODAL_LOOPS_PERMITTED
 | 
			
		||||
    /** Tries to close all the documents.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is true, then the tryToCloseDocument() method will
 | 
			
		||||
        be called for each open document, and any of these calls fails, this method
 | 
			
		||||
        will stop and return false, leaving some documents still open.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is false, then all documents will be closed
 | 
			
		||||
        unconditionally.
 | 
			
		||||
 | 
			
		||||
        @see closeDocument
 | 
			
		||||
    */
 | 
			
		||||
    bool closeAllDocuments (bool checkItsOkToCloseFirst);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    /** Tries to close all the documents.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is true, then the tryToCloseDocumentAsync() method will
 | 
			
		||||
        be called for each open document, and any of these calls fails, this method
 | 
			
		||||
        will stop and provide an argument of false to the callback, leaving some documents
 | 
			
		||||
        still open.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is false, then all documents will be closed
 | 
			
		||||
        unconditionally.
 | 
			
		||||
 | 
			
		||||
        @see closeDocumentAsync
 | 
			
		||||
    */
 | 
			
		||||
    void closeAllDocumentsAsync (bool checkItsOkToCloseFirst,
 | 
			
		||||
                                 std::function<void (bool)> callback);
 | 
			
		||||
 | 
			
		||||
    /** Adds a document component to the panel.
 | 
			
		||||
 | 
			
		||||
        If the number of documents would exceed the limit set by setMaximumNumDocuments() then
 | 
			
		||||
        this will fail and return false. (If it does fail, the component passed-in will not be
 | 
			
		||||
        deleted, even if deleteWhenRemoved was set to true).
 | 
			
		||||
 | 
			
		||||
        The MultiDocumentPanel will deal with creating a window border to go around your component,
 | 
			
		||||
        so just pass in the bare content component here, no need to give it a ResizableWindow
 | 
			
		||||
        or DocumentWindow.
 | 
			
		||||
 | 
			
		||||
        @param component            the component to add
 | 
			
		||||
        @param backgroundColour     the background colour to use to fill the component's
 | 
			
		||||
                                    window or tab
 | 
			
		||||
        @param deleteWhenRemoved    if true, then when the component is removed by closeDocumentAsync()
 | 
			
		||||
                                    or closeAllDocumentsAsync(), then it will be deleted. If false, then
 | 
			
		||||
                                    the caller must handle the component's deletion
 | 
			
		||||
    */
 | 
			
		||||
    bool addDocument (Component* component,
 | 
			
		||||
                      Colour backgroundColour,
 | 
			
		||||
                      bool deleteWhenRemoved);
 | 
			
		||||
 | 
			
		||||
   #if JUCE_MODAL_LOOPS_PERMITTED
 | 
			
		||||
    /** Closes one of the documents.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is true, then the tryToCloseDocument() method will
 | 
			
		||||
        be called, and if it fails, this method will return false without closing the
 | 
			
		||||
        document.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is false, then the documents will be closed
 | 
			
		||||
        unconditionally.
 | 
			
		||||
 | 
			
		||||
        The component will be deleted if the deleteWhenRemoved parameter was set to
 | 
			
		||||
        true when it was added with addDocument.
 | 
			
		||||
 | 
			
		||||
        @see addDocument, closeAllDocuments
 | 
			
		||||
    */
 | 
			
		||||
    bool closeDocument (Component* component,
 | 
			
		||||
                        bool checkItsOkToCloseFirst);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    /** Closes one of the documents.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is true, then the tryToCloseDocumentAsync() method will
 | 
			
		||||
        be called, and if it fails, this method will call the callback with a false
 | 
			
		||||
        argument without closing the document.
 | 
			
		||||
 | 
			
		||||
        If checkItsOkToCloseFirst is false, then the documents will be closed
 | 
			
		||||
        unconditionally.
 | 
			
		||||
 | 
			
		||||
        The component will be deleted if the deleteWhenRemoved parameter was set to
 | 
			
		||||
        true when it was added with addDocument.
 | 
			
		||||
 | 
			
		||||
        @see addDocument, closeAllDocumentsAsync
 | 
			
		||||
    */
 | 
			
		||||
    void closeDocumentAsync (Component* component,
 | 
			
		||||
                             bool checkItsOkToCloseFirst,
 | 
			
		||||
                             std::function<void (bool)> callback);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of open document windows.
 | 
			
		||||
 | 
			
		||||
        @see getDocument
 | 
			
		||||
    */
 | 
			
		||||
    int getNumDocuments() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns one of the open documents.
 | 
			
		||||
 | 
			
		||||
        The order of the documents in this array may change when they are added, removed
 | 
			
		||||
        or moved around.
 | 
			
		||||
 | 
			
		||||
        @see getNumDocuments
 | 
			
		||||
    */
 | 
			
		||||
    Component* getDocument (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the document component that is currently focused or on top.
 | 
			
		||||
 | 
			
		||||
        If currently using floating windows, then this will be the component in the currently
 | 
			
		||||
        active window, or the top component if none are active.
 | 
			
		||||
 | 
			
		||||
        If it's currently in tabbed mode, then it'll return the component in the active tab.
 | 
			
		||||
 | 
			
		||||
        @see setActiveDocument
 | 
			
		||||
    */
 | 
			
		||||
    Component* getActiveDocument() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Makes one of the components active and brings it to the top.
 | 
			
		||||
 | 
			
		||||
        @see getActiveDocument
 | 
			
		||||
    */
 | 
			
		||||
    void setActiveDocument (Component* component);
 | 
			
		||||
 | 
			
		||||
    /** Callback which gets invoked when the currently-active document changes. */
 | 
			
		||||
    virtual void activeDocumentChanged();
 | 
			
		||||
 | 
			
		||||
    /** Sets a limit on how many windows can be open at once.
 | 
			
		||||
 | 
			
		||||
        If this is zero or less there's no limit (the default). addDocument() will fail
 | 
			
		||||
        if this number is exceeded.
 | 
			
		||||
    */
 | 
			
		||||
    void setMaximumNumDocuments (int maximumNumDocuments);
 | 
			
		||||
 | 
			
		||||
    /** Sets an option to make the document fullscreen if there's only one document open.
 | 
			
		||||
 | 
			
		||||
        If set to true, then if there's only one document, it'll fill the whole of this
 | 
			
		||||
        component without tabs or a window border. If false, then tabs or a window
 | 
			
		||||
        will always be shown, even if there's only one document. If there's more than
 | 
			
		||||
        one document open, then this option makes no difference.
 | 
			
		||||
    */
 | 
			
		||||
    void useFullscreenWhenOneDocument (bool shouldUseTabs);
 | 
			
		||||
 | 
			
		||||
    /** Returns the result of the last time useFullscreenWhenOneDocument() was called.
 | 
			
		||||
    */
 | 
			
		||||
    bool isFullscreenWhenOneDocument() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The different layout modes available. */
 | 
			
		||||
    enum LayoutMode
 | 
			
		||||
    {
 | 
			
		||||
        FloatingWindows,            /**< In this mode, there are overlapping DocumentWindow components for each document. */
 | 
			
		||||
        MaximisedWindowsWithTabs    /**< In this mode, a TabbedComponent is used to show one document at a time. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Changes the panel's mode.
 | 
			
		||||
 | 
			
		||||
        @see LayoutMode, getLayoutMode
 | 
			
		||||
    */
 | 
			
		||||
    void setLayoutMode (LayoutMode newLayoutMode);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current layout mode. */
 | 
			
		||||
    LayoutMode getLayoutMode() const noexcept                           { return mode; }
 | 
			
		||||
 | 
			
		||||
    /** Sets the background colour for the whole panel.
 | 
			
		||||
 | 
			
		||||
        Each document has its own background colour, but this is the one used to fill the areas
 | 
			
		||||
        behind them.
 | 
			
		||||
    */
 | 
			
		||||
    void setBackgroundColour (Colour newBackgroundColour);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current background colour.
 | 
			
		||||
 | 
			
		||||
        @see setBackgroundColour
 | 
			
		||||
    */
 | 
			
		||||
    Colour getBackgroundColour() const noexcept                         { return backgroundColour; }
 | 
			
		||||
 | 
			
		||||
    /** If the panel is being used in tabbed mode, this returns the TabbedComponent that's involved. */
 | 
			
		||||
    TabbedComponent* getCurrentTabbedComponent() const noexcept         { return tabComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
   #if JUCE_MODAL_LOOPS_PERMITTED
 | 
			
		||||
    /** A subclass must override this to say whether its currently ok for a document
 | 
			
		||||
        to be closed.
 | 
			
		||||
 | 
			
		||||
        This method is called by closeDocument() and closeAllDocuments() to indicate that
 | 
			
		||||
        a document should be saved if possible, ready for it to be closed.
 | 
			
		||||
 | 
			
		||||
        If this method returns true, then it means the document is ok and can be closed.
 | 
			
		||||
 | 
			
		||||
        If it returns false, then it means that the closeDocument() method should stop
 | 
			
		||||
        and not close.
 | 
			
		||||
 | 
			
		||||
        Normally, you'd use this method to ask the user if they want to save any changes,
 | 
			
		||||
        then return true if the save operation went ok. If the user cancelled the save
 | 
			
		||||
        operation you could return false here to abort the close operation.
 | 
			
		||||
 | 
			
		||||
        If your component is based on the FileBasedDocument class, then you'd probably want
 | 
			
		||||
        to call FileBasedDocument::saveIfNeededAndUserAgrees() and return true if this returned
 | 
			
		||||
        FileBasedDocument::savedOk
 | 
			
		||||
 | 
			
		||||
        @see closeDocument, FileBasedDocument::saveIfNeededAndUserAgrees()
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool tryToCloseDocument (Component* component);
 | 
			
		||||
   #endif
 | 
			
		||||
 | 
			
		||||
    /** A subclass must override this to say whether its currently ok for a document
 | 
			
		||||
        to be closed.
 | 
			
		||||
 | 
			
		||||
        This method is called by closeDocumentAsync() and closeAllDocumentsAsync()
 | 
			
		||||
        to indicate that a document should be saved if possible, ready for it to be closed.
 | 
			
		||||
 | 
			
		||||
        If the callback is called with a true argument, then it means the document is ok
 | 
			
		||||
        and can be closed.
 | 
			
		||||
 | 
			
		||||
        If the callback is called with a false argument, then it means that the
 | 
			
		||||
        closeDocumentAsync() method should stop and not close.
 | 
			
		||||
 | 
			
		||||
        Normally, you'd use this method to ask the user if they want to save any changes,
 | 
			
		||||
        then return true if the save operation went ok. If the user cancelled the save
 | 
			
		||||
        operation you could give a value of false to the callback to abort the close operation.
 | 
			
		||||
 | 
			
		||||
        If your component is based on the FileBasedDocument class, then you'd probably want
 | 
			
		||||
        to call FileBasedDocument::saveIfNeededAndUserAgreesAsync() and call the callback with
 | 
			
		||||
        true if this returned FileBasedDocument::savedOk.
 | 
			
		||||
 | 
			
		||||
        @see closeDocumentAsync, FileBasedDocument::saveIfNeededAndUserAgreesAsync()
 | 
			
		||||
    */
 | 
			
		||||
    virtual void tryToCloseDocumentAsync (Component* component, std::function<void (bool)> callback) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Creates a new window to be used for a document.
 | 
			
		||||
 | 
			
		||||
        The default implementation of this just returns a basic MultiDocumentPanelWindow object,
 | 
			
		||||
        but you might want to override it to return a custom component.
 | 
			
		||||
    */
 | 
			
		||||
    virtual MultiDocumentPanelWindow* createNewDocumentWindow();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentNameChanged (Component&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void closeDocumentInternal (Component*);
 | 
			
		||||
    static void closeLastDocumentRecursive (SafePointer<MultiDocumentPanel>,
 | 
			
		||||
                                            bool,
 | 
			
		||||
                                            std::function<void (bool)>);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct TabbedComponentInternal;
 | 
			
		||||
    friend class MultiDocumentPanelWindow;
 | 
			
		||||
 | 
			
		||||
    Component* getContainerComp (Component*) const;
 | 
			
		||||
    void updateActiveDocumentFromUIState();
 | 
			
		||||
    void updateActiveDocument (Component*);
 | 
			
		||||
    void addWindow (Component*);
 | 
			
		||||
    void recreateLayout();
 | 
			
		||||
 | 
			
		||||
    LayoutMode mode = MaximisedWindowsWithTabs;
 | 
			
		||||
    Array<Component*> components;
 | 
			
		||||
    Component* activeComponent = nullptr;
 | 
			
		||||
    bool isLayoutBeingChanged = false;
 | 
			
		||||
    std::unique_ptr<TabbedComponent> tabComponent;
 | 
			
		||||
    Colour backgroundColour { Colours::lightblue };
 | 
			
		||||
    int maximumNumDocuments = 0, numDocsBeforeTabsUsed = 0;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiDocumentPanel)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,195 +1,195 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ResizableBorderComponent::Zone::Zone() noexcept {}
 | 
			
		||||
ResizableBorderComponent::Zone::Zone (int zoneFlags) noexcept  : zone (zoneFlags) {}
 | 
			
		||||
ResizableBorderComponent::Zone::Zone (const ResizableBorderComponent::Zone& other) noexcept  : zone (other.zone) {}
 | 
			
		||||
 | 
			
		||||
ResizableBorderComponent::Zone& ResizableBorderComponent::Zone::operator= (const ResizableBorderComponent::Zone& other) noexcept
 | 
			
		||||
{
 | 
			
		||||
    zone = other.zone;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ResizableBorderComponent::Zone::operator== (const ResizableBorderComponent::Zone& other) const noexcept      { return zone == other.zone; }
 | 
			
		||||
bool ResizableBorderComponent::Zone::operator!= (const ResizableBorderComponent::Zone& other) const noexcept      { return zone != other.zone; }
 | 
			
		||||
 | 
			
		||||
ResizableBorderComponent::Zone ResizableBorderComponent::Zone::fromPositionOnBorder (Rectangle<int> totalSize,
 | 
			
		||||
                                                                                     BorderSize<int> border,
 | 
			
		||||
                                                                                     Point<int> position)
 | 
			
		||||
{
 | 
			
		||||
    int z = 0;
 | 
			
		||||
 | 
			
		||||
    if (totalSize.contains (position)
 | 
			
		||||
         && ! border.subtractedFrom (totalSize).contains (position))
 | 
			
		||||
    {
 | 
			
		||||
        auto minW = jmax (totalSize.getWidth() / 10, jmin (10, totalSize.getWidth() / 3));
 | 
			
		||||
 | 
			
		||||
        if (position.x < jmax (border.getLeft(), minW) && border.getLeft() > 0)
 | 
			
		||||
            z |= left;
 | 
			
		||||
        else if (position.x >= totalSize.getWidth() - jmax (border.getRight(), minW) && border.getRight() > 0)
 | 
			
		||||
            z |= right;
 | 
			
		||||
 | 
			
		||||
        auto minH = jmax (totalSize.getHeight() / 10, jmin (10, totalSize.getHeight() / 3));
 | 
			
		||||
 | 
			
		||||
        if (position.y < jmax (border.getTop(), minH) && border.getTop() > 0)
 | 
			
		||||
            z |= top;
 | 
			
		||||
        else if (position.y >= totalSize.getHeight() - jmax (border.getBottom(), minH) && border.getBottom() > 0)
 | 
			
		||||
            z |= bottom;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Zone (z);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MouseCursor ResizableBorderComponent::Zone::getMouseCursor() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto mc = MouseCursor::NormalCursor;
 | 
			
		||||
 | 
			
		||||
    switch (zone)
 | 
			
		||||
    {
 | 
			
		||||
        case (left | top):      mc = MouseCursor::TopLeftCornerResizeCursor; break;
 | 
			
		||||
        case top:               mc = MouseCursor::TopEdgeResizeCursor; break;
 | 
			
		||||
        case (right | top):     mc = MouseCursor::TopRightCornerResizeCursor; break;
 | 
			
		||||
        case left:              mc = MouseCursor::LeftEdgeResizeCursor; break;
 | 
			
		||||
        case right:             mc = MouseCursor::RightEdgeResizeCursor; break;
 | 
			
		||||
        case (left | bottom):   mc = MouseCursor::BottomLeftCornerResizeCursor; break;
 | 
			
		||||
        case bottom:            mc = MouseCursor::BottomEdgeResizeCursor; break;
 | 
			
		||||
        case (right | bottom):  mc = MouseCursor::BottomRightCornerResizeCursor; break;
 | 
			
		||||
        default:                break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return mc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ResizableBorderComponent::ResizableBorderComponent (Component* componentToResize,
 | 
			
		||||
                                                    ComponentBoundsConstrainer* boundsConstrainer)
 | 
			
		||||
   : component (componentToResize),
 | 
			
		||||
     constrainer (boundsConstrainer),
 | 
			
		||||
     borderSize (5)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResizableBorderComponent::~ResizableBorderComponent() = default;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ResizableBorderComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawResizableFrame (g, getWidth(), getHeight(), borderSize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseEnter (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    updateMouseZone (e);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseMove (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    updateMouseZone (e);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseDown (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer was supposed to be using!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateMouseZone (e);
 | 
			
		||||
 | 
			
		||||
    originalBounds = component->getBounds();
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeStart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer was supposed to be using!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto newBounds = mouseZone.resizeRectangleBy (originalBounds, e.getOffsetFromDragStart());
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        constrainer->setBoundsForComponent (component, newBounds,
 | 
			
		||||
                                            mouseZone.isDraggingTopEdge(),
 | 
			
		||||
                                            mouseZone.isDraggingLeftEdge(),
 | 
			
		||||
                                            mouseZone.isDraggingBottomEdge(),
 | 
			
		||||
                                            mouseZone.isDraggingRightEdge());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* p = component->getPositioner())
 | 
			
		||||
            p->applyNewBounds (newBounds);
 | 
			
		||||
        else
 | 
			
		||||
            component->setBounds (newBounds);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeEnd();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ResizableBorderComponent::hitTest (int x, int y)
 | 
			
		||||
{
 | 
			
		||||
    return ! borderSize.subtractedFrom (getLocalBounds()).contains (x, y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::setBorderThickness (BorderSize<int> newBorderSize)
 | 
			
		||||
{
 | 
			
		||||
    if (borderSize != newBorderSize)
 | 
			
		||||
    {
 | 
			
		||||
        borderSize = newBorderSize;
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BorderSize<int> ResizableBorderComponent::getBorderThickness() const
 | 
			
		||||
{
 | 
			
		||||
    return borderSize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::updateMouseZone (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    auto newZone = Zone::fromPositionOnBorder (getLocalBounds(), borderSize, e.getPosition());
 | 
			
		||||
 | 
			
		||||
    if (mouseZone != newZone)
 | 
			
		||||
    {
 | 
			
		||||
        mouseZone = newZone;
 | 
			
		||||
        setMouseCursor (newZone.getMouseCursor());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ResizableBorderComponent::Zone::Zone() noexcept {}
 | 
			
		||||
ResizableBorderComponent::Zone::Zone (int zoneFlags) noexcept  : zone (zoneFlags) {}
 | 
			
		||||
ResizableBorderComponent::Zone::Zone (const ResizableBorderComponent::Zone& other) noexcept  : zone (other.zone) {}
 | 
			
		||||
 | 
			
		||||
ResizableBorderComponent::Zone& ResizableBorderComponent::Zone::operator= (const ResizableBorderComponent::Zone& other) noexcept
 | 
			
		||||
{
 | 
			
		||||
    zone = other.zone;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ResizableBorderComponent::Zone::operator== (const ResizableBorderComponent::Zone& other) const noexcept      { return zone == other.zone; }
 | 
			
		||||
bool ResizableBorderComponent::Zone::operator!= (const ResizableBorderComponent::Zone& other) const noexcept      { return zone != other.zone; }
 | 
			
		||||
 | 
			
		||||
ResizableBorderComponent::Zone ResizableBorderComponent::Zone::fromPositionOnBorder (Rectangle<int> totalSize,
 | 
			
		||||
                                                                                     BorderSize<int> border,
 | 
			
		||||
                                                                                     Point<int> position)
 | 
			
		||||
{
 | 
			
		||||
    int z = 0;
 | 
			
		||||
 | 
			
		||||
    if (totalSize.contains (position)
 | 
			
		||||
         && ! border.subtractedFrom (totalSize).contains (position))
 | 
			
		||||
    {
 | 
			
		||||
        auto minW = jmax (totalSize.getWidth() / 10, jmin (10, totalSize.getWidth() / 3));
 | 
			
		||||
 | 
			
		||||
        if (position.x < jmax (border.getLeft(), minW) && border.getLeft() > 0)
 | 
			
		||||
            z |= left;
 | 
			
		||||
        else if (position.x >= totalSize.getWidth() - jmax (border.getRight(), minW) && border.getRight() > 0)
 | 
			
		||||
            z |= right;
 | 
			
		||||
 | 
			
		||||
        auto minH = jmax (totalSize.getHeight() / 10, jmin (10, totalSize.getHeight() / 3));
 | 
			
		||||
 | 
			
		||||
        if (position.y < jmax (border.getTop(), minH) && border.getTop() > 0)
 | 
			
		||||
            z |= top;
 | 
			
		||||
        else if (position.y >= totalSize.getHeight() - jmax (border.getBottom(), minH) && border.getBottom() > 0)
 | 
			
		||||
            z |= bottom;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Zone (z);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MouseCursor ResizableBorderComponent::Zone::getMouseCursor() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto mc = MouseCursor::NormalCursor;
 | 
			
		||||
 | 
			
		||||
    switch (zone)
 | 
			
		||||
    {
 | 
			
		||||
        case (left | top):      mc = MouseCursor::TopLeftCornerResizeCursor; break;
 | 
			
		||||
        case top:               mc = MouseCursor::TopEdgeResizeCursor; break;
 | 
			
		||||
        case (right | top):     mc = MouseCursor::TopRightCornerResizeCursor; break;
 | 
			
		||||
        case left:              mc = MouseCursor::LeftEdgeResizeCursor; break;
 | 
			
		||||
        case right:             mc = MouseCursor::RightEdgeResizeCursor; break;
 | 
			
		||||
        case (left | bottom):   mc = MouseCursor::BottomLeftCornerResizeCursor; break;
 | 
			
		||||
        case bottom:            mc = MouseCursor::BottomEdgeResizeCursor; break;
 | 
			
		||||
        case (right | bottom):  mc = MouseCursor::BottomRightCornerResizeCursor; break;
 | 
			
		||||
        default:                break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return mc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ResizableBorderComponent::ResizableBorderComponent (Component* componentToResize,
 | 
			
		||||
                                                    ComponentBoundsConstrainer* boundsConstrainer)
 | 
			
		||||
   : component (componentToResize),
 | 
			
		||||
     constrainer (boundsConstrainer),
 | 
			
		||||
     borderSize (5)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResizableBorderComponent::~ResizableBorderComponent() = default;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ResizableBorderComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawResizableFrame (g, getWidth(), getHeight(), borderSize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseEnter (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    updateMouseZone (e);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseMove (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    updateMouseZone (e);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseDown (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer was supposed to be using!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateMouseZone (e);
 | 
			
		||||
 | 
			
		||||
    originalBounds = component->getBounds();
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeStart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer was supposed to be using!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto newBounds = mouseZone.resizeRectangleBy (originalBounds, e.getOffsetFromDragStart());
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        constrainer->setBoundsForComponent (component, newBounds,
 | 
			
		||||
                                            mouseZone.isDraggingTopEdge(),
 | 
			
		||||
                                            mouseZone.isDraggingLeftEdge(),
 | 
			
		||||
                                            mouseZone.isDraggingBottomEdge(),
 | 
			
		||||
                                            mouseZone.isDraggingRightEdge());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* p = component->getPositioner())
 | 
			
		||||
            p->applyNewBounds (newBounds);
 | 
			
		||||
        else
 | 
			
		||||
            component->setBounds (newBounds);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeEnd();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ResizableBorderComponent::hitTest (int x, int y)
 | 
			
		||||
{
 | 
			
		||||
    return ! borderSize.subtractedFrom (getLocalBounds()).contains (x, y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::setBorderThickness (BorderSize<int> newBorderSize)
 | 
			
		||||
{
 | 
			
		||||
    if (borderSize != newBorderSize)
 | 
			
		||||
    {
 | 
			
		||||
        borderSize = newBorderSize;
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BorderSize<int> ResizableBorderComponent::getBorderThickness() const
 | 
			
		||||
{
 | 
			
		||||
    return borderSize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableBorderComponent::updateMouseZone (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    auto newZone = Zone::fromPositionOnBorder (getLocalBounds(), borderSize, e.getPosition());
 | 
			
		||||
 | 
			
		||||
    if (mouseZone != newZone)
 | 
			
		||||
    {
 | 
			
		||||
        mouseZone = newZone;
 | 
			
		||||
        setMouseCursor (newZone.getMouseCursor());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,196 +1,196 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that resizes its parent component when dragged.
 | 
			
		||||
 | 
			
		||||
    This component forms a frame around the edge of a component, allowing it to
 | 
			
		||||
    be dragged by the edges or corners to resize it - like the way windows are
 | 
			
		||||
    resized in MSWindows or Linux.
 | 
			
		||||
 | 
			
		||||
    To use it, just add it to your component, making it fill the entire parent component
 | 
			
		||||
    (there's a mouse hit-test that only traps mouse-events which land around the
 | 
			
		||||
    edge of the component, so it's even ok to put it on top of any other components
 | 
			
		||||
    you're using). Make sure you rescale the resizer component to fill the parent
 | 
			
		||||
    each time the parent's size changes.
 | 
			
		||||
 | 
			
		||||
    @see ResizableCornerComponent
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ResizableBorderComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a resizer.
 | 
			
		||||
 | 
			
		||||
        Pass in the target component which you want to be resized when this one is
 | 
			
		||||
        dragged.
 | 
			
		||||
 | 
			
		||||
        The target component will usually be a parent of the resizer component, but this
 | 
			
		||||
        isn't mandatory.
 | 
			
		||||
 | 
			
		||||
        Remember that when the target component is resized, it'll need to move and
 | 
			
		||||
        resize this component to keep it in place, as this won't happen automatically.
 | 
			
		||||
 | 
			
		||||
        If the constrainer parameter is not a nullptr, then this object will be used to
 | 
			
		||||
        enforce limits on the size and position that the component can be stretched to.
 | 
			
		||||
        Make sure that the constrainer isn't deleted while still in use by this object.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBoundsConstrainer
 | 
			
		||||
    */
 | 
			
		||||
    ResizableBorderComponent (Component* componentToResize,
 | 
			
		||||
                              ComponentBoundsConstrainer* constrainer);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ResizableBorderComponent() override;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Specifies how many pixels wide the draggable edges of this component are.
 | 
			
		||||
 | 
			
		||||
        @see getBorderThickness
 | 
			
		||||
    */
 | 
			
		||||
    void setBorderThickness (BorderSize<int> newBorderSize);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of pixels wide that the draggable edges of this component are.
 | 
			
		||||
 | 
			
		||||
        @see setBorderThickness
 | 
			
		||||
    */
 | 
			
		||||
    BorderSize<int> getBorderThickness() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents the different sections of a resizable border, which allow it to
 | 
			
		||||
        resized in different ways.
 | 
			
		||||
    */
 | 
			
		||||
    class Zone
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        enum Zones
 | 
			
		||||
        {
 | 
			
		||||
            centre  = 0,
 | 
			
		||||
            left    = 1,
 | 
			
		||||
            top     = 2,
 | 
			
		||||
            right   = 4,
 | 
			
		||||
            bottom  = 8
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** Creates a Zone from a combination of the flags in zoneFlags. */
 | 
			
		||||
        explicit Zone (int zoneFlags) noexcept;
 | 
			
		||||
 | 
			
		||||
        Zone() noexcept;
 | 
			
		||||
        Zone (const Zone&) noexcept;
 | 
			
		||||
        Zone& operator= (const Zone&) noexcept;
 | 
			
		||||
 | 
			
		||||
        bool operator== (const Zone&) const noexcept;
 | 
			
		||||
        bool operator!= (const Zone&) const noexcept;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** Given a point within a rectangle with a resizable border, this returns the
 | 
			
		||||
            zone that the point lies within.
 | 
			
		||||
        */
 | 
			
		||||
        static Zone fromPositionOnBorder (Rectangle<int> totalSize,
 | 
			
		||||
                                          BorderSize<int> border,
 | 
			
		||||
                                          Point<int> position);
 | 
			
		||||
 | 
			
		||||
        /** Returns an appropriate mouse-cursor for this resize zone. */
 | 
			
		||||
        MouseCursor getMouseCursor() const noexcept;
 | 
			
		||||
 | 
			
		||||
        /** Returns true if dragging this zone will move the enire object without resizing it. */
 | 
			
		||||
        bool isDraggingWholeObject() const noexcept     { return zone == centre; }
 | 
			
		||||
        /** Returns true if dragging this zone will move the object's left edge. */
 | 
			
		||||
        bool isDraggingLeftEdge() const noexcept        { return (zone & left) != 0; }
 | 
			
		||||
        /** Returns true if dragging this zone will move the object's right edge. */
 | 
			
		||||
        bool isDraggingRightEdge() const noexcept       { return (zone & right) != 0; }
 | 
			
		||||
        /** Returns true if dragging this zone will move the object's top edge. */
 | 
			
		||||
        bool isDraggingTopEdge() const noexcept         { return (zone & top) != 0; }
 | 
			
		||||
        /** Returns true if dragging this zone will move the object's bottom edge. */
 | 
			
		||||
        bool isDraggingBottomEdge() const noexcept      { return (zone & bottom) != 0; }
 | 
			
		||||
 | 
			
		||||
        /** Resizes this rectangle by the given amount, moving just the edges that this zone
 | 
			
		||||
            applies to.
 | 
			
		||||
        */
 | 
			
		||||
        template <typename ValueType>
 | 
			
		||||
        Rectangle<ValueType> resizeRectangleBy (Rectangle<ValueType> original,
 | 
			
		||||
                                                const Point<ValueType>& distance) const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            if (isDraggingWholeObject())
 | 
			
		||||
                return original + distance;
 | 
			
		||||
 | 
			
		||||
            if (isDraggingLeftEdge())   original.setLeft (jmin (original.getRight(), original.getX() + distance.x));
 | 
			
		||||
            if (isDraggingRightEdge())  original.setWidth (jmax (ValueType(), original.getWidth() + distance.x));
 | 
			
		||||
            if (isDraggingTopEdge())    original.setTop (jmin (original.getBottom(), original.getY() + distance.y));
 | 
			
		||||
            if (isDraggingBottomEdge()) original.setHeight (jmax (ValueType(), original.getHeight() + distance.y));
 | 
			
		||||
 | 
			
		||||
            return original;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Returns the raw flags for this zone. */
 | 
			
		||||
        int getZoneFlags() const noexcept               { return zone; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        int zone = centre;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Returns the zone in which the mouse was last seen. */
 | 
			
		||||
    Zone getCurrentZone() const noexcept                 { return mouseZone; }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseEnter (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseMove (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool hitTest (int x, int y) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    ComponentBoundsConstrainer* constrainer;
 | 
			
		||||
    BorderSize<int> borderSize;
 | 
			
		||||
    Rectangle<int> originalBounds;
 | 
			
		||||
    Zone mouseZone;
 | 
			
		||||
 | 
			
		||||
    void updateMouseZone (const MouseEvent&);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableBorderComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that resizes its parent component when dragged.
 | 
			
		||||
 | 
			
		||||
    This component forms a frame around the edge of a component, allowing it to
 | 
			
		||||
    be dragged by the edges or corners to resize it - like the way windows are
 | 
			
		||||
    resized in MSWindows or Linux.
 | 
			
		||||
 | 
			
		||||
    To use it, just add it to your component, making it fill the entire parent component
 | 
			
		||||
    (there's a mouse hit-test that only traps mouse-events which land around the
 | 
			
		||||
    edge of the component, so it's even ok to put it on top of any other components
 | 
			
		||||
    you're using). Make sure you rescale the resizer component to fill the parent
 | 
			
		||||
    each time the parent's size changes.
 | 
			
		||||
 | 
			
		||||
    @see ResizableCornerComponent
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ResizableBorderComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a resizer.
 | 
			
		||||
 | 
			
		||||
        Pass in the target component which you want to be resized when this one is
 | 
			
		||||
        dragged.
 | 
			
		||||
 | 
			
		||||
        The target component will usually be a parent of the resizer component, but this
 | 
			
		||||
        isn't mandatory.
 | 
			
		||||
 | 
			
		||||
        Remember that when the target component is resized, it'll need to move and
 | 
			
		||||
        resize this component to keep it in place, as this won't happen automatically.
 | 
			
		||||
 | 
			
		||||
        If the constrainer parameter is not a nullptr, then this object will be used to
 | 
			
		||||
        enforce limits on the size and position that the component can be stretched to.
 | 
			
		||||
        Make sure that the constrainer isn't deleted while still in use by this object.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBoundsConstrainer
 | 
			
		||||
    */
 | 
			
		||||
    ResizableBorderComponent (Component* componentToResize,
 | 
			
		||||
                              ComponentBoundsConstrainer* constrainer);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ResizableBorderComponent() override;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Specifies how many pixels wide the draggable edges of this component are.
 | 
			
		||||
 | 
			
		||||
        @see getBorderThickness
 | 
			
		||||
    */
 | 
			
		||||
    void setBorderThickness (BorderSize<int> newBorderSize);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of pixels wide that the draggable edges of this component are.
 | 
			
		||||
 | 
			
		||||
        @see setBorderThickness
 | 
			
		||||
    */
 | 
			
		||||
    BorderSize<int> getBorderThickness() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Represents the different sections of a resizable border, which allow it to
 | 
			
		||||
        resized in different ways.
 | 
			
		||||
    */
 | 
			
		||||
    class Zone
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        enum Zones
 | 
			
		||||
        {
 | 
			
		||||
            centre  = 0,
 | 
			
		||||
            left    = 1,
 | 
			
		||||
            top     = 2,
 | 
			
		||||
            right   = 4,
 | 
			
		||||
            bottom  = 8
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** Creates a Zone from a combination of the flags in zoneFlags. */
 | 
			
		||||
        explicit Zone (int zoneFlags) noexcept;
 | 
			
		||||
 | 
			
		||||
        Zone() noexcept;
 | 
			
		||||
        Zone (const Zone&) noexcept;
 | 
			
		||||
        Zone& operator= (const Zone&) noexcept;
 | 
			
		||||
 | 
			
		||||
        bool operator== (const Zone&) const noexcept;
 | 
			
		||||
        bool operator!= (const Zone&) const noexcept;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** Given a point within a rectangle with a resizable border, this returns the
 | 
			
		||||
            zone that the point lies within.
 | 
			
		||||
        */
 | 
			
		||||
        static Zone fromPositionOnBorder (Rectangle<int> totalSize,
 | 
			
		||||
                                          BorderSize<int> border,
 | 
			
		||||
                                          Point<int> position);
 | 
			
		||||
 | 
			
		||||
        /** Returns an appropriate mouse-cursor for this resize zone. */
 | 
			
		||||
        MouseCursor getMouseCursor() const noexcept;
 | 
			
		||||
 | 
			
		||||
        /** Returns true if dragging this zone will move the enire object without resizing it. */
 | 
			
		||||
        bool isDraggingWholeObject() const noexcept     { return zone == centre; }
 | 
			
		||||
        /** Returns true if dragging this zone will move the object's left edge. */
 | 
			
		||||
        bool isDraggingLeftEdge() const noexcept        { return (zone & left) != 0; }
 | 
			
		||||
        /** Returns true if dragging this zone will move the object's right edge. */
 | 
			
		||||
        bool isDraggingRightEdge() const noexcept       { return (zone & right) != 0; }
 | 
			
		||||
        /** Returns true if dragging this zone will move the object's top edge. */
 | 
			
		||||
        bool isDraggingTopEdge() const noexcept         { return (zone & top) != 0; }
 | 
			
		||||
        /** Returns true if dragging this zone will move the object's bottom edge. */
 | 
			
		||||
        bool isDraggingBottomEdge() const noexcept      { return (zone & bottom) != 0; }
 | 
			
		||||
 | 
			
		||||
        /** Resizes this rectangle by the given amount, moving just the edges that this zone
 | 
			
		||||
            applies to.
 | 
			
		||||
        */
 | 
			
		||||
        template <typename ValueType>
 | 
			
		||||
        Rectangle<ValueType> resizeRectangleBy (Rectangle<ValueType> original,
 | 
			
		||||
                                                const Point<ValueType>& distance) const noexcept
 | 
			
		||||
        {
 | 
			
		||||
            if (isDraggingWholeObject())
 | 
			
		||||
                return original + distance;
 | 
			
		||||
 | 
			
		||||
            if (isDraggingLeftEdge())   original.setLeft (jmin (original.getRight(), original.getX() + distance.x));
 | 
			
		||||
            if (isDraggingRightEdge())  original.setWidth (jmax (ValueType(), original.getWidth() + distance.x));
 | 
			
		||||
            if (isDraggingTopEdge())    original.setTop (jmin (original.getBottom(), original.getY() + distance.y));
 | 
			
		||||
            if (isDraggingBottomEdge()) original.setHeight (jmax (ValueType(), original.getHeight() + distance.y));
 | 
			
		||||
 | 
			
		||||
            return original;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Returns the raw flags for this zone. */
 | 
			
		||||
        int getZoneFlags() const noexcept               { return zone; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        int zone = centre;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Returns the zone in which the mouse was last seen. */
 | 
			
		||||
    Zone getCurrentZone() const noexcept                 { return mouseZone; }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseEnter (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseMove (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool hitTest (int x, int y) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    ComponentBoundsConstrainer* constrainer;
 | 
			
		||||
    BorderSize<int> borderSize;
 | 
			
		||||
    Rectangle<int> originalBounds;
 | 
			
		||||
    Zone mouseZone;
 | 
			
		||||
 | 
			
		||||
    void updateMouseZone (const MouseEvent&);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableBorderComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,96 +1,96 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ResizableCornerComponent::ResizableCornerComponent (Component* componentToResize,
 | 
			
		||||
                                                    ComponentBoundsConstrainer* boundsConstrainer)
 | 
			
		||||
   : component (componentToResize),
 | 
			
		||||
     constrainer (boundsConstrainer)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setMouseCursor (MouseCursor::BottomRightCornerResizeCursor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResizableCornerComponent::~ResizableCornerComponent() = default;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ResizableCornerComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawCornerResizer (g, getWidth(), getHeight(),
 | 
			
		||||
                                        isMouseOverOrDragging(),
 | 
			
		||||
                                        isMouseButtonDown());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableCornerComponent::mouseDown (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer is supposed to be controlling!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    originalBounds = component->getBounds();
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeStart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableCornerComponent::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer is supposed to be controlling!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto r = originalBounds.withSize (originalBounds.getWidth() + e.getDistanceFromDragStartX(),
 | 
			
		||||
                                      originalBounds.getHeight() + e.getDistanceFromDragStartY());
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->setBoundsForComponent (component, r, false, false, true, true);
 | 
			
		||||
    else if (auto pos = component->getPositioner())
 | 
			
		||||
        pos->applyNewBounds (r);
 | 
			
		||||
    else
 | 
			
		||||
        component->setBounds (r);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableCornerComponent::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeEnd();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ResizableCornerComponent::hitTest (int x, int y)
 | 
			
		||||
{
 | 
			
		||||
    if (getWidth() <= 0)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    auto yAtX = getHeight() - (getHeight() * x / getWidth());
 | 
			
		||||
    return y >= yAtX - getHeight() / 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ResizableCornerComponent::ResizableCornerComponent (Component* componentToResize,
 | 
			
		||||
                                                    ComponentBoundsConstrainer* boundsConstrainer)
 | 
			
		||||
   : component (componentToResize),
 | 
			
		||||
     constrainer (boundsConstrainer)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setMouseCursor (MouseCursor::BottomRightCornerResizeCursor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResizableCornerComponent::~ResizableCornerComponent() = default;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ResizableCornerComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawCornerResizer (g, getWidth(), getHeight(),
 | 
			
		||||
                                        isMouseOverOrDragging(),
 | 
			
		||||
                                        isMouseButtonDown());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableCornerComponent::mouseDown (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer is supposed to be controlling!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    originalBounds = component->getBounds();
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeStart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableCornerComponent::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer is supposed to be controlling!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto r = originalBounds.withSize (originalBounds.getWidth() + e.getDistanceFromDragStartX(),
 | 
			
		||||
                                      originalBounds.getHeight() + e.getDistanceFromDragStartY());
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->setBoundsForComponent (component, r, false, false, true, true);
 | 
			
		||||
    else if (auto pos = component->getPositioner())
 | 
			
		||||
        pos->applyNewBounds (r);
 | 
			
		||||
    else
 | 
			
		||||
        component->setBounds (r);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableCornerComponent::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeEnd();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ResizableCornerComponent::hitTest (int x, int y)
 | 
			
		||||
{
 | 
			
		||||
    if (getWidth() <= 0)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    auto yAtX = getHeight() - (getHeight() * x / getWidth());
 | 
			
		||||
    return y >= yAtX - getHeight() / 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,92 +1,92 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** A component that resizes a parent component when dragged.
 | 
			
		||||
 | 
			
		||||
    This is the small triangular stripey resizer component you get in the bottom-right
 | 
			
		||||
    of windows (more commonly on the Mac than Windows). Put one in the corner of
 | 
			
		||||
    a larger component and it will automatically resize its parent when it gets dragged
 | 
			
		||||
    around.
 | 
			
		||||
 | 
			
		||||
    @see ResizableBorderComponent
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ResizableCornerComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a resizer.
 | 
			
		||||
 | 
			
		||||
        Pass in the target component which you want to be resized when this one is
 | 
			
		||||
        dragged.
 | 
			
		||||
 | 
			
		||||
        The target component will usually be a parent of the resizer component, but this
 | 
			
		||||
        isn't mandatory.
 | 
			
		||||
 | 
			
		||||
        Remember that when the target component is resized, it'll need to move and
 | 
			
		||||
        resize this component to keep it in place, as this won't happen automatically.
 | 
			
		||||
 | 
			
		||||
        If a constrainer object is provided, then this object will be used to enforce
 | 
			
		||||
        limits on the size and position that the component can be stretched to. Make sure
 | 
			
		||||
        that the constrainer isn't deleted while still in use by this object. If you
 | 
			
		||||
        pass a nullptr in here, no limits will be put on the sizes it can be stretched to.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBoundsConstrainer
 | 
			
		||||
    */
 | 
			
		||||
    ResizableCornerComponent (Component* componentToResize,
 | 
			
		||||
                              ComponentBoundsConstrainer* constrainer);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ResizableCornerComponent() override;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool hitTest (int x, int y) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    ComponentBoundsConstrainer* constrainer;
 | 
			
		||||
    Rectangle<int> originalBounds;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableCornerComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** A component that resizes a parent component when dragged.
 | 
			
		||||
 | 
			
		||||
    This is the small triangular stripey resizer component you get in the bottom-right
 | 
			
		||||
    of windows (more commonly on the Mac than Windows). Put one in the corner of
 | 
			
		||||
    a larger component and it will automatically resize its parent when it gets dragged
 | 
			
		||||
    around.
 | 
			
		||||
 | 
			
		||||
    @see ResizableBorderComponent
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ResizableCornerComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a resizer.
 | 
			
		||||
 | 
			
		||||
        Pass in the target component which you want to be resized when this one is
 | 
			
		||||
        dragged.
 | 
			
		||||
 | 
			
		||||
        The target component will usually be a parent of the resizer component, but this
 | 
			
		||||
        isn't mandatory.
 | 
			
		||||
 | 
			
		||||
        Remember that when the target component is resized, it'll need to move and
 | 
			
		||||
        resize this component to keep it in place, as this won't happen automatically.
 | 
			
		||||
 | 
			
		||||
        If a constrainer object is provided, then this object will be used to enforce
 | 
			
		||||
        limits on the size and position that the component can be stretched to. Make sure
 | 
			
		||||
        that the constrainer isn't deleted while still in use by this object. If you
 | 
			
		||||
        pass a nullptr in here, no limits will be put on the sizes it can be stretched to.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBoundsConstrainer
 | 
			
		||||
    */
 | 
			
		||||
    ResizableCornerComponent (Component* componentToResize,
 | 
			
		||||
                              ComponentBoundsConstrainer* constrainer);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ResizableCornerComponent() override;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool hitTest (int x, int y) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    ComponentBoundsConstrainer* constrainer;
 | 
			
		||||
    Rectangle<int> originalBounds;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableCornerComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,111 +1,111 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ResizableEdgeComponent::ResizableEdgeComponent (Component* componentToResize,
 | 
			
		||||
                                                ComponentBoundsConstrainer* boundsConstrainer,
 | 
			
		||||
                                                Edge e)
 | 
			
		||||
   : component (componentToResize),
 | 
			
		||||
     constrainer (boundsConstrainer),
 | 
			
		||||
     edge (e)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setMouseCursor (isVertical() ? MouseCursor::LeftRightResizeCursor
 | 
			
		||||
                                 : MouseCursor::UpDownResizeCursor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResizableEdgeComponent::~ResizableEdgeComponent() = default;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool ResizableEdgeComponent::isVertical() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return edge == leftEdge || edge == rightEdge;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableEdgeComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawStretchableLayoutResizerBar (g, getWidth(), getHeight(), isVertical(),
 | 
			
		||||
                                                      isMouseOver(), isMouseButtonDown());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableEdgeComponent::mouseDown (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer was supposed to be using!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    originalBounds = component->getBounds();
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeStart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableEdgeComponent::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer was supposed to be using!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto newBounds = originalBounds;
 | 
			
		||||
 | 
			
		||||
    switch (edge)
 | 
			
		||||
    {
 | 
			
		||||
        case leftEdge:      newBounds.setLeft (jmin (newBounds.getRight(), newBounds.getX() + e.getDistanceFromDragStartX())); break;
 | 
			
		||||
        case rightEdge:     newBounds.setWidth (jmax (0, newBounds.getWidth() + e.getDistanceFromDragStartX())); break;
 | 
			
		||||
        case topEdge:       newBounds.setTop (jmin (newBounds.getBottom(), newBounds.getY() + e.getDistanceFromDragStartY())); break;
 | 
			
		||||
        case bottomEdge:    newBounds.setHeight (jmax (0, newBounds.getHeight() + e.getDistanceFromDragStartY())); break;
 | 
			
		||||
        default:            jassertfalse; break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        constrainer->setBoundsForComponent (component, newBounds,
 | 
			
		||||
                                            edge == topEdge,
 | 
			
		||||
                                            edge == leftEdge,
 | 
			
		||||
                                            edge == bottomEdge,
 | 
			
		||||
                                            edge == rightEdge);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* p = component->getPositioner())
 | 
			
		||||
            p->applyNewBounds (newBounds);
 | 
			
		||||
        else
 | 
			
		||||
            component->setBounds (newBounds);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableEdgeComponent::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeEnd();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ResizableEdgeComponent::ResizableEdgeComponent (Component* componentToResize,
 | 
			
		||||
                                                ComponentBoundsConstrainer* boundsConstrainer,
 | 
			
		||||
                                                Edge e)
 | 
			
		||||
   : component (componentToResize),
 | 
			
		||||
     constrainer (boundsConstrainer),
 | 
			
		||||
     edge (e)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setMouseCursor (isVertical() ? MouseCursor::LeftRightResizeCursor
 | 
			
		||||
                                 : MouseCursor::UpDownResizeCursor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResizableEdgeComponent::~ResizableEdgeComponent() = default;
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool ResizableEdgeComponent::isVertical() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return edge == leftEdge || edge == rightEdge;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableEdgeComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawStretchableLayoutResizerBar (g, getWidth(), getHeight(), isVertical(),
 | 
			
		||||
                                                      isMouseOver(), isMouseButtonDown());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableEdgeComponent::mouseDown (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer was supposed to be using!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    originalBounds = component->getBounds();
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeStart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableEdgeComponent::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (component == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        jassertfalse; // You've deleted the component that this resizer was supposed to be using!
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto newBounds = originalBounds;
 | 
			
		||||
 | 
			
		||||
    switch (edge)
 | 
			
		||||
    {
 | 
			
		||||
        case leftEdge:      newBounds.setLeft (jmin (newBounds.getRight(), newBounds.getX() + e.getDistanceFromDragStartX())); break;
 | 
			
		||||
        case rightEdge:     newBounds.setWidth (jmax (0, newBounds.getWidth() + e.getDistanceFromDragStartX())); break;
 | 
			
		||||
        case topEdge:       newBounds.setTop (jmin (newBounds.getBottom(), newBounds.getY() + e.getDistanceFromDragStartY())); break;
 | 
			
		||||
        case bottomEdge:    newBounds.setHeight (jmax (0, newBounds.getHeight() + e.getDistanceFromDragStartY())); break;
 | 
			
		||||
        default:            jassertfalse; break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        constrainer->setBoundsForComponent (component, newBounds,
 | 
			
		||||
                                            edge == topEdge,
 | 
			
		||||
                                            edge == leftEdge,
 | 
			
		||||
                                            edge == bottomEdge,
 | 
			
		||||
                                            edge == rightEdge);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* p = component->getPositioner())
 | 
			
		||||
            p->applyNewBounds (newBounds);
 | 
			
		||||
        else
 | 
			
		||||
            component->setBounds (newBounds);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ResizableEdgeComponent::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (constrainer != nullptr)
 | 
			
		||||
        constrainer->resizeEnd();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,100 +1,100 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that resizes its parent component when dragged.
 | 
			
		||||
 | 
			
		||||
    This component forms a bar along one edge of a component, allowing it to
 | 
			
		||||
    be dragged by that edges to resize it.
 | 
			
		||||
 | 
			
		||||
    To use it, just add it to your component, positioning it along the appropriate
 | 
			
		||||
    edge. Make sure you reposition the resizer component each time the parent's size
 | 
			
		||||
    changes, to keep it in the correct position.
 | 
			
		||||
 | 
			
		||||
    @see ResizableBorderComponent, ResizableCornerComponent
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ResizableEdgeComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum Edge
 | 
			
		||||
    {
 | 
			
		||||
        leftEdge,   /**< Indicates a vertical bar that can be dragged left/right to move the component's left-hand edge. */
 | 
			
		||||
        rightEdge,  /**< Indicates a vertical bar that can be dragged left/right to move the component's right-hand edge. */
 | 
			
		||||
        topEdge,    /**< Indicates a horizontal bar that can be dragged up/down to move the top of the component. */
 | 
			
		||||
        bottomEdge  /**< Indicates a horizontal bar that can be dragged up/down to move the bottom of the component. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Creates a resizer bar.
 | 
			
		||||
 | 
			
		||||
        Pass in the target component which you want to be resized when this one is
 | 
			
		||||
        dragged. The target component will usually be this component's parent, but this
 | 
			
		||||
        isn't mandatory.
 | 
			
		||||
 | 
			
		||||
        Remember that when the target component is resized, it'll need to move and
 | 
			
		||||
        resize this component to keep it in place, as this won't happen automatically.
 | 
			
		||||
 | 
			
		||||
        If the constrainer parameter is not a nullptr, then this object will be used to
 | 
			
		||||
        enforce limits on the size and position that the component can be stretched to.
 | 
			
		||||
        Make sure that the constrainer isn't deleted while still in use by this object.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBoundsConstrainer
 | 
			
		||||
    */
 | 
			
		||||
    ResizableEdgeComponent (Component* componentToResize,
 | 
			
		||||
                            ComponentBoundsConstrainer* constrainer,
 | 
			
		||||
                            Edge edgeToResize);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ResizableEdgeComponent() override;
 | 
			
		||||
 | 
			
		||||
    bool isVertical() const noexcept;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    ComponentBoundsConstrainer* constrainer;
 | 
			
		||||
    Rectangle<int> originalBounds;
 | 
			
		||||
    const Edge edge;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableEdgeComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that resizes its parent component when dragged.
 | 
			
		||||
 | 
			
		||||
    This component forms a bar along one edge of a component, allowing it to
 | 
			
		||||
    be dragged by that edges to resize it.
 | 
			
		||||
 | 
			
		||||
    To use it, just add it to your component, positioning it along the appropriate
 | 
			
		||||
    edge. Make sure you reposition the resizer component each time the parent's size
 | 
			
		||||
    changes, to keep it in the correct position.
 | 
			
		||||
 | 
			
		||||
    @see ResizableBorderComponent, ResizableCornerComponent
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ResizableEdgeComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    enum Edge
 | 
			
		||||
    {
 | 
			
		||||
        leftEdge,   /**< Indicates a vertical bar that can be dragged left/right to move the component's left-hand edge. */
 | 
			
		||||
        rightEdge,  /**< Indicates a vertical bar that can be dragged left/right to move the component's right-hand edge. */
 | 
			
		||||
        topEdge,    /**< Indicates a horizontal bar that can be dragged up/down to move the top of the component. */
 | 
			
		||||
        bottomEdge  /**< Indicates a horizontal bar that can be dragged up/down to move the bottom of the component. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Creates a resizer bar.
 | 
			
		||||
 | 
			
		||||
        Pass in the target component which you want to be resized when this one is
 | 
			
		||||
        dragged. The target component will usually be this component's parent, but this
 | 
			
		||||
        isn't mandatory.
 | 
			
		||||
 | 
			
		||||
        Remember that when the target component is resized, it'll need to move and
 | 
			
		||||
        resize this component to keep it in place, as this won't happen automatically.
 | 
			
		||||
 | 
			
		||||
        If the constrainer parameter is not a nullptr, then this object will be used to
 | 
			
		||||
        enforce limits on the size and position that the component can be stretched to.
 | 
			
		||||
        Make sure that the constrainer isn't deleted while still in use by this object.
 | 
			
		||||
 | 
			
		||||
        @see ComponentBoundsConstrainer
 | 
			
		||||
    */
 | 
			
		||||
    ResizableEdgeComponent (Component* componentToResize,
 | 
			
		||||
                            ComponentBoundsConstrainer* constrainer,
 | 
			
		||||
                            Edge edgeToResize);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ResizableEdgeComponent() override;
 | 
			
		||||
 | 
			
		||||
    bool isVertical() const noexcept;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    WeakReference<Component> component;
 | 
			
		||||
    ComponentBoundsConstrainer* constrainer;
 | 
			
		||||
    Rectangle<int> originalBounds;
 | 
			
		||||
    const Edge edge;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableEdgeComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,477 +1,477 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class ScrollBar::ScrollbarButton  : public Button
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ScrollbarButton (int direc, ScrollBar& s)
 | 
			
		||||
        : Button (String()), direction (direc), owner (s)
 | 
			
		||||
    {
 | 
			
		||||
        setWantsKeyboardFocus (false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintButton (Graphics& g, bool over, bool down) override
 | 
			
		||||
    {
 | 
			
		||||
        getLookAndFeel().drawScrollbarButton (g, owner, getWidth(), getHeight(),
 | 
			
		||||
                                              direction, owner.isVertical(), over, down);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void clicked() override
 | 
			
		||||
    {
 | 
			
		||||
        owner.moveScrollbarInSteps ((direction == 1 || direction == 2) ? 1 : -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using Button::clicked;
 | 
			
		||||
 | 
			
		||||
    int direction;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ScrollBar& owner;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScrollbarButton)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ScrollBar::ScrollBar (bool shouldBeVertical)  : vertical (shouldBeVertical)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setFocusContainerType (FocusContainerType::keyboardFocusContainer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ScrollBar::~ScrollBar()
 | 
			
		||||
{
 | 
			
		||||
    upButton.reset();
 | 
			
		||||
    downButton.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ScrollBar::setRangeLimits (Range<double> newRangeLimit, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    if (totalRange != newRangeLimit)
 | 
			
		||||
    {
 | 
			
		||||
        totalRange = newRangeLimit;
 | 
			
		||||
        setCurrentRange (visibleRange, notification);
 | 
			
		||||
        updateThumbPosition();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setRangeLimits (double newMinimum, double newMaximum, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    jassert (newMaximum >= newMinimum); // these can't be the wrong way round!
 | 
			
		||||
    setRangeLimits (Range<double> (newMinimum, newMaximum), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::setCurrentRange (Range<double> newRange, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    auto constrainedRange = totalRange.constrainRange (newRange);
 | 
			
		||||
 | 
			
		||||
    if (visibleRange != constrainedRange)
 | 
			
		||||
    {
 | 
			
		||||
        visibleRange = constrainedRange;
 | 
			
		||||
 | 
			
		||||
        updateThumbPosition();
 | 
			
		||||
 | 
			
		||||
        if (notification != dontSendNotification)
 | 
			
		||||
            triggerAsyncUpdate();
 | 
			
		||||
 | 
			
		||||
        if (notification == sendNotificationSync)
 | 
			
		||||
            handleUpdateNowIfNeeded();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setCurrentRange (double newStart, double newSize, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    setCurrentRange (Range<double> (newStart, newStart + newSize), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setCurrentRangeStart (double newStart, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    setCurrentRange (visibleRange.movedToStartAt (newStart), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setSingleStepSize (double newSingleStepSize) noexcept
 | 
			
		||||
{
 | 
			
		||||
    singleStepSize = newSingleStepSize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::moveScrollbarInSteps (int howManySteps, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    return setCurrentRange (visibleRange + howManySteps * singleStepSize, notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::moveScrollbarInPages (int howManyPages, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    return setCurrentRange (visibleRange + howManyPages * visibleRange.getLength(), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::scrollToTop (NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    return setCurrentRange (visibleRange.movedToStartAt (getMinimumRangeLimit()), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::scrollToBottom (NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    return setCurrentRange (visibleRange.movedToEndAt (getMaximumRangeLimit()), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setButtonRepeatSpeed (int newInitialDelay,
 | 
			
		||||
                                      int newRepeatDelay,
 | 
			
		||||
                                      int newMinimumDelay)
 | 
			
		||||
{
 | 
			
		||||
    initialDelayInMillisecs = newInitialDelay;
 | 
			
		||||
    repeatDelayInMillisecs  = newRepeatDelay;
 | 
			
		||||
    minimumDelayInMillisecs = newMinimumDelay;
 | 
			
		||||
 | 
			
		||||
    if (upButton != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        upButton  ->setRepeatSpeed (newInitialDelay, newRepeatDelay, newMinimumDelay);
 | 
			
		||||
        downButton->setRepeatSpeed (newInitialDelay, newRepeatDelay, newMinimumDelay);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ScrollBar::addListener (Listener* listener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.add (listener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::removeListener (Listener* listener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.remove (listener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::handleAsyncUpdate()
 | 
			
		||||
{
 | 
			
		||||
    auto start = visibleRange.getStart(); // (need to use a temp variable for VC7 compatibility)
 | 
			
		||||
    listeners.call ([this, start] (Listener& l) { l.scrollBarMoved (this, start); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ScrollBar::updateThumbPosition()
 | 
			
		||||
{
 | 
			
		||||
    auto minimumScrollBarThumbSize = getLookAndFeel().getMinimumScrollbarThumbSize (*this);
 | 
			
		||||
 | 
			
		||||
    int newThumbSize = roundToInt (totalRange.getLength() > 0 ? (visibleRange.getLength() * thumbAreaSize) / totalRange.getLength()
 | 
			
		||||
                                                              : thumbAreaSize);
 | 
			
		||||
 | 
			
		||||
    if (newThumbSize < minimumScrollBarThumbSize)
 | 
			
		||||
        newThumbSize = jmin (minimumScrollBarThumbSize, thumbAreaSize - 1);
 | 
			
		||||
 | 
			
		||||
    if (newThumbSize > thumbAreaSize)
 | 
			
		||||
        newThumbSize = thumbAreaSize;
 | 
			
		||||
 | 
			
		||||
    int newThumbStart = thumbAreaStart;
 | 
			
		||||
 | 
			
		||||
    if (totalRange.getLength() > visibleRange.getLength())
 | 
			
		||||
        newThumbStart += roundToInt (((visibleRange.getStart() - totalRange.getStart()) * (thumbAreaSize - newThumbSize))
 | 
			
		||||
                                         / (totalRange.getLength() - visibleRange.getLength()));
 | 
			
		||||
 | 
			
		||||
    Component::setVisible (getVisibility());
 | 
			
		||||
 | 
			
		||||
    if (thumbStart != newThumbStart  || thumbSize != newThumbSize)
 | 
			
		||||
    {
 | 
			
		||||
        auto repaintStart = jmin (thumbStart, newThumbStart) - 4;
 | 
			
		||||
        auto repaintSize = jmax (thumbStart + thumbSize, newThumbStart + newThumbSize) + 8 - repaintStart;
 | 
			
		||||
 | 
			
		||||
        if (vertical)
 | 
			
		||||
            repaint (0, repaintStart, getWidth(), repaintSize);
 | 
			
		||||
        else
 | 
			
		||||
            repaint (repaintStart, 0, repaintSize, getHeight());
 | 
			
		||||
 | 
			
		||||
        thumbStart = newThumbStart;
 | 
			
		||||
        thumbSize = newThumbSize;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setOrientation (bool shouldBeVertical)
 | 
			
		||||
{
 | 
			
		||||
    if (vertical != shouldBeVertical)
 | 
			
		||||
    {
 | 
			
		||||
        vertical = shouldBeVertical;
 | 
			
		||||
 | 
			
		||||
        if (upButton != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            upButton->direction    = vertical ? 0 : 3;
 | 
			
		||||
            downButton->direction  = vertical ? 2 : 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        updateThumbPosition();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setAutoHide (bool shouldHideWhenFullRange)
 | 
			
		||||
{
 | 
			
		||||
    autohides = shouldHideWhenFullRange;
 | 
			
		||||
    updateThumbPosition();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::autoHides() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return autohides;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ScrollBar::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    if (thumbAreaSize > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto& lf = getLookAndFeel();
 | 
			
		||||
 | 
			
		||||
        auto thumb = (thumbAreaSize > lf.getMinimumScrollbarThumbSize (*this))
 | 
			
		||||
                       ? thumbSize : 0;
 | 
			
		||||
 | 
			
		||||
        if (vertical)
 | 
			
		||||
            lf.drawScrollbar (g, *this, 0, thumbAreaStart, getWidth(), thumbAreaSize,
 | 
			
		||||
                              vertical, thumbStart, thumb, isMouseOver(), isMouseButtonDown());
 | 
			
		||||
        else
 | 
			
		||||
            lf.drawScrollbar (g, *this, thumbAreaStart, 0, thumbAreaSize, getHeight(),
 | 
			
		||||
                              vertical, thumbStart, thumb, isMouseOver(), isMouseButtonDown());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::lookAndFeelChanged()
 | 
			
		||||
{
 | 
			
		||||
    setComponentEffect (getLookAndFeel().getScrollbarEffect());
 | 
			
		||||
 | 
			
		||||
    if (isVisible())
 | 
			
		||||
        resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::resized()
 | 
			
		||||
{
 | 
			
		||||
    auto length = vertical ? getHeight() : getWidth();
 | 
			
		||||
 | 
			
		||||
    auto& lf = getLookAndFeel();
 | 
			
		||||
    bool buttonsVisible = lf.areScrollbarButtonsVisible();
 | 
			
		||||
    int buttonSize = 0;
 | 
			
		||||
 | 
			
		||||
    if (buttonsVisible)
 | 
			
		||||
    {
 | 
			
		||||
        if (upButton == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            upButton  .reset (new ScrollbarButton (vertical ? 0 : 3, *this));
 | 
			
		||||
            downButton.reset (new ScrollbarButton (vertical ? 2 : 1, *this));
 | 
			
		||||
 | 
			
		||||
            addAndMakeVisible (upButton.get());
 | 
			
		||||
            addAndMakeVisible (downButton.get());
 | 
			
		||||
 | 
			
		||||
            setButtonRepeatSpeed (initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        buttonSize = jmin (lf.getScrollbarButtonSize (*this), length / 2);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        upButton.reset();
 | 
			
		||||
        downButton.reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (length < 32 + lf.getMinimumScrollbarThumbSize (*this))
 | 
			
		||||
    {
 | 
			
		||||
        thumbAreaStart = length / 2;
 | 
			
		||||
        thumbAreaSize = 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        thumbAreaStart = buttonSize;
 | 
			
		||||
        thumbAreaSize = length - 2 * buttonSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (upButton != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        if (vertical)
 | 
			
		||||
        {
 | 
			
		||||
            upButton->setBounds (r.removeFromTop (buttonSize));
 | 
			
		||||
            downButton->setBounds (r.removeFromBottom (buttonSize));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            upButton->setBounds (r.removeFromLeft (buttonSize));
 | 
			
		||||
            downButton->setBounds (r.removeFromRight (buttonSize));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateThumbPosition();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::parentHierarchyChanged()
 | 
			
		||||
{
 | 
			
		||||
    lookAndFeelChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::mouseDown (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    isDraggingThumb = false;
 | 
			
		||||
    lastMousePos = vertical ? e.y : e.x;
 | 
			
		||||
    dragStartMousePos = lastMousePos;
 | 
			
		||||
    dragStartRange = visibleRange.getStart();
 | 
			
		||||
 | 
			
		||||
    if (dragStartMousePos < thumbStart)
 | 
			
		||||
    {
 | 
			
		||||
        moveScrollbarInPages (-1);
 | 
			
		||||
        startTimer (400);
 | 
			
		||||
    }
 | 
			
		||||
    else if (dragStartMousePos >= thumbStart + thumbSize)
 | 
			
		||||
    {
 | 
			
		||||
        moveScrollbarInPages (1);
 | 
			
		||||
        startTimer (400);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        isDraggingThumb = (thumbAreaSize > getLookAndFeel().getMinimumScrollbarThumbSize (*this))
 | 
			
		||||
                            && (thumbAreaSize > thumbSize);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    auto mousePos = vertical ? e.y : e.x;
 | 
			
		||||
 | 
			
		||||
    if (isDraggingThumb && lastMousePos != mousePos && thumbAreaSize > thumbSize)
 | 
			
		||||
    {
 | 
			
		||||
        auto deltaPixels = mousePos - dragStartMousePos;
 | 
			
		||||
 | 
			
		||||
        setCurrentRangeStart (dragStartRange
 | 
			
		||||
                                + deltaPixels * (totalRange.getLength() - visibleRange.getLength())
 | 
			
		||||
                                    / (thumbAreaSize - thumbSize));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lastMousePos = mousePos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    isDraggingThumb = false;
 | 
			
		||||
    stopTimer();
 | 
			
		||||
    repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel)
 | 
			
		||||
{
 | 
			
		||||
    float increment = 10.0f * (vertical ? wheel.deltaY : wheel.deltaX);
 | 
			
		||||
 | 
			
		||||
    if (increment < 0)
 | 
			
		||||
        increment = jmin (increment, -1.0f);
 | 
			
		||||
    else if (increment > 0)
 | 
			
		||||
        increment = jmax (increment, 1.0f);
 | 
			
		||||
 | 
			
		||||
    setCurrentRange (visibleRange - singleStepSize * increment);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::timerCallback()
 | 
			
		||||
{
 | 
			
		||||
    if (isMouseButtonDown())
 | 
			
		||||
    {
 | 
			
		||||
        startTimer (40);
 | 
			
		||||
 | 
			
		||||
        if (lastMousePos < thumbStart)
 | 
			
		||||
            setCurrentRange (visibleRange - visibleRange.getLength());
 | 
			
		||||
        else if (lastMousePos > thumbStart + thumbSize)
 | 
			
		||||
            setCurrentRangeStart (visibleRange.getEnd());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        stopTimer();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::keyPressed (const KeyPress& key)
 | 
			
		||||
{
 | 
			
		||||
    if (isVisible())
 | 
			
		||||
    {
 | 
			
		||||
        if (key == KeyPress::upKey || key == KeyPress::leftKey)    return moveScrollbarInSteps (-1);
 | 
			
		||||
        if (key == KeyPress::downKey || key == KeyPress::rightKey) return moveScrollbarInSteps (1);
 | 
			
		||||
        if (key == KeyPress::pageUpKey)                            return moveScrollbarInPages (-1);
 | 
			
		||||
        if (key == KeyPress::pageDownKey)                          return moveScrollbarInPages (1);
 | 
			
		||||
        if (key == KeyPress::homeKey)                              return scrollToTop();
 | 
			
		||||
        if (key == KeyPress::endKey)                               return scrollToBottom();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setVisible (bool shouldBeVisible)
 | 
			
		||||
{
 | 
			
		||||
    if (userVisibilityFlag != shouldBeVisible)
 | 
			
		||||
    {
 | 
			
		||||
        userVisibilityFlag = shouldBeVisible;
 | 
			
		||||
        Component::setVisible (getVisibility());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::getVisibility() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (! userVisibilityFlag)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    return (! autohides) || (totalRange.getLength() > visibleRange.getLength()
 | 
			
		||||
                                    && visibleRange.getLength() > 0.0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> ScrollBar::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    class ValueInterface  : public AccessibilityRangedNumericValueInterface
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        explicit ValueInterface (ScrollBar& scrollBarToWrap)  : scrollBar (scrollBarToWrap) {}
 | 
			
		||||
 | 
			
		||||
        bool isReadOnly() const override          { return false; }
 | 
			
		||||
 | 
			
		||||
        double getCurrentValue() const override   { return scrollBar.getCurrentRangeStart(); }
 | 
			
		||||
        void setValue (double newValue) override  { scrollBar.setCurrentRangeStart (newValue); }
 | 
			
		||||
 | 
			
		||||
        AccessibleValueRange getRange() const override
 | 
			
		||||
        {
 | 
			
		||||
            if (scrollBar.getRangeLimit().isEmpty())
 | 
			
		||||
                return {};
 | 
			
		||||
 | 
			
		||||
            return { { scrollBar.getMinimumRangeLimit(), scrollBar.getMaximumRangeLimit() },
 | 
			
		||||
                     scrollBar.getSingleStepSize() };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        ScrollBar& scrollBar;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueInterface)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this,
 | 
			
		||||
                                                   AccessibilityRole::scrollBar,
 | 
			
		||||
                                                   AccessibilityActions{},
 | 
			
		||||
                                                   AccessibilityHandler::Interfaces { std::make_unique<ValueInterface> (*this) });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class ScrollBar::ScrollbarButton  : public Button
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ScrollbarButton (int direc, ScrollBar& s)
 | 
			
		||||
        : Button (String()), direction (direc), owner (s)
 | 
			
		||||
    {
 | 
			
		||||
        setWantsKeyboardFocus (false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintButton (Graphics& g, bool over, bool down) override
 | 
			
		||||
    {
 | 
			
		||||
        getLookAndFeel().drawScrollbarButton (g, owner, getWidth(), getHeight(),
 | 
			
		||||
                                              direction, owner.isVertical(), over, down);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void clicked() override
 | 
			
		||||
    {
 | 
			
		||||
        owner.moveScrollbarInSteps ((direction == 1 || direction == 2) ? 1 : -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    using Button::clicked;
 | 
			
		||||
 | 
			
		||||
    int direction;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ScrollBar& owner;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScrollbarButton)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ScrollBar::ScrollBar (bool shouldBeVertical)  : vertical (shouldBeVertical)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setFocusContainerType (FocusContainerType::keyboardFocusContainer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ScrollBar::~ScrollBar()
 | 
			
		||||
{
 | 
			
		||||
    upButton.reset();
 | 
			
		||||
    downButton.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ScrollBar::setRangeLimits (Range<double> newRangeLimit, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    if (totalRange != newRangeLimit)
 | 
			
		||||
    {
 | 
			
		||||
        totalRange = newRangeLimit;
 | 
			
		||||
        setCurrentRange (visibleRange, notification);
 | 
			
		||||
        updateThumbPosition();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setRangeLimits (double newMinimum, double newMaximum, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    jassert (newMaximum >= newMinimum); // these can't be the wrong way round!
 | 
			
		||||
    setRangeLimits (Range<double> (newMinimum, newMaximum), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::setCurrentRange (Range<double> newRange, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    auto constrainedRange = totalRange.constrainRange (newRange);
 | 
			
		||||
 | 
			
		||||
    if (visibleRange != constrainedRange)
 | 
			
		||||
    {
 | 
			
		||||
        visibleRange = constrainedRange;
 | 
			
		||||
 | 
			
		||||
        updateThumbPosition();
 | 
			
		||||
 | 
			
		||||
        if (notification != dontSendNotification)
 | 
			
		||||
            triggerAsyncUpdate();
 | 
			
		||||
 | 
			
		||||
        if (notification == sendNotificationSync)
 | 
			
		||||
            handleUpdateNowIfNeeded();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setCurrentRange (double newStart, double newSize, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    setCurrentRange (Range<double> (newStart, newStart + newSize), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setCurrentRangeStart (double newStart, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    setCurrentRange (visibleRange.movedToStartAt (newStart), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setSingleStepSize (double newSingleStepSize) noexcept
 | 
			
		||||
{
 | 
			
		||||
    singleStepSize = newSingleStepSize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::moveScrollbarInSteps (int howManySteps, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    return setCurrentRange (visibleRange + howManySteps * singleStepSize, notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::moveScrollbarInPages (int howManyPages, NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    return setCurrentRange (visibleRange + howManyPages * visibleRange.getLength(), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::scrollToTop (NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    return setCurrentRange (visibleRange.movedToStartAt (getMinimumRangeLimit()), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::scrollToBottom (NotificationType notification)
 | 
			
		||||
{
 | 
			
		||||
    return setCurrentRange (visibleRange.movedToEndAt (getMaximumRangeLimit()), notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setButtonRepeatSpeed (int newInitialDelay,
 | 
			
		||||
                                      int newRepeatDelay,
 | 
			
		||||
                                      int newMinimumDelay)
 | 
			
		||||
{
 | 
			
		||||
    initialDelayInMillisecs = newInitialDelay;
 | 
			
		||||
    repeatDelayInMillisecs  = newRepeatDelay;
 | 
			
		||||
    minimumDelayInMillisecs = newMinimumDelay;
 | 
			
		||||
 | 
			
		||||
    if (upButton != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        upButton  ->setRepeatSpeed (newInitialDelay, newRepeatDelay, newMinimumDelay);
 | 
			
		||||
        downButton->setRepeatSpeed (newInitialDelay, newRepeatDelay, newMinimumDelay);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ScrollBar::addListener (Listener* listener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.add (listener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::removeListener (Listener* listener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.remove (listener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::handleAsyncUpdate()
 | 
			
		||||
{
 | 
			
		||||
    auto start = visibleRange.getStart(); // (need to use a temp variable for VC7 compatibility)
 | 
			
		||||
    listeners.call ([this, start] (Listener& l) { l.scrollBarMoved (this, start); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ScrollBar::updateThumbPosition()
 | 
			
		||||
{
 | 
			
		||||
    auto minimumScrollBarThumbSize = getLookAndFeel().getMinimumScrollbarThumbSize (*this);
 | 
			
		||||
 | 
			
		||||
    int newThumbSize = roundToInt (totalRange.getLength() > 0 ? (visibleRange.getLength() * thumbAreaSize) / totalRange.getLength()
 | 
			
		||||
                                                              : thumbAreaSize);
 | 
			
		||||
 | 
			
		||||
    if (newThumbSize < minimumScrollBarThumbSize)
 | 
			
		||||
        newThumbSize = jmin (minimumScrollBarThumbSize, thumbAreaSize - 1);
 | 
			
		||||
 | 
			
		||||
    if (newThumbSize > thumbAreaSize)
 | 
			
		||||
        newThumbSize = thumbAreaSize;
 | 
			
		||||
 | 
			
		||||
    int newThumbStart = thumbAreaStart;
 | 
			
		||||
 | 
			
		||||
    if (totalRange.getLength() > visibleRange.getLength())
 | 
			
		||||
        newThumbStart += roundToInt (((visibleRange.getStart() - totalRange.getStart()) * (thumbAreaSize - newThumbSize))
 | 
			
		||||
                                         / (totalRange.getLength() - visibleRange.getLength()));
 | 
			
		||||
 | 
			
		||||
    Component::setVisible (getVisibility());
 | 
			
		||||
 | 
			
		||||
    if (thumbStart != newThumbStart  || thumbSize != newThumbSize)
 | 
			
		||||
    {
 | 
			
		||||
        auto repaintStart = jmin (thumbStart, newThumbStart) - 4;
 | 
			
		||||
        auto repaintSize = jmax (thumbStart + thumbSize, newThumbStart + newThumbSize) + 8 - repaintStart;
 | 
			
		||||
 | 
			
		||||
        if (vertical)
 | 
			
		||||
            repaint (0, repaintStart, getWidth(), repaintSize);
 | 
			
		||||
        else
 | 
			
		||||
            repaint (repaintStart, 0, repaintSize, getHeight());
 | 
			
		||||
 | 
			
		||||
        thumbStart = newThumbStart;
 | 
			
		||||
        thumbSize = newThumbSize;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setOrientation (bool shouldBeVertical)
 | 
			
		||||
{
 | 
			
		||||
    if (vertical != shouldBeVertical)
 | 
			
		||||
    {
 | 
			
		||||
        vertical = shouldBeVertical;
 | 
			
		||||
 | 
			
		||||
        if (upButton != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            upButton->direction    = vertical ? 0 : 3;
 | 
			
		||||
            downButton->direction  = vertical ? 2 : 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        updateThumbPosition();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setAutoHide (bool shouldHideWhenFullRange)
 | 
			
		||||
{
 | 
			
		||||
    autohides = shouldHideWhenFullRange;
 | 
			
		||||
    updateThumbPosition();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::autoHides() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return autohides;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ScrollBar::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    if (thumbAreaSize > 0)
 | 
			
		||||
    {
 | 
			
		||||
        auto& lf = getLookAndFeel();
 | 
			
		||||
 | 
			
		||||
        auto thumb = (thumbAreaSize > lf.getMinimumScrollbarThumbSize (*this))
 | 
			
		||||
                       ? thumbSize : 0;
 | 
			
		||||
 | 
			
		||||
        if (vertical)
 | 
			
		||||
            lf.drawScrollbar (g, *this, 0, thumbAreaStart, getWidth(), thumbAreaSize,
 | 
			
		||||
                              vertical, thumbStart, thumb, isMouseOver(), isMouseButtonDown());
 | 
			
		||||
        else
 | 
			
		||||
            lf.drawScrollbar (g, *this, thumbAreaStart, 0, thumbAreaSize, getHeight(),
 | 
			
		||||
                              vertical, thumbStart, thumb, isMouseOver(), isMouseButtonDown());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::lookAndFeelChanged()
 | 
			
		||||
{
 | 
			
		||||
    setComponentEffect (getLookAndFeel().getScrollbarEffect());
 | 
			
		||||
 | 
			
		||||
    if (isVisible())
 | 
			
		||||
        resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::resized()
 | 
			
		||||
{
 | 
			
		||||
    auto length = vertical ? getHeight() : getWidth();
 | 
			
		||||
 | 
			
		||||
    auto& lf = getLookAndFeel();
 | 
			
		||||
    bool buttonsVisible = lf.areScrollbarButtonsVisible();
 | 
			
		||||
    int buttonSize = 0;
 | 
			
		||||
 | 
			
		||||
    if (buttonsVisible)
 | 
			
		||||
    {
 | 
			
		||||
        if (upButton == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            upButton  .reset (new ScrollbarButton (vertical ? 0 : 3, *this));
 | 
			
		||||
            downButton.reset (new ScrollbarButton (vertical ? 2 : 1, *this));
 | 
			
		||||
 | 
			
		||||
            addAndMakeVisible (upButton.get());
 | 
			
		||||
            addAndMakeVisible (downButton.get());
 | 
			
		||||
 | 
			
		||||
            setButtonRepeatSpeed (initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        buttonSize = jmin (lf.getScrollbarButtonSize (*this), length / 2);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        upButton.reset();
 | 
			
		||||
        downButton.reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (length < 32 + lf.getMinimumScrollbarThumbSize (*this))
 | 
			
		||||
    {
 | 
			
		||||
        thumbAreaStart = length / 2;
 | 
			
		||||
        thumbAreaSize = 0;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        thumbAreaStart = buttonSize;
 | 
			
		||||
        thumbAreaSize = length - 2 * buttonSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (upButton != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        if (vertical)
 | 
			
		||||
        {
 | 
			
		||||
            upButton->setBounds (r.removeFromTop (buttonSize));
 | 
			
		||||
            downButton->setBounds (r.removeFromBottom (buttonSize));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            upButton->setBounds (r.removeFromLeft (buttonSize));
 | 
			
		||||
            downButton->setBounds (r.removeFromRight (buttonSize));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateThumbPosition();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::parentHierarchyChanged()
 | 
			
		||||
{
 | 
			
		||||
    lookAndFeelChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::mouseDown (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    isDraggingThumb = false;
 | 
			
		||||
    lastMousePos = vertical ? e.y : e.x;
 | 
			
		||||
    dragStartMousePos = lastMousePos;
 | 
			
		||||
    dragStartRange = visibleRange.getStart();
 | 
			
		||||
 | 
			
		||||
    if (dragStartMousePos < thumbStart)
 | 
			
		||||
    {
 | 
			
		||||
        moveScrollbarInPages (-1);
 | 
			
		||||
        startTimer (400);
 | 
			
		||||
    }
 | 
			
		||||
    else if (dragStartMousePos >= thumbStart + thumbSize)
 | 
			
		||||
    {
 | 
			
		||||
        moveScrollbarInPages (1);
 | 
			
		||||
        startTimer (400);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        isDraggingThumb = (thumbAreaSize > getLookAndFeel().getMinimumScrollbarThumbSize (*this))
 | 
			
		||||
                            && (thumbAreaSize > thumbSize);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    auto mousePos = vertical ? e.y : e.x;
 | 
			
		||||
 | 
			
		||||
    if (isDraggingThumb && lastMousePos != mousePos && thumbAreaSize > thumbSize)
 | 
			
		||||
    {
 | 
			
		||||
        auto deltaPixels = mousePos - dragStartMousePos;
 | 
			
		||||
 | 
			
		||||
        setCurrentRangeStart (dragStartRange
 | 
			
		||||
                                + deltaPixels * (totalRange.getLength() - visibleRange.getLength())
 | 
			
		||||
                                    / (thumbAreaSize - thumbSize));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lastMousePos = mousePos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    isDraggingThumb = false;
 | 
			
		||||
    stopTimer();
 | 
			
		||||
    repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel)
 | 
			
		||||
{
 | 
			
		||||
    float increment = 10.0f * (vertical ? wheel.deltaY : wheel.deltaX);
 | 
			
		||||
 | 
			
		||||
    if (increment < 0)
 | 
			
		||||
        increment = jmin (increment, -1.0f);
 | 
			
		||||
    else if (increment > 0)
 | 
			
		||||
        increment = jmax (increment, 1.0f);
 | 
			
		||||
 | 
			
		||||
    setCurrentRange (visibleRange - singleStepSize * increment);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::timerCallback()
 | 
			
		||||
{
 | 
			
		||||
    if (isMouseButtonDown())
 | 
			
		||||
    {
 | 
			
		||||
        startTimer (40);
 | 
			
		||||
 | 
			
		||||
        if (lastMousePos < thumbStart)
 | 
			
		||||
            setCurrentRange (visibleRange - visibleRange.getLength());
 | 
			
		||||
        else if (lastMousePos > thumbStart + thumbSize)
 | 
			
		||||
            setCurrentRangeStart (visibleRange.getEnd());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        stopTimer();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::keyPressed (const KeyPress& key)
 | 
			
		||||
{
 | 
			
		||||
    if (isVisible())
 | 
			
		||||
    {
 | 
			
		||||
        if (key == KeyPress::upKey || key == KeyPress::leftKey)    return moveScrollbarInSteps (-1);
 | 
			
		||||
        if (key == KeyPress::downKey || key == KeyPress::rightKey) return moveScrollbarInSteps (1);
 | 
			
		||||
        if (key == KeyPress::pageUpKey)                            return moveScrollbarInPages (-1);
 | 
			
		||||
        if (key == KeyPress::pageDownKey)                          return moveScrollbarInPages (1);
 | 
			
		||||
        if (key == KeyPress::homeKey)                              return scrollToTop();
 | 
			
		||||
        if (key == KeyPress::endKey)                               return scrollToBottom();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ScrollBar::setVisible (bool shouldBeVisible)
 | 
			
		||||
{
 | 
			
		||||
    if (userVisibilityFlag != shouldBeVisible)
 | 
			
		||||
    {
 | 
			
		||||
        userVisibilityFlag = shouldBeVisible;
 | 
			
		||||
        Component::setVisible (getVisibility());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ScrollBar::getVisibility() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (! userVisibilityFlag)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    return (! autohides) || (totalRange.getLength() > visibleRange.getLength()
 | 
			
		||||
                                    && visibleRange.getLength() > 0.0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> ScrollBar::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    class ValueInterface  : public AccessibilityRangedNumericValueInterface
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        explicit ValueInterface (ScrollBar& scrollBarToWrap)  : scrollBar (scrollBarToWrap) {}
 | 
			
		||||
 | 
			
		||||
        bool isReadOnly() const override          { return false; }
 | 
			
		||||
 | 
			
		||||
        double getCurrentValue() const override   { return scrollBar.getCurrentRangeStart(); }
 | 
			
		||||
        void setValue (double newValue) override  { scrollBar.setCurrentRangeStart (newValue); }
 | 
			
		||||
 | 
			
		||||
        AccessibleValueRange getRange() const override
 | 
			
		||||
        {
 | 
			
		||||
            if (scrollBar.getRangeLimit().isEmpty())
 | 
			
		||||
                return {};
 | 
			
		||||
 | 
			
		||||
            return { { scrollBar.getMinimumRangeLimit(), scrollBar.getMaximumRangeLimit() },
 | 
			
		||||
                     scrollBar.getSingleStepSize() };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        ScrollBar& scrollBar;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueInterface)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this,
 | 
			
		||||
                                                   AccessibilityRole::scrollBar,
 | 
			
		||||
                                                   AccessibilityActions{},
 | 
			
		||||
                                                   AccessibilityHandler::Interfaces { std::make_unique<ValueInterface> (*this) });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,440 +1,440 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A scrollbar component.
 | 
			
		||||
 | 
			
		||||
    To use a scrollbar, set up its total range using the setRangeLimits() method - this
 | 
			
		||||
    sets the range of values it can represent. Then you can use setCurrentRange() to
 | 
			
		||||
    change the position and size of the scrollbar's 'thumb'.
 | 
			
		||||
 | 
			
		||||
    Registering a ScrollBar::Listener with the scrollbar will allow you to find out when
 | 
			
		||||
    the user moves it, and you can use the getCurrentRangeStart() to find out where
 | 
			
		||||
    they moved it to.
 | 
			
		||||
 | 
			
		||||
    The scrollbar will adjust its own visibility according to whether its thumb size
 | 
			
		||||
    allows it to actually be scrolled.
 | 
			
		||||
 | 
			
		||||
    For most purposes, it's probably easier to use a Viewport or ListBox
 | 
			
		||||
    instead of handling a scrollbar directly.
 | 
			
		||||
 | 
			
		||||
    @see ScrollBar::Listener
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ScrollBar  : public Component,
 | 
			
		||||
                             public AsyncUpdater,
 | 
			
		||||
                             private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a Scrollbar.
 | 
			
		||||
        @param isVertical      specifies whether the bar should be a vertical or horizontal
 | 
			
		||||
    */
 | 
			
		||||
    ScrollBar (bool isVertical);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ScrollBar() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns true if the scrollbar is vertical, false if it's horizontal. */
 | 
			
		||||
    bool isVertical() const noexcept                                { return vertical; }
 | 
			
		||||
 | 
			
		||||
    /** Changes the scrollbar's direction.
 | 
			
		||||
 | 
			
		||||
        You'll also need to resize the bar appropriately - this just changes its internal
 | 
			
		||||
        layout.
 | 
			
		||||
 | 
			
		||||
        @param shouldBeVertical     true makes it vertical; false makes it horizontal.
 | 
			
		||||
    */
 | 
			
		||||
    void setOrientation (bool shouldBeVertical);
 | 
			
		||||
 | 
			
		||||
    /** Tells the scrollbar whether to make itself invisible when not needed.
 | 
			
		||||
 | 
			
		||||
        The default behaviour is for a scrollbar to become invisible when the thumb
 | 
			
		||||
        fills the whole of its range (i.e. when it can't be moved). Setting this
 | 
			
		||||
        value to false forces the bar to always be visible.
 | 
			
		||||
        @see autoHides()
 | 
			
		||||
    */
 | 
			
		||||
    void setAutoHide (bool shouldHideWhenFullRange);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if this scrollbar is set to auto-hide when its thumb is as big
 | 
			
		||||
        as its maximum range.
 | 
			
		||||
        @see setAutoHide
 | 
			
		||||
    */
 | 
			
		||||
    bool autoHides() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the minimum and maximum values that the bar will move between.
 | 
			
		||||
 | 
			
		||||
        The bar's thumb will always be constrained so that the entire thumb lies
 | 
			
		||||
        within this range.
 | 
			
		||||
 | 
			
		||||
        @param newRangeLimit    the new range.
 | 
			
		||||
        @param notification     whether to send a notification of the change to listeners.
 | 
			
		||||
                                A notification will only be sent if the range has changed.
 | 
			
		||||
 | 
			
		||||
        @see setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    void setRangeLimits (Range<double> newRangeLimit,
 | 
			
		||||
                         NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Sets the minimum and maximum values that the bar will move between.
 | 
			
		||||
 | 
			
		||||
        The bar's thumb will always be constrained so that the entire thumb lies
 | 
			
		||||
        within this range.
 | 
			
		||||
 | 
			
		||||
        @param minimum         the new range minimum.
 | 
			
		||||
        @param maximum         the new range maximum.
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the range has changed.
 | 
			
		||||
 | 
			
		||||
        @see setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    void setRangeLimits (double minimum, double maximum,
 | 
			
		||||
                         NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current limits on the thumb position.
 | 
			
		||||
        @see setRangeLimits
 | 
			
		||||
    */
 | 
			
		||||
    Range<double> getRangeLimit() const noexcept                    { return totalRange; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the lower value that the thumb can be set to.
 | 
			
		||||
 | 
			
		||||
        This is the value set by setRangeLimits().
 | 
			
		||||
    */
 | 
			
		||||
    double getMinimumRangeLimit() const noexcept                    { return totalRange.getStart(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the upper value that the thumb can be set to.
 | 
			
		||||
 | 
			
		||||
        This is the value set by setRangeLimits().
 | 
			
		||||
    */
 | 
			
		||||
    double getMaximumRangeLimit() const noexcept                    { return totalRange.getEnd(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the position of the scrollbar's 'thumb'.
 | 
			
		||||
 | 
			
		||||
        This sets both the position and size of the thumb - to just set the position without
 | 
			
		||||
        changing the size, you can use setCurrentRangeStart().
 | 
			
		||||
 | 
			
		||||
        If this method call actually changes the scrollbar's position, it will trigger an
 | 
			
		||||
        asynchronous call to ScrollBar::Listener::scrollBarMoved() for all the listeners that
 | 
			
		||||
        are registered.
 | 
			
		||||
 | 
			
		||||
        The notification parameter can be used to optionally send or inhibit a callback to
 | 
			
		||||
        any scrollbar listeners.
 | 
			
		||||
 | 
			
		||||
        @returns true if the range was changed, or false if nothing was changed.
 | 
			
		||||
        @see getCurrentRange. setCurrentRangeStart
 | 
			
		||||
    */
 | 
			
		||||
    bool setCurrentRange (Range<double> newRange,
 | 
			
		||||
                          NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Changes the position of the scrollbar's 'thumb'.
 | 
			
		||||
 | 
			
		||||
        This sets both the position and size of the thumb - to just set the position without
 | 
			
		||||
        changing the size, you can use setCurrentRangeStart().
 | 
			
		||||
 | 
			
		||||
        @param newStart     the top (or left) of the thumb, in the range
 | 
			
		||||
                            getMinimumRangeLimit() <= newStart <= getMaximumRangeLimit(). If the
 | 
			
		||||
                            value is beyond these limits, it will be clipped.
 | 
			
		||||
        @param newSize      the size of the thumb, such that
 | 
			
		||||
                            getMinimumRangeLimit() <= newStart + newSize <= getMaximumRangeLimit(). If the
 | 
			
		||||
                            size is beyond these limits, it will be clipped.
 | 
			
		||||
        @param notification specifies if and how a callback should be made to any listeners
 | 
			
		||||
                            if the range actually changes
 | 
			
		||||
        @see setCurrentRangeStart, getCurrentRangeStart, getCurrentRangeSize
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentRange (double newStart, double newSize,
 | 
			
		||||
                          NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Moves the bar's thumb position.
 | 
			
		||||
 | 
			
		||||
        This will move the thumb position without changing the thumb size. Note
 | 
			
		||||
        that the maximum thumb start position is (getMaximumRangeLimit() - getCurrentRangeSize()).
 | 
			
		||||
 | 
			
		||||
        If this method call actually changes the scrollbar's position, it will trigger an
 | 
			
		||||
        asynchronous call to ScrollBar::Listener::scrollBarMoved() for all the listeners that
 | 
			
		||||
        are registered.
 | 
			
		||||
 | 
			
		||||
        @see setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentRangeStart (double newStart,
 | 
			
		||||
                               NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current thumb range.
 | 
			
		||||
        @see getCurrentRange, setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    Range<double> getCurrentRange() const noexcept                  { return visibleRange; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the position of the top of the thumb.
 | 
			
		||||
        @see getCurrentRange, setCurrentRangeStart
 | 
			
		||||
    */
 | 
			
		||||
    double getCurrentRangeStart() const noexcept                    { return visibleRange.getStart(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the current size of the thumb.
 | 
			
		||||
        @see getCurrentRange, setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    double getCurrentRangeSize() const noexcept                     { return visibleRange.getLength(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the amount by which the up and down buttons will move the bar.
 | 
			
		||||
 | 
			
		||||
        The value here is in terms of the total range, and is added or subtracted
 | 
			
		||||
        from the thumb position when the user clicks an up/down (or left/right) button.
 | 
			
		||||
    */
 | 
			
		||||
    void setSingleStepSize (double newSingleStepSize) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current step size.
 | 
			
		||||
        @see setSingleStepSize
 | 
			
		||||
    */
 | 
			
		||||
    double getSingleStepSize() const noexcept                       { return singleStepSize; }
 | 
			
		||||
 | 
			
		||||
    /** Moves the scrollbar by a number of single-steps.
 | 
			
		||||
 | 
			
		||||
        This will move the bar by a multiple of its single-step interval (as
 | 
			
		||||
        specified using the setSingleStepSize() method).
 | 
			
		||||
 | 
			
		||||
        A positive value here will move the bar down or to the right, a negative
 | 
			
		||||
        value moves it up or to the left.
 | 
			
		||||
 | 
			
		||||
        @param howManySteps    the number of steps to move the scrollbar
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the position has changed.
 | 
			
		||||
 | 
			
		||||
        @returns true if the scrollbar's position actually changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool moveScrollbarInSteps (int howManySteps,
 | 
			
		||||
                               NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Moves the scroll bar up or down in pages.
 | 
			
		||||
 | 
			
		||||
        This will move the bar by a multiple of its current thumb size, effectively
 | 
			
		||||
        doing a page-up or down.
 | 
			
		||||
 | 
			
		||||
        A positive value here will move the bar down or to the right, a negative
 | 
			
		||||
        value moves it up or to the left.
 | 
			
		||||
 | 
			
		||||
        @param howManyPages    the number of pages to move the scrollbar
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the position has changed.
 | 
			
		||||
 | 
			
		||||
        @returns true if the scrollbar's position actually changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool moveScrollbarInPages (int howManyPages,
 | 
			
		||||
                               NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Scrolls to the top (or left).
 | 
			
		||||
        This is the same as calling setCurrentRangeStart (getMinimumRangeLimit());
 | 
			
		||||
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the position has changed.
 | 
			
		||||
 | 
			
		||||
        @returns true if the scrollbar's position actually changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool scrollToTop (NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Scrolls to the bottom (or right).
 | 
			
		||||
        This is the same as calling setCurrentRangeStart (getMaximumRangeLimit() - getCurrentRangeSize());
 | 
			
		||||
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the position has changed.
 | 
			
		||||
 | 
			
		||||
        @returns true if the scrollbar's position actually changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool scrollToBottom (NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Changes the delay before the up and down buttons autorepeat when they are held
 | 
			
		||||
        down.
 | 
			
		||||
 | 
			
		||||
        For an explanation of what the parameters are for, see Button::setRepeatSpeed().
 | 
			
		||||
 | 
			
		||||
        @see Button::setRepeatSpeed
 | 
			
		||||
    */
 | 
			
		||||
    void setButtonRepeatSpeed (int initialDelayInMillisecs,
 | 
			
		||||
                               int repeatDelayInMillisecs,
 | 
			
		||||
                               int minimumDelayInMillisecs = -1);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the component.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        backgroundColourId          = 0x1000300,    /**< The background colour of the scrollbar. */
 | 
			
		||||
        thumbColourId               = 0x1000400,    /**< A base colour to use for the thumb. The look and feel will probably use variations on this colour. */
 | 
			
		||||
        trackColourId               = 0x1000401     /**< A base colour to use for the slot area of the bar. The look and feel will probably use variations on this colour. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
        A class for receiving events from a ScrollBar.
 | 
			
		||||
 | 
			
		||||
        You can register a ScrollBar::Listener with a ScrollBar using the ScrollBar::addListener()
 | 
			
		||||
        method, and it will be called when the bar's position changes.
 | 
			
		||||
 | 
			
		||||
        @see ScrollBar::addListener, ScrollBar::removeListener
 | 
			
		||||
    */
 | 
			
		||||
    class JUCE_API  Listener
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        /** Destructor. */
 | 
			
		||||
        virtual ~Listener() = default;
 | 
			
		||||
 | 
			
		||||
        /** Called when a ScrollBar is moved.
 | 
			
		||||
 | 
			
		||||
            @param scrollBarThatHasMoved    the bar that has moved
 | 
			
		||||
            @param newRangeStart            the new range start of this bar
 | 
			
		||||
        */
 | 
			
		||||
        virtual void scrollBarMoved (ScrollBar* scrollBarThatHasMoved,
 | 
			
		||||
                                     double newRangeStart) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Registers a listener that will be called when the scrollbar is moved. */
 | 
			
		||||
    void addListener (Listener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Deregisters a previously-registered listener. */
 | 
			
		||||
    void removeListener (Listener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes to provide
 | 
			
		||||
        scrollbar-drawing functionality.
 | 
			
		||||
    */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual bool areScrollbarButtonsVisible() = 0;
 | 
			
		||||
 | 
			
		||||
        /** Draws one of the buttons on a scrollbar.
 | 
			
		||||
 | 
			
		||||
            @param g                    the context to draw into
 | 
			
		||||
            @param scrollbar            the bar itself
 | 
			
		||||
            @param width                the width of the button
 | 
			
		||||
            @param height               the height of the button
 | 
			
		||||
            @param buttonDirection      the direction of the button, where 0 = up, 1 = right, 2 = down, 3 = left
 | 
			
		||||
            @param isScrollbarVertical  true if it's a vertical bar, false if horizontal
 | 
			
		||||
            @param isMouseOverButton    whether the mouse is currently over the button (also true if it's held down)
 | 
			
		||||
            @param isButtonDown         whether the mouse button's held down
 | 
			
		||||
        */
 | 
			
		||||
        virtual void drawScrollbarButton (Graphics& g,
 | 
			
		||||
                                          ScrollBar& scrollbar,
 | 
			
		||||
                                          int width, int height,
 | 
			
		||||
                                          int buttonDirection,
 | 
			
		||||
                                          bool isScrollbarVertical,
 | 
			
		||||
                                          bool isMouseOverButton,
 | 
			
		||||
                                          bool isButtonDown) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Draws the thumb area of a scrollbar.
 | 
			
		||||
 | 
			
		||||
            @param g                    the context to draw into
 | 
			
		||||
            @param scrollbar            the bar itself
 | 
			
		||||
            @param x                    the x position of the left edge of the thumb area to draw in
 | 
			
		||||
            @param y                    the y position of the top edge of the thumb area to draw in
 | 
			
		||||
            @param width                the width of the thumb area to draw in
 | 
			
		||||
            @param height               the height of the thumb area to draw in
 | 
			
		||||
            @param isScrollbarVertical  true if it's a vertical bar, false if horizontal
 | 
			
		||||
            @param thumbStartPosition   for vertical bars, the y coordinate of the top of the
 | 
			
		||||
                                        thumb, or its x position for horizontal bars
 | 
			
		||||
            @param thumbSize            for vertical bars, the height of the thumb, or its width for
 | 
			
		||||
                                        horizontal bars. This may be 0 if the thumb shouldn't be drawn.
 | 
			
		||||
            @param isMouseOver          whether the mouse is over the thumb area, also true if the mouse is
 | 
			
		||||
                                        currently dragging the thumb
 | 
			
		||||
            @param isMouseDown          whether the mouse is currently dragging the scrollbar
 | 
			
		||||
        */
 | 
			
		||||
        virtual void drawScrollbar (Graphics& g, ScrollBar& scrollbar,
 | 
			
		||||
                                    int x, int y, int width, int height,
 | 
			
		||||
                                    bool isScrollbarVertical,
 | 
			
		||||
                                    int thumbStartPosition,
 | 
			
		||||
                                    int thumbSize,
 | 
			
		||||
                                    bool isMouseOver,
 | 
			
		||||
                                    bool isMouseDown) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns the component effect to use for a scrollbar */
 | 
			
		||||
        virtual ImageEffectFilter* getScrollbarEffect() = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns the minimum length in pixels to use for a scrollbar thumb. */
 | 
			
		||||
        virtual int getMinimumScrollbarThumbSize (ScrollBar&) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns the default thickness to use for a scrollbar. */
 | 
			
		||||
        virtual int getDefaultScrollbarWidth() = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns the length in pixels to use for a scrollbar button. */
 | 
			
		||||
        virtual int getScrollbarButtonSize (ScrollBar&) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyPressed (const KeyPress&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp   (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void parentHierarchyChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void setVisible (bool) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Range<double> totalRange { 0.0, 1.0 }, visibleRange { 0.0, 1.0 };
 | 
			
		||||
    double singleStepSize = 0.1, dragStartRange = 0;
 | 
			
		||||
    int thumbAreaStart = 0, thumbAreaSize = 0, thumbStart = 0, thumbSize = 0;
 | 
			
		||||
    int dragStartMousePos = 0, lastMousePos = 0;
 | 
			
		||||
    int initialDelayInMillisecs = 100, repeatDelayInMillisecs = 50, minimumDelayInMillisecs = 10;
 | 
			
		||||
    bool vertical, isDraggingThumb = false, autohides = true, userVisibilityFlag = false;
 | 
			
		||||
    class ScrollbarButton;
 | 
			
		||||
    std::unique_ptr<ScrollbarButton> upButton, downButton;
 | 
			
		||||
    ListenerList<Listener> listeners;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void handleAsyncUpdate() override;
 | 
			
		||||
    void updateThumbPosition();
 | 
			
		||||
    void timerCallback() override;
 | 
			
		||||
    bool getVisibility() const noexcept;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScrollBar)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A scrollbar component.
 | 
			
		||||
 | 
			
		||||
    To use a scrollbar, set up its total range using the setRangeLimits() method - this
 | 
			
		||||
    sets the range of values it can represent. Then you can use setCurrentRange() to
 | 
			
		||||
    change the position and size of the scrollbar's 'thumb'.
 | 
			
		||||
 | 
			
		||||
    Registering a ScrollBar::Listener with the scrollbar will allow you to find out when
 | 
			
		||||
    the user moves it, and you can use the getCurrentRangeStart() to find out where
 | 
			
		||||
    they moved it to.
 | 
			
		||||
 | 
			
		||||
    The scrollbar will adjust its own visibility according to whether its thumb size
 | 
			
		||||
    allows it to actually be scrolled.
 | 
			
		||||
 | 
			
		||||
    For most purposes, it's probably easier to use a Viewport or ListBox
 | 
			
		||||
    instead of handling a scrollbar directly.
 | 
			
		||||
 | 
			
		||||
    @see ScrollBar::Listener
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ScrollBar  : public Component,
 | 
			
		||||
                             public AsyncUpdater,
 | 
			
		||||
                             private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a Scrollbar.
 | 
			
		||||
        @param isVertical      specifies whether the bar should be a vertical or horizontal
 | 
			
		||||
    */
 | 
			
		||||
    ScrollBar (bool isVertical);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~ScrollBar() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns true if the scrollbar is vertical, false if it's horizontal. */
 | 
			
		||||
    bool isVertical() const noexcept                                { return vertical; }
 | 
			
		||||
 | 
			
		||||
    /** Changes the scrollbar's direction.
 | 
			
		||||
 | 
			
		||||
        You'll also need to resize the bar appropriately - this just changes its internal
 | 
			
		||||
        layout.
 | 
			
		||||
 | 
			
		||||
        @param shouldBeVertical     true makes it vertical; false makes it horizontal.
 | 
			
		||||
    */
 | 
			
		||||
    void setOrientation (bool shouldBeVertical);
 | 
			
		||||
 | 
			
		||||
    /** Tells the scrollbar whether to make itself invisible when not needed.
 | 
			
		||||
 | 
			
		||||
        The default behaviour is for a scrollbar to become invisible when the thumb
 | 
			
		||||
        fills the whole of its range (i.e. when it can't be moved). Setting this
 | 
			
		||||
        value to false forces the bar to always be visible.
 | 
			
		||||
        @see autoHides()
 | 
			
		||||
    */
 | 
			
		||||
    void setAutoHide (bool shouldHideWhenFullRange);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if this scrollbar is set to auto-hide when its thumb is as big
 | 
			
		||||
        as its maximum range.
 | 
			
		||||
        @see setAutoHide
 | 
			
		||||
    */
 | 
			
		||||
    bool autoHides() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the minimum and maximum values that the bar will move between.
 | 
			
		||||
 | 
			
		||||
        The bar's thumb will always be constrained so that the entire thumb lies
 | 
			
		||||
        within this range.
 | 
			
		||||
 | 
			
		||||
        @param newRangeLimit    the new range.
 | 
			
		||||
        @param notification     whether to send a notification of the change to listeners.
 | 
			
		||||
                                A notification will only be sent if the range has changed.
 | 
			
		||||
 | 
			
		||||
        @see setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    void setRangeLimits (Range<double> newRangeLimit,
 | 
			
		||||
                         NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Sets the minimum and maximum values that the bar will move between.
 | 
			
		||||
 | 
			
		||||
        The bar's thumb will always be constrained so that the entire thumb lies
 | 
			
		||||
        within this range.
 | 
			
		||||
 | 
			
		||||
        @param minimum         the new range minimum.
 | 
			
		||||
        @param maximum         the new range maximum.
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the range has changed.
 | 
			
		||||
 | 
			
		||||
        @see setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    void setRangeLimits (double minimum, double maximum,
 | 
			
		||||
                         NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current limits on the thumb position.
 | 
			
		||||
        @see setRangeLimits
 | 
			
		||||
    */
 | 
			
		||||
    Range<double> getRangeLimit() const noexcept                    { return totalRange; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the lower value that the thumb can be set to.
 | 
			
		||||
 | 
			
		||||
        This is the value set by setRangeLimits().
 | 
			
		||||
    */
 | 
			
		||||
    double getMinimumRangeLimit() const noexcept                    { return totalRange.getStart(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the upper value that the thumb can be set to.
 | 
			
		||||
 | 
			
		||||
        This is the value set by setRangeLimits().
 | 
			
		||||
    */
 | 
			
		||||
    double getMaximumRangeLimit() const noexcept                    { return totalRange.getEnd(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the position of the scrollbar's 'thumb'.
 | 
			
		||||
 | 
			
		||||
        This sets both the position and size of the thumb - to just set the position without
 | 
			
		||||
        changing the size, you can use setCurrentRangeStart().
 | 
			
		||||
 | 
			
		||||
        If this method call actually changes the scrollbar's position, it will trigger an
 | 
			
		||||
        asynchronous call to ScrollBar::Listener::scrollBarMoved() for all the listeners that
 | 
			
		||||
        are registered.
 | 
			
		||||
 | 
			
		||||
        The notification parameter can be used to optionally send or inhibit a callback to
 | 
			
		||||
        any scrollbar listeners.
 | 
			
		||||
 | 
			
		||||
        @returns true if the range was changed, or false if nothing was changed.
 | 
			
		||||
        @see getCurrentRange. setCurrentRangeStart
 | 
			
		||||
    */
 | 
			
		||||
    bool setCurrentRange (Range<double> newRange,
 | 
			
		||||
                          NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Changes the position of the scrollbar's 'thumb'.
 | 
			
		||||
 | 
			
		||||
        This sets both the position and size of the thumb - to just set the position without
 | 
			
		||||
        changing the size, you can use setCurrentRangeStart().
 | 
			
		||||
 | 
			
		||||
        @param newStart     the top (or left) of the thumb, in the range
 | 
			
		||||
                            getMinimumRangeLimit() <= newStart <= getMaximumRangeLimit(). If the
 | 
			
		||||
                            value is beyond these limits, it will be clipped.
 | 
			
		||||
        @param newSize      the size of the thumb, such that
 | 
			
		||||
                            getMinimumRangeLimit() <= newStart + newSize <= getMaximumRangeLimit(). If the
 | 
			
		||||
                            size is beyond these limits, it will be clipped.
 | 
			
		||||
        @param notification specifies if and how a callback should be made to any listeners
 | 
			
		||||
                            if the range actually changes
 | 
			
		||||
        @see setCurrentRangeStart, getCurrentRangeStart, getCurrentRangeSize
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentRange (double newStart, double newSize,
 | 
			
		||||
                          NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Moves the bar's thumb position.
 | 
			
		||||
 | 
			
		||||
        This will move the thumb position without changing the thumb size. Note
 | 
			
		||||
        that the maximum thumb start position is (getMaximumRangeLimit() - getCurrentRangeSize()).
 | 
			
		||||
 | 
			
		||||
        If this method call actually changes the scrollbar's position, it will trigger an
 | 
			
		||||
        asynchronous call to ScrollBar::Listener::scrollBarMoved() for all the listeners that
 | 
			
		||||
        are registered.
 | 
			
		||||
 | 
			
		||||
        @see setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentRangeStart (double newStart,
 | 
			
		||||
                               NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current thumb range.
 | 
			
		||||
        @see getCurrentRange, setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    Range<double> getCurrentRange() const noexcept                  { return visibleRange; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the position of the top of the thumb.
 | 
			
		||||
        @see getCurrentRange, setCurrentRangeStart
 | 
			
		||||
    */
 | 
			
		||||
    double getCurrentRangeStart() const noexcept                    { return visibleRange.getStart(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the current size of the thumb.
 | 
			
		||||
        @see getCurrentRange, setCurrentRange
 | 
			
		||||
    */
 | 
			
		||||
    double getCurrentRangeSize() const noexcept                     { return visibleRange.getLength(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the amount by which the up and down buttons will move the bar.
 | 
			
		||||
 | 
			
		||||
        The value here is in terms of the total range, and is added or subtracted
 | 
			
		||||
        from the thumb position when the user clicks an up/down (or left/right) button.
 | 
			
		||||
    */
 | 
			
		||||
    void setSingleStepSize (double newSingleStepSize) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current step size.
 | 
			
		||||
        @see setSingleStepSize
 | 
			
		||||
    */
 | 
			
		||||
    double getSingleStepSize() const noexcept                       { return singleStepSize; }
 | 
			
		||||
 | 
			
		||||
    /** Moves the scrollbar by a number of single-steps.
 | 
			
		||||
 | 
			
		||||
        This will move the bar by a multiple of its single-step interval (as
 | 
			
		||||
        specified using the setSingleStepSize() method).
 | 
			
		||||
 | 
			
		||||
        A positive value here will move the bar down or to the right, a negative
 | 
			
		||||
        value moves it up or to the left.
 | 
			
		||||
 | 
			
		||||
        @param howManySteps    the number of steps to move the scrollbar
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the position has changed.
 | 
			
		||||
 | 
			
		||||
        @returns true if the scrollbar's position actually changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool moveScrollbarInSteps (int howManySteps,
 | 
			
		||||
                               NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Moves the scroll bar up or down in pages.
 | 
			
		||||
 | 
			
		||||
        This will move the bar by a multiple of its current thumb size, effectively
 | 
			
		||||
        doing a page-up or down.
 | 
			
		||||
 | 
			
		||||
        A positive value here will move the bar down or to the right, a negative
 | 
			
		||||
        value moves it up or to the left.
 | 
			
		||||
 | 
			
		||||
        @param howManyPages    the number of pages to move the scrollbar
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the position has changed.
 | 
			
		||||
 | 
			
		||||
        @returns true if the scrollbar's position actually changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool moveScrollbarInPages (int howManyPages,
 | 
			
		||||
                               NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Scrolls to the top (or left).
 | 
			
		||||
        This is the same as calling setCurrentRangeStart (getMinimumRangeLimit());
 | 
			
		||||
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the position has changed.
 | 
			
		||||
 | 
			
		||||
        @returns true if the scrollbar's position actually changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool scrollToTop (NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Scrolls to the bottom (or right).
 | 
			
		||||
        This is the same as calling setCurrentRangeStart (getMaximumRangeLimit() - getCurrentRangeSize());
 | 
			
		||||
 | 
			
		||||
        @param notification    whether to send a notification of the change to listeners.
 | 
			
		||||
                               A notification will only be sent if the position has changed.
 | 
			
		||||
 | 
			
		||||
        @returns true if the scrollbar's position actually changed.
 | 
			
		||||
    */
 | 
			
		||||
    bool scrollToBottom (NotificationType notification = sendNotificationAsync);
 | 
			
		||||
 | 
			
		||||
    /** Changes the delay before the up and down buttons autorepeat when they are held
 | 
			
		||||
        down.
 | 
			
		||||
 | 
			
		||||
        For an explanation of what the parameters are for, see Button::setRepeatSpeed().
 | 
			
		||||
 | 
			
		||||
        @see Button::setRepeatSpeed
 | 
			
		||||
    */
 | 
			
		||||
    void setButtonRepeatSpeed (int initialDelayInMillisecs,
 | 
			
		||||
                               int repeatDelayInMillisecs,
 | 
			
		||||
                               int minimumDelayInMillisecs = -1);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the component.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        backgroundColourId          = 0x1000300,    /**< The background colour of the scrollbar. */
 | 
			
		||||
        thumbColourId               = 0x1000400,    /**< A base colour to use for the thumb. The look and feel will probably use variations on this colour. */
 | 
			
		||||
        trackColourId               = 0x1000401     /**< A base colour to use for the slot area of the bar. The look and feel will probably use variations on this colour. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
        A class for receiving events from a ScrollBar.
 | 
			
		||||
 | 
			
		||||
        You can register a ScrollBar::Listener with a ScrollBar using the ScrollBar::addListener()
 | 
			
		||||
        method, and it will be called when the bar's position changes.
 | 
			
		||||
 | 
			
		||||
        @see ScrollBar::addListener, ScrollBar::removeListener
 | 
			
		||||
    */
 | 
			
		||||
    class JUCE_API  Listener
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        /** Destructor. */
 | 
			
		||||
        virtual ~Listener() = default;
 | 
			
		||||
 | 
			
		||||
        /** Called when a ScrollBar is moved.
 | 
			
		||||
 | 
			
		||||
            @param scrollBarThatHasMoved    the bar that has moved
 | 
			
		||||
            @param newRangeStart            the new range start of this bar
 | 
			
		||||
        */
 | 
			
		||||
        virtual void scrollBarMoved (ScrollBar* scrollBarThatHasMoved,
 | 
			
		||||
                                     double newRangeStart) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Registers a listener that will be called when the scrollbar is moved. */
 | 
			
		||||
    void addListener (Listener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Deregisters a previously-registered listener. */
 | 
			
		||||
    void removeListener (Listener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes to provide
 | 
			
		||||
        scrollbar-drawing functionality.
 | 
			
		||||
    */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual bool areScrollbarButtonsVisible() = 0;
 | 
			
		||||
 | 
			
		||||
        /** Draws one of the buttons on a scrollbar.
 | 
			
		||||
 | 
			
		||||
            @param g                    the context to draw into
 | 
			
		||||
            @param scrollbar            the bar itself
 | 
			
		||||
            @param width                the width of the button
 | 
			
		||||
            @param height               the height of the button
 | 
			
		||||
            @param buttonDirection      the direction of the button, where 0 = up, 1 = right, 2 = down, 3 = left
 | 
			
		||||
            @param isScrollbarVertical  true if it's a vertical bar, false if horizontal
 | 
			
		||||
            @param isMouseOverButton    whether the mouse is currently over the button (also true if it's held down)
 | 
			
		||||
            @param isButtonDown         whether the mouse button's held down
 | 
			
		||||
        */
 | 
			
		||||
        virtual void drawScrollbarButton (Graphics& g,
 | 
			
		||||
                                          ScrollBar& scrollbar,
 | 
			
		||||
                                          int width, int height,
 | 
			
		||||
                                          int buttonDirection,
 | 
			
		||||
                                          bool isScrollbarVertical,
 | 
			
		||||
                                          bool isMouseOverButton,
 | 
			
		||||
                                          bool isButtonDown) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Draws the thumb area of a scrollbar.
 | 
			
		||||
 | 
			
		||||
            @param g                    the context to draw into
 | 
			
		||||
            @param scrollbar            the bar itself
 | 
			
		||||
            @param x                    the x position of the left edge of the thumb area to draw in
 | 
			
		||||
            @param y                    the y position of the top edge of the thumb area to draw in
 | 
			
		||||
            @param width                the width of the thumb area to draw in
 | 
			
		||||
            @param height               the height of the thumb area to draw in
 | 
			
		||||
            @param isScrollbarVertical  true if it's a vertical bar, false if horizontal
 | 
			
		||||
            @param thumbStartPosition   for vertical bars, the y coordinate of the top of the
 | 
			
		||||
                                        thumb, or its x position for horizontal bars
 | 
			
		||||
            @param thumbSize            for vertical bars, the height of the thumb, or its width for
 | 
			
		||||
                                        horizontal bars. This may be 0 if the thumb shouldn't be drawn.
 | 
			
		||||
            @param isMouseOver          whether the mouse is over the thumb area, also true if the mouse is
 | 
			
		||||
                                        currently dragging the thumb
 | 
			
		||||
            @param isMouseDown          whether the mouse is currently dragging the scrollbar
 | 
			
		||||
        */
 | 
			
		||||
        virtual void drawScrollbar (Graphics& g, ScrollBar& scrollbar,
 | 
			
		||||
                                    int x, int y, int width, int height,
 | 
			
		||||
                                    bool isScrollbarVertical,
 | 
			
		||||
                                    int thumbStartPosition,
 | 
			
		||||
                                    int thumbSize,
 | 
			
		||||
                                    bool isMouseOver,
 | 
			
		||||
                                    bool isMouseDown) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns the component effect to use for a scrollbar */
 | 
			
		||||
        virtual ImageEffectFilter* getScrollbarEffect() = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns the minimum length in pixels to use for a scrollbar thumb. */
 | 
			
		||||
        virtual int getMinimumScrollbarThumbSize (ScrollBar&) = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns the default thickness to use for a scrollbar. */
 | 
			
		||||
        virtual int getDefaultScrollbarWidth() = 0;
 | 
			
		||||
 | 
			
		||||
        /** Returns the length in pixels to use for a scrollbar button. */
 | 
			
		||||
        virtual int getScrollbarButtonSize (ScrollBar&) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyPressed (const KeyPress&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp   (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void parentHierarchyChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void setVisible (bool) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Range<double> totalRange { 0.0, 1.0 }, visibleRange { 0.0, 1.0 };
 | 
			
		||||
    double singleStepSize = 0.1, dragStartRange = 0;
 | 
			
		||||
    int thumbAreaStart = 0, thumbAreaSize = 0, thumbStart = 0, thumbSize = 0;
 | 
			
		||||
    int dragStartMousePos = 0, lastMousePos = 0;
 | 
			
		||||
    int initialDelayInMillisecs = 100, repeatDelayInMillisecs = 50, minimumDelayInMillisecs = 10;
 | 
			
		||||
    bool vertical, isDraggingThumb = false, autohides = true, userVisibilityFlag = false;
 | 
			
		||||
    class ScrollbarButton;
 | 
			
		||||
    std::unique_ptr<ScrollbarButton> upButton, downButton;
 | 
			
		||||
    ListenerList<Listener> listeners;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void handleAsyncUpdate() override;
 | 
			
		||||
    void updateThumbPosition();
 | 
			
		||||
    void timerCallback() override;
 | 
			
		||||
    bool getVisibility() const noexcept;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScrollBar)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,303 +1,303 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
SidePanel::SidePanel (StringRef title, int width, bool positionOnLeft,
 | 
			
		||||
                      Component* contentToDisplay, bool deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
    : titleLabel ("titleLabel", title),
 | 
			
		||||
      isOnLeft (positionOnLeft),
 | 
			
		||||
      panelWidth (width)
 | 
			
		||||
{
 | 
			
		||||
    lookAndFeelChanged();
 | 
			
		||||
 | 
			
		||||
    addAndMakeVisible (titleLabel);
 | 
			
		||||
 | 
			
		||||
    dismissButton.onClick = [this] { showOrHide (false); };
 | 
			
		||||
    addAndMakeVisible (dismissButton);
 | 
			
		||||
 | 
			
		||||
    auto& desktop = Desktop::getInstance();
 | 
			
		||||
 | 
			
		||||
    desktop.addGlobalMouseListener (this);
 | 
			
		||||
    desktop.getAnimator().addChangeListener (this);
 | 
			
		||||
 | 
			
		||||
    if (contentToDisplay != nullptr)
 | 
			
		||||
        setContent (contentToDisplay, deleteComponentWhenNoLongerNeeded);
 | 
			
		||||
 | 
			
		||||
    setOpaque (false);
 | 
			
		||||
    setVisible (false);
 | 
			
		||||
    setAlwaysOnTop (true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SidePanel::~SidePanel()
 | 
			
		||||
{
 | 
			
		||||
    auto& desktop = Desktop::getInstance();
 | 
			
		||||
 | 
			
		||||
    desktop.removeGlobalMouseListener (this);
 | 
			
		||||
    desktop.getAnimator().removeChangeListener (this);
 | 
			
		||||
 | 
			
		||||
    if (parent != nullptr)
 | 
			
		||||
        parent->removeComponentListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::setContent (Component* newContent, bool deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
{
 | 
			
		||||
    if (contentComponent.get() != newContent)
 | 
			
		||||
    {
 | 
			
		||||
        if (deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
            contentComponent.setOwned (newContent);
 | 
			
		||||
        else
 | 
			
		||||
            contentComponent.setNonOwned (newContent);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (contentComponent);
 | 
			
		||||
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::setTitleBarComponent (Component* titleBarComponentToUse,
 | 
			
		||||
                                      bool keepDismissButton,
 | 
			
		||||
                                      bool deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
{
 | 
			
		||||
    if (titleBarComponent.get() != titleBarComponentToUse)
 | 
			
		||||
    {
 | 
			
		||||
        if (deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
            titleBarComponent.setOwned (titleBarComponentToUse);
 | 
			
		||||
        else
 | 
			
		||||
            titleBarComponent.setNonOwned (titleBarComponentToUse);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (titleBarComponent);
 | 
			
		||||
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shouldShowDismissButton = keepDismissButton;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::showOrHide (bool show)
 | 
			
		||||
{
 | 
			
		||||
    if (parent != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        isShowing = show;
 | 
			
		||||
 | 
			
		||||
        Desktop::getInstance().getAnimator().animateComponent (this, calculateBoundsInParent (*parent),
 | 
			
		||||
                                                               1.0f, 250, true, 1.0, 0.0);
 | 
			
		||||
 | 
			
		||||
        if (isShowing && ! isVisible())
 | 
			
		||||
            setVisible (true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::moved()
 | 
			
		||||
{
 | 
			
		||||
    if (onPanelMove != nullptr)
 | 
			
		||||
        onPanelMove();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::resized()
 | 
			
		||||
{
 | 
			
		||||
    auto bounds = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
    calculateAndRemoveShadowBounds (bounds);
 | 
			
		||||
 | 
			
		||||
    auto titleBounds = bounds.removeFromTop (titleBarHeight);
 | 
			
		||||
 | 
			
		||||
    if (titleBarComponent != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (shouldShowDismissButton)
 | 
			
		||||
            dismissButton.setBounds (isOnLeft ? titleBounds.removeFromRight (30).withTrimmedRight (10)
 | 
			
		||||
                                              : titleBounds.removeFromLeft  (30).withTrimmedLeft  (10));
 | 
			
		||||
 | 
			
		||||
        titleBarComponent->setBounds (titleBounds);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        dismissButton.setBounds (isOnLeft ? titleBounds.removeFromRight (30).withTrimmedRight (10)
 | 
			
		||||
                                          : titleBounds.removeFromLeft  (30).withTrimmedLeft  (10));
 | 
			
		||||
 | 
			
		||||
        titleLabel.setBounds (isOnLeft ? titleBounds.withTrimmedRight (40)
 | 
			
		||||
                                       : titleBounds.withTrimmedLeft (40));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (contentComponent != nullptr)
 | 
			
		||||
        contentComponent->setBounds (bounds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    auto& lf = getLookAndFeel();
 | 
			
		||||
 | 
			
		||||
    auto bgColour     = lf.findColour (SidePanel::backgroundColour);
 | 
			
		||||
    auto shadowColour = lf.findColour (SidePanel::shadowBaseColour);
 | 
			
		||||
 | 
			
		||||
    g.setGradientFill (ColourGradient (shadowColour.withAlpha (0.7f), (isOnLeft ? shadowArea.getTopLeft()
 | 
			
		||||
                                                                                : shadowArea.getTopRight()).toFloat(),
 | 
			
		||||
                                       shadowColour.withAlpha (0.0f), (isOnLeft ? shadowArea.getTopRight()
 | 
			
		||||
                                                                                : shadowArea.getTopLeft()).toFloat(), false));
 | 
			
		||||
    g.fillRect (shadowArea);
 | 
			
		||||
 | 
			
		||||
    g.excludeClipRegion (shadowArea);
 | 
			
		||||
    g.fillAll (bgColour);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::parentHierarchyChanged()
 | 
			
		||||
{
 | 
			
		||||
    auto* newParent = getParentComponent();
 | 
			
		||||
 | 
			
		||||
    if ((newParent != nullptr) && (parent != newParent))
 | 
			
		||||
    {
 | 
			
		||||
        if (parent != nullptr)
 | 
			
		||||
            parent->removeComponentListener (this);
 | 
			
		||||
 | 
			
		||||
        parent = newParent;
 | 
			
		||||
        parent->addComponentListener (this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (shouldResize)
 | 
			
		||||
    {
 | 
			
		||||
        Point<int> convertedPoint;
 | 
			
		||||
 | 
			
		||||
        if (getParentComponent() == nullptr)
 | 
			
		||||
            convertedPoint = e.eventComponent->localPointToGlobal (e.getPosition());
 | 
			
		||||
        else
 | 
			
		||||
            convertedPoint = getParentComponent()->getLocalPoint (e.eventComponent, e.getPosition());
 | 
			
		||||
 | 
			
		||||
        auto currentMouseDragX = convertedPoint.x;
 | 
			
		||||
 | 
			
		||||
        if (isOnLeft)
 | 
			
		||||
        {
 | 
			
		||||
            amountMoved = startingBounds.getRight() - currentMouseDragX;
 | 
			
		||||
            setBounds (getBounds().withX (startingBounds.getX() - jmax (amountMoved, 0)));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            amountMoved = currentMouseDragX - startingBounds.getX();
 | 
			
		||||
            setBounds (getBounds().withX (startingBounds.getX() + jmax (amountMoved, 0)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (isShowing)
 | 
			
		||||
    {
 | 
			
		||||
        auto relativeMouseDownPosition = getLocalPoint (e.eventComponent, e.getMouseDownPosition());
 | 
			
		||||
        auto relativeMouseDragPosition = getLocalPoint (e.eventComponent, e.getPosition());
 | 
			
		||||
 | 
			
		||||
        if (! getLocalBounds().contains (relativeMouseDownPosition)
 | 
			
		||||
              && getLocalBounds().contains (relativeMouseDragPosition))
 | 
			
		||||
        {
 | 
			
		||||
            shouldResize = true;
 | 
			
		||||
            startingBounds = getBounds();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (shouldResize)
 | 
			
		||||
    {
 | 
			
		||||
        showOrHide (amountMoved < (panelWidth / 2));
 | 
			
		||||
 | 
			
		||||
        amountMoved = 0;
 | 
			
		||||
        shouldResize = false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void SidePanel::lookAndFeelChanged()
 | 
			
		||||
{
 | 
			
		||||
    auto& lf = getLookAndFeel();
 | 
			
		||||
 | 
			
		||||
    dismissButton.setShape (lf.getSidePanelDismissButtonShape (*this), false, true, false);
 | 
			
		||||
 | 
			
		||||
    dismissButton.setColours (lf.findColour (SidePanel::dismissButtonNormalColour),
 | 
			
		||||
                              lf.findColour (SidePanel::dismissButtonOverColour),
 | 
			
		||||
                              lf.findColour (SidePanel::dismissButtonDownColour));
 | 
			
		||||
 | 
			
		||||
    titleLabel.setFont (lf.getSidePanelTitleFont (*this));
 | 
			
		||||
    titleLabel.setColour (Label::textColourId, findColour (SidePanel::titleTextColour));
 | 
			
		||||
    titleLabel.setJustificationType (lf.getSidePanelTitleJustification (*this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
 | 
			
		||||
{
 | 
			
		||||
    ignoreUnused (wasMoved);
 | 
			
		||||
 | 
			
		||||
    if (wasResized && (&component == parent))
 | 
			
		||||
        setBounds (calculateBoundsInParent (component));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::changeListenerCallback (ChangeBroadcaster*)
 | 
			
		||||
{
 | 
			
		||||
    if (! Desktop::getInstance().getAnimator().isAnimating (this))
 | 
			
		||||
    {
 | 
			
		||||
        if (onPanelShowHide != nullptr)
 | 
			
		||||
            onPanelShowHide (isShowing);
 | 
			
		||||
 | 
			
		||||
        if (isVisible() && ! isShowing)
 | 
			
		||||
            setVisible (false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> SidePanel::calculateBoundsInParent (Component& parentComp) const
 | 
			
		||||
{
 | 
			
		||||
    auto parentBounds = parentComp.getLocalBounds();
 | 
			
		||||
 | 
			
		||||
    if (isOnLeft)
 | 
			
		||||
    {
 | 
			
		||||
        return isShowing ? parentBounds.removeFromLeft (panelWidth)
 | 
			
		||||
                         : parentBounds.withX (parentBounds.getX() - panelWidth).withWidth (panelWidth);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return isShowing ? parentBounds.removeFromRight (panelWidth)
 | 
			
		||||
                     : parentBounds.withX (parentBounds.getRight()).withWidth (panelWidth);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::calculateAndRemoveShadowBounds (Rectangle<int>& bounds)
 | 
			
		||||
{
 | 
			
		||||
    shadowArea = isOnLeft ? bounds.removeFromRight (shadowWidth)
 | 
			
		||||
                          : bounds.removeFromLeft  (shadowWidth);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SidePanel::isMouseEventInThisOrChildren (Component* eventComponent)
 | 
			
		||||
{
 | 
			
		||||
    if (eventComponent == this)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    for (auto& child : getChildren())
 | 
			
		||||
        if (eventComponent == child)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> SidePanel::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
SidePanel::SidePanel (StringRef title, int width, bool positionOnLeft,
 | 
			
		||||
                      Component* contentToDisplay, bool deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
    : titleLabel ("titleLabel", title),
 | 
			
		||||
      isOnLeft (positionOnLeft),
 | 
			
		||||
      panelWidth (width)
 | 
			
		||||
{
 | 
			
		||||
    lookAndFeelChanged();
 | 
			
		||||
 | 
			
		||||
    addAndMakeVisible (titleLabel);
 | 
			
		||||
 | 
			
		||||
    dismissButton.onClick = [this] { showOrHide (false); };
 | 
			
		||||
    addAndMakeVisible (dismissButton);
 | 
			
		||||
 | 
			
		||||
    auto& desktop = Desktop::getInstance();
 | 
			
		||||
 | 
			
		||||
    desktop.addGlobalMouseListener (this);
 | 
			
		||||
    desktop.getAnimator().addChangeListener (this);
 | 
			
		||||
 | 
			
		||||
    if (contentToDisplay != nullptr)
 | 
			
		||||
        setContent (contentToDisplay, deleteComponentWhenNoLongerNeeded);
 | 
			
		||||
 | 
			
		||||
    setOpaque (false);
 | 
			
		||||
    setVisible (false);
 | 
			
		||||
    setAlwaysOnTop (true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SidePanel::~SidePanel()
 | 
			
		||||
{
 | 
			
		||||
    auto& desktop = Desktop::getInstance();
 | 
			
		||||
 | 
			
		||||
    desktop.removeGlobalMouseListener (this);
 | 
			
		||||
    desktop.getAnimator().removeChangeListener (this);
 | 
			
		||||
 | 
			
		||||
    if (parent != nullptr)
 | 
			
		||||
        parent->removeComponentListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::setContent (Component* newContent, bool deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
{
 | 
			
		||||
    if (contentComponent.get() != newContent)
 | 
			
		||||
    {
 | 
			
		||||
        if (deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
            contentComponent.setOwned (newContent);
 | 
			
		||||
        else
 | 
			
		||||
            contentComponent.setNonOwned (newContent);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (contentComponent);
 | 
			
		||||
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::setTitleBarComponent (Component* titleBarComponentToUse,
 | 
			
		||||
                                      bool keepDismissButton,
 | 
			
		||||
                                      bool deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
{
 | 
			
		||||
    if (titleBarComponent.get() != titleBarComponentToUse)
 | 
			
		||||
    {
 | 
			
		||||
        if (deleteComponentWhenNoLongerNeeded)
 | 
			
		||||
            titleBarComponent.setOwned (titleBarComponentToUse);
 | 
			
		||||
        else
 | 
			
		||||
            titleBarComponent.setNonOwned (titleBarComponentToUse);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (titleBarComponent);
 | 
			
		||||
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shouldShowDismissButton = keepDismissButton;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::showOrHide (bool show)
 | 
			
		||||
{
 | 
			
		||||
    if (parent != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        isShowing = show;
 | 
			
		||||
 | 
			
		||||
        Desktop::getInstance().getAnimator().animateComponent (this, calculateBoundsInParent (*parent),
 | 
			
		||||
                                                               1.0f, 250, true, 1.0, 0.0);
 | 
			
		||||
 | 
			
		||||
        if (isShowing && ! isVisible())
 | 
			
		||||
            setVisible (true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::moved()
 | 
			
		||||
{
 | 
			
		||||
    if (onPanelMove != nullptr)
 | 
			
		||||
        onPanelMove();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::resized()
 | 
			
		||||
{
 | 
			
		||||
    auto bounds = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
    calculateAndRemoveShadowBounds (bounds);
 | 
			
		||||
 | 
			
		||||
    auto titleBounds = bounds.removeFromTop (titleBarHeight);
 | 
			
		||||
 | 
			
		||||
    if (titleBarComponent != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (shouldShowDismissButton)
 | 
			
		||||
            dismissButton.setBounds (isOnLeft ? titleBounds.removeFromRight (30).withTrimmedRight (10)
 | 
			
		||||
                                              : titleBounds.removeFromLeft  (30).withTrimmedLeft  (10));
 | 
			
		||||
 | 
			
		||||
        titleBarComponent->setBounds (titleBounds);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        dismissButton.setBounds (isOnLeft ? titleBounds.removeFromRight (30).withTrimmedRight (10)
 | 
			
		||||
                                          : titleBounds.removeFromLeft  (30).withTrimmedLeft  (10));
 | 
			
		||||
 | 
			
		||||
        titleLabel.setBounds (isOnLeft ? titleBounds.withTrimmedRight (40)
 | 
			
		||||
                                       : titleBounds.withTrimmedLeft (40));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (contentComponent != nullptr)
 | 
			
		||||
        contentComponent->setBounds (bounds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    auto& lf = getLookAndFeel();
 | 
			
		||||
 | 
			
		||||
    auto bgColour     = lf.findColour (SidePanel::backgroundColour);
 | 
			
		||||
    auto shadowColour = lf.findColour (SidePanel::shadowBaseColour);
 | 
			
		||||
 | 
			
		||||
    g.setGradientFill (ColourGradient (shadowColour.withAlpha (0.7f), (isOnLeft ? shadowArea.getTopLeft()
 | 
			
		||||
                                                                                : shadowArea.getTopRight()).toFloat(),
 | 
			
		||||
                                       shadowColour.withAlpha (0.0f), (isOnLeft ? shadowArea.getTopRight()
 | 
			
		||||
                                                                                : shadowArea.getTopLeft()).toFloat(), false));
 | 
			
		||||
    g.fillRect (shadowArea);
 | 
			
		||||
 | 
			
		||||
    g.excludeClipRegion (shadowArea);
 | 
			
		||||
    g.fillAll (bgColour);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::parentHierarchyChanged()
 | 
			
		||||
{
 | 
			
		||||
    auto* newParent = getParentComponent();
 | 
			
		||||
 | 
			
		||||
    if ((newParent != nullptr) && (parent != newParent))
 | 
			
		||||
    {
 | 
			
		||||
        if (parent != nullptr)
 | 
			
		||||
            parent->removeComponentListener (this);
 | 
			
		||||
 | 
			
		||||
        parent = newParent;
 | 
			
		||||
        parent->addComponentListener (this);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    if (shouldResize)
 | 
			
		||||
    {
 | 
			
		||||
        Point<int> convertedPoint;
 | 
			
		||||
 | 
			
		||||
        if (getParentComponent() == nullptr)
 | 
			
		||||
            convertedPoint = e.eventComponent->localPointToGlobal (e.getPosition());
 | 
			
		||||
        else
 | 
			
		||||
            convertedPoint = getParentComponent()->getLocalPoint (e.eventComponent, e.getPosition());
 | 
			
		||||
 | 
			
		||||
        auto currentMouseDragX = convertedPoint.x;
 | 
			
		||||
 | 
			
		||||
        if (isOnLeft)
 | 
			
		||||
        {
 | 
			
		||||
            amountMoved = startingBounds.getRight() - currentMouseDragX;
 | 
			
		||||
            setBounds (getBounds().withX (startingBounds.getX() - jmax (amountMoved, 0)));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            amountMoved = currentMouseDragX - startingBounds.getX();
 | 
			
		||||
            setBounds (getBounds().withX (startingBounds.getX() + jmax (amountMoved, 0)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (isShowing)
 | 
			
		||||
    {
 | 
			
		||||
        auto relativeMouseDownPosition = getLocalPoint (e.eventComponent, e.getMouseDownPosition());
 | 
			
		||||
        auto relativeMouseDragPosition = getLocalPoint (e.eventComponent, e.getPosition());
 | 
			
		||||
 | 
			
		||||
        if (! getLocalBounds().contains (relativeMouseDownPosition)
 | 
			
		||||
              && getLocalBounds().contains (relativeMouseDragPosition))
 | 
			
		||||
        {
 | 
			
		||||
            shouldResize = true;
 | 
			
		||||
            startingBounds = getBounds();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::mouseUp (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    if (shouldResize)
 | 
			
		||||
    {
 | 
			
		||||
        showOrHide (amountMoved < (panelWidth / 2));
 | 
			
		||||
 | 
			
		||||
        amountMoved = 0;
 | 
			
		||||
        shouldResize = false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void SidePanel::lookAndFeelChanged()
 | 
			
		||||
{
 | 
			
		||||
    auto& lf = getLookAndFeel();
 | 
			
		||||
 | 
			
		||||
    dismissButton.setShape (lf.getSidePanelDismissButtonShape (*this), false, true, false);
 | 
			
		||||
 | 
			
		||||
    dismissButton.setColours (lf.findColour (SidePanel::dismissButtonNormalColour),
 | 
			
		||||
                              lf.findColour (SidePanel::dismissButtonOverColour),
 | 
			
		||||
                              lf.findColour (SidePanel::dismissButtonDownColour));
 | 
			
		||||
 | 
			
		||||
    titleLabel.setFont (lf.getSidePanelTitleFont (*this));
 | 
			
		||||
    titleLabel.setColour (Label::textColourId, findColour (SidePanel::titleTextColour));
 | 
			
		||||
    titleLabel.setJustificationType (lf.getSidePanelTitleJustification (*this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
 | 
			
		||||
{
 | 
			
		||||
    ignoreUnused (wasMoved);
 | 
			
		||||
 | 
			
		||||
    if (wasResized && (&component == parent))
 | 
			
		||||
        setBounds (calculateBoundsInParent (component));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::changeListenerCallback (ChangeBroadcaster*)
 | 
			
		||||
{
 | 
			
		||||
    if (! Desktop::getInstance().getAnimator().isAnimating (this))
 | 
			
		||||
    {
 | 
			
		||||
        if (onPanelShowHide != nullptr)
 | 
			
		||||
            onPanelShowHide (isShowing);
 | 
			
		||||
 | 
			
		||||
        if (isVisible() && ! isShowing)
 | 
			
		||||
            setVisible (false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Rectangle<int> SidePanel::calculateBoundsInParent (Component& parentComp) const
 | 
			
		||||
{
 | 
			
		||||
    auto parentBounds = parentComp.getLocalBounds();
 | 
			
		||||
 | 
			
		||||
    if (isOnLeft)
 | 
			
		||||
    {
 | 
			
		||||
        return isShowing ? parentBounds.removeFromLeft (panelWidth)
 | 
			
		||||
                         : parentBounds.withX (parentBounds.getX() - panelWidth).withWidth (panelWidth);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return isShowing ? parentBounds.removeFromRight (panelWidth)
 | 
			
		||||
                     : parentBounds.withX (parentBounds.getRight()).withWidth (panelWidth);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SidePanel::calculateAndRemoveShadowBounds (Rectangle<int>& bounds)
 | 
			
		||||
{
 | 
			
		||||
    shadowArea = isOnLeft ? bounds.removeFromRight (shadowWidth)
 | 
			
		||||
                          : bounds.removeFromLeft  (shadowWidth);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool SidePanel::isMouseEventInThisOrChildren (Component* eventComponent)
 | 
			
		||||
{
 | 
			
		||||
    if (eventComponent == this)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    for (auto& child : getChildren())
 | 
			
		||||
        if (eventComponent == child)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> SidePanel::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,238 +1,238 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that is positioned on either the left- or right-hand side of its parent,
 | 
			
		||||
    containing a header and some content. This sort of component is typically used for
 | 
			
		||||
    navigation and forms in mobile applications.
 | 
			
		||||
 | 
			
		||||
    When triggered with the showOrHide() method, the SidePanel will animate itself to its
 | 
			
		||||
    new position. This component also contains some logic to reactively resize and dismiss
 | 
			
		||||
    itself when the user drags it.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class SidePanel    : public Component,
 | 
			
		||||
                     private ComponentListener,
 | 
			
		||||
                     private ChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a SidePanel component.
 | 
			
		||||
 | 
			
		||||
        @param title               the text to use for the SidePanel's title bar
 | 
			
		||||
        @param width               the width of the SidePanel
 | 
			
		||||
        @param positionOnLeft      if true, the SidePanel will be positioned on the left of its parent component and
 | 
			
		||||
                                   if false, the SidePanel will be positioned on the right of its parent component
 | 
			
		||||
        @param contentComponent    the component to add to this SidePanel - this content will take up the full
 | 
			
		||||
                                   size of the SidePanel, minus the height of the title bar. You can pass nullptr
 | 
			
		||||
                                   to this if you like and set the content component later using the setContent() method
 | 
			
		||||
        @param deleteComponentWhenNoLongerNeeded    if true, the component will be deleted automatically when
 | 
			
		||||
                                   the SidePanel is deleted or when a different component is added. If false,
 | 
			
		||||
                                   the caller must manage the lifetime of the component
 | 
			
		||||
    */
 | 
			
		||||
    SidePanel (StringRef title, int width, bool positionOnLeft,
 | 
			
		||||
               Component* contentComponent = nullptr,
 | 
			
		||||
               bool deleteComponentWhenNoLongerNeeded = true);
 | 
			
		||||
 | 
			
		||||
    /** Destructor */
 | 
			
		||||
    ~SidePanel() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the component that this SidePanel will contain.
 | 
			
		||||
 | 
			
		||||
        This will add the given component to this SidePanel and position it below the title bar.
 | 
			
		||||
 | 
			
		||||
        (Don't add or remove any child components directly using the normal
 | 
			
		||||
        Component::addChildComponent() methods).
 | 
			
		||||
 | 
			
		||||
        @param newContentComponent   the component to add to this SidePanel, or nullptr to remove
 | 
			
		||||
                                     the current component.
 | 
			
		||||
        @param deleteComponentWhenNoLongerNeeded    if true, the component will be deleted automatically when
 | 
			
		||||
                                   the SidePanel is deleted or when a different component is added. If false,
 | 
			
		||||
                                   the caller must manage the lifetime of the component
 | 
			
		||||
 | 
			
		||||
        @see getContent
 | 
			
		||||
    */
 | 
			
		||||
    void setContent (Component* newContentComponent,
 | 
			
		||||
                     bool deleteComponentWhenNoLongerNeeded = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that's currently being used inside the SidePanel.
 | 
			
		||||
 | 
			
		||||
        @see setViewedComponent
 | 
			
		||||
    */
 | 
			
		||||
    Component* getContent() const noexcept    { return contentComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    /** Sets a custom component to be used for the title bar of this SidePanel, replacing
 | 
			
		||||
        the default. You can pass a nullptr to revert to the default title bar.
 | 
			
		||||
 | 
			
		||||
        @param titleBarComponentToUse  the component to use as the title bar, or nullptr to use
 | 
			
		||||
                                       the default
 | 
			
		||||
        @param keepDismissButton       if false the specified component will take up the full width of
 | 
			
		||||
                                       the title bar including the dismiss button but if true, the default
 | 
			
		||||
                                       dismiss button will be kept
 | 
			
		||||
        @param deleteComponentWhenNoLongerNeeded  if true, the component will be deleted automatically when
 | 
			
		||||
                                       the SidePanel is deleted or when a different component is added. If false,
 | 
			
		||||
                                       the caller must manage the lifetime of the component
 | 
			
		||||
 | 
			
		||||
        @see getTitleBarComponent
 | 
			
		||||
    */
 | 
			
		||||
    void setTitleBarComponent (Component* titleBarComponentToUse,
 | 
			
		||||
                               bool keepDismissButton,
 | 
			
		||||
                               bool deleteComponentWhenNoLongerNeeded = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that is currently being used as the title bar of the SidePanel.
 | 
			
		||||
 | 
			
		||||
        @see setTitleBarComponent
 | 
			
		||||
    */
 | 
			
		||||
    Component* getTitleBarComponent() const noexcept    { return titleBarComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    /** Shows or hides the SidePanel.
 | 
			
		||||
 | 
			
		||||
        This will animate the SidePanel to either its full width or to be hidden on the
 | 
			
		||||
        left- or right-hand side of its parent component depending on the value of positionOnLeft
 | 
			
		||||
        that was passed to the constructor.
 | 
			
		||||
 | 
			
		||||
        @param show    if true, this will show the SidePanel and if false the SidePanel will be hidden
 | 
			
		||||
    */
 | 
			
		||||
    void showOrHide (bool show);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns true if the SidePanel is currently showing. */
 | 
			
		||||
    bool isPanelShowing() const noexcept               { return isShowing; }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the SidePanel is positioned on the left of its parent. */
 | 
			
		||||
    bool isPanelOnLeft() const noexcept                { return isOnLeft; }
 | 
			
		||||
 | 
			
		||||
    /** Sets the width of the shadow that will be drawn on the side of the panel. */
 | 
			
		||||
    void setShadowWidth (int newWidth) noexcept        { shadowWidth = newWidth; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the width of the shadow that will be drawn on the side of the panel. */
 | 
			
		||||
    int getShadowWidth() const noexcept                { return shadowWidth; }
 | 
			
		||||
 | 
			
		||||
    /** Sets the height of the title bar at the top of the SidePanel. */
 | 
			
		||||
    void setTitleBarHeight (int newHeight) noexcept    { titleBarHeight = newHeight; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the height of the title bar at the top of the SidePanel. */
 | 
			
		||||
    int getTitleBarHeight() const noexcept             { return titleBarHeight; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the text that is displayed in the title bar at the top of the SidePanel. */
 | 
			
		||||
    String getTitleText() const noexcept               { return titleLabel.getText(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes to provide
 | 
			
		||||
        SidePanel drawing functionality.
 | 
			
		||||
     */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual Font getSidePanelTitleFont (SidePanel&) = 0;
 | 
			
		||||
        virtual Justification getSidePanelTitleJustification (SidePanel&) = 0;
 | 
			
		||||
        virtual Path getSidePanelDismissButtonShape (SidePanel&) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the SidePanel.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        backgroundColour          = 0x100f001,
 | 
			
		||||
        titleTextColour           = 0x100f002,
 | 
			
		||||
        shadowBaseColour          = 0x100f003,
 | 
			
		||||
        dismissButtonNormalColour = 0x100f004,
 | 
			
		||||
        dismissButtonOverColour   = 0x100f005,
 | 
			
		||||
        dismissButtonDownColour   = 0x100f006
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** You can assign a lambda to this callback object and it will be called when the panel is moved. */
 | 
			
		||||
    std::function<void()> onPanelMove;
 | 
			
		||||
 | 
			
		||||
    /** You can assign a lambda to this callback object and it will be called when the panel is shown or hidden. */
 | 
			
		||||
    std::function<void (bool)> onPanelShowHide;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void moved() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics& g) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void parentHierarchyChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Component* parent = nullptr;
 | 
			
		||||
    OptionalScopedPointer<Component> contentComponent;
 | 
			
		||||
    OptionalScopedPointer<Component> titleBarComponent;
 | 
			
		||||
 | 
			
		||||
    Label titleLabel;
 | 
			
		||||
    ShapeButton dismissButton { "dismissButton", Colours::lightgrey, Colours::lightgrey, Colours::white };
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> shadowArea;
 | 
			
		||||
 | 
			
		||||
    bool isOnLeft = false;
 | 
			
		||||
    bool isShowing = false;
 | 
			
		||||
 | 
			
		||||
    int panelWidth = 0;
 | 
			
		||||
    int shadowWidth = 15;
 | 
			
		||||
    int titleBarHeight = 40;
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> startingBounds;
 | 
			
		||||
    bool shouldResize = false;
 | 
			
		||||
    int amountMoved = 0;
 | 
			
		||||
 | 
			
		||||
    bool shouldShowDismissButton = true;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
    void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
 | 
			
		||||
    void changeListenerCallback (ChangeBroadcaster*) override;
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> calculateBoundsInParent (Component&) const;
 | 
			
		||||
    void calculateAndRemoveShadowBounds (Rectangle<int>& bounds);
 | 
			
		||||
 | 
			
		||||
    bool isMouseEventInThisOrChildren (Component*);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SidePanel)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that is positioned on either the left- or right-hand side of its parent,
 | 
			
		||||
    containing a header and some content. This sort of component is typically used for
 | 
			
		||||
    navigation and forms in mobile applications.
 | 
			
		||||
 | 
			
		||||
    When triggered with the showOrHide() method, the SidePanel will animate itself to its
 | 
			
		||||
    new position. This component also contains some logic to reactively resize and dismiss
 | 
			
		||||
    itself when the user drags it.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class SidePanel    : public Component,
 | 
			
		||||
                     private ComponentListener,
 | 
			
		||||
                     private ChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a SidePanel component.
 | 
			
		||||
 | 
			
		||||
        @param title               the text to use for the SidePanel's title bar
 | 
			
		||||
        @param width               the width of the SidePanel
 | 
			
		||||
        @param positionOnLeft      if true, the SidePanel will be positioned on the left of its parent component and
 | 
			
		||||
                                   if false, the SidePanel will be positioned on the right of its parent component
 | 
			
		||||
        @param contentComponent    the component to add to this SidePanel - this content will take up the full
 | 
			
		||||
                                   size of the SidePanel, minus the height of the title bar. You can pass nullptr
 | 
			
		||||
                                   to this if you like and set the content component later using the setContent() method
 | 
			
		||||
        @param deleteComponentWhenNoLongerNeeded    if true, the component will be deleted automatically when
 | 
			
		||||
                                   the SidePanel is deleted or when a different component is added. If false,
 | 
			
		||||
                                   the caller must manage the lifetime of the component
 | 
			
		||||
    */
 | 
			
		||||
    SidePanel (StringRef title, int width, bool positionOnLeft,
 | 
			
		||||
               Component* contentComponent = nullptr,
 | 
			
		||||
               bool deleteComponentWhenNoLongerNeeded = true);
 | 
			
		||||
 | 
			
		||||
    /** Destructor */
 | 
			
		||||
    ~SidePanel() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the component that this SidePanel will contain.
 | 
			
		||||
 | 
			
		||||
        This will add the given component to this SidePanel and position it below the title bar.
 | 
			
		||||
 | 
			
		||||
        (Don't add or remove any child components directly using the normal
 | 
			
		||||
        Component::addChildComponent() methods).
 | 
			
		||||
 | 
			
		||||
        @param newContentComponent   the component to add to this SidePanel, or nullptr to remove
 | 
			
		||||
                                     the current component.
 | 
			
		||||
        @param deleteComponentWhenNoLongerNeeded    if true, the component will be deleted automatically when
 | 
			
		||||
                                   the SidePanel is deleted or when a different component is added. If false,
 | 
			
		||||
                                   the caller must manage the lifetime of the component
 | 
			
		||||
 | 
			
		||||
        @see getContent
 | 
			
		||||
    */
 | 
			
		||||
    void setContent (Component* newContentComponent,
 | 
			
		||||
                     bool deleteComponentWhenNoLongerNeeded = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that's currently being used inside the SidePanel.
 | 
			
		||||
 | 
			
		||||
        @see setViewedComponent
 | 
			
		||||
    */
 | 
			
		||||
    Component* getContent() const noexcept    { return contentComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    /** Sets a custom component to be used for the title bar of this SidePanel, replacing
 | 
			
		||||
        the default. You can pass a nullptr to revert to the default title bar.
 | 
			
		||||
 | 
			
		||||
        @param titleBarComponentToUse  the component to use as the title bar, or nullptr to use
 | 
			
		||||
                                       the default
 | 
			
		||||
        @param keepDismissButton       if false the specified component will take up the full width of
 | 
			
		||||
                                       the title bar including the dismiss button but if true, the default
 | 
			
		||||
                                       dismiss button will be kept
 | 
			
		||||
        @param deleteComponentWhenNoLongerNeeded  if true, the component will be deleted automatically when
 | 
			
		||||
                                       the SidePanel is deleted or when a different component is added. If false,
 | 
			
		||||
                                       the caller must manage the lifetime of the component
 | 
			
		||||
 | 
			
		||||
        @see getTitleBarComponent
 | 
			
		||||
    */
 | 
			
		||||
    void setTitleBarComponent (Component* titleBarComponentToUse,
 | 
			
		||||
                               bool keepDismissButton,
 | 
			
		||||
                               bool deleteComponentWhenNoLongerNeeded = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that is currently being used as the title bar of the SidePanel.
 | 
			
		||||
 | 
			
		||||
        @see setTitleBarComponent
 | 
			
		||||
    */
 | 
			
		||||
    Component* getTitleBarComponent() const noexcept    { return titleBarComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    /** Shows or hides the SidePanel.
 | 
			
		||||
 | 
			
		||||
        This will animate the SidePanel to either its full width or to be hidden on the
 | 
			
		||||
        left- or right-hand side of its parent component depending on the value of positionOnLeft
 | 
			
		||||
        that was passed to the constructor.
 | 
			
		||||
 | 
			
		||||
        @param show    if true, this will show the SidePanel and if false the SidePanel will be hidden
 | 
			
		||||
    */
 | 
			
		||||
    void showOrHide (bool show);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns true if the SidePanel is currently showing. */
 | 
			
		||||
    bool isPanelShowing() const noexcept               { return isShowing; }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the SidePanel is positioned on the left of its parent. */
 | 
			
		||||
    bool isPanelOnLeft() const noexcept                { return isOnLeft; }
 | 
			
		||||
 | 
			
		||||
    /** Sets the width of the shadow that will be drawn on the side of the panel. */
 | 
			
		||||
    void setShadowWidth (int newWidth) noexcept        { shadowWidth = newWidth; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the width of the shadow that will be drawn on the side of the panel. */
 | 
			
		||||
    int getShadowWidth() const noexcept                { return shadowWidth; }
 | 
			
		||||
 | 
			
		||||
    /** Sets the height of the title bar at the top of the SidePanel. */
 | 
			
		||||
    void setTitleBarHeight (int newHeight) noexcept    { titleBarHeight = newHeight; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the height of the title bar at the top of the SidePanel. */
 | 
			
		||||
    int getTitleBarHeight() const noexcept             { return titleBarHeight; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the text that is displayed in the title bar at the top of the SidePanel. */
 | 
			
		||||
    String getTitleText() const noexcept               { return titleLabel.getText(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes to provide
 | 
			
		||||
        SidePanel drawing functionality.
 | 
			
		||||
     */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual Font getSidePanelTitleFont (SidePanel&) = 0;
 | 
			
		||||
        virtual Justification getSidePanelTitleJustification (SidePanel&) = 0;
 | 
			
		||||
        virtual Path getSidePanelDismissButtonShape (SidePanel&) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the SidePanel.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        backgroundColour          = 0x100f001,
 | 
			
		||||
        titleTextColour           = 0x100f002,
 | 
			
		||||
        shadowBaseColour          = 0x100f003,
 | 
			
		||||
        dismissButtonNormalColour = 0x100f004,
 | 
			
		||||
        dismissButtonOverColour   = 0x100f005,
 | 
			
		||||
        dismissButtonDownColour   = 0x100f006
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** You can assign a lambda to this callback object and it will be called when the panel is moved. */
 | 
			
		||||
    std::function<void()> onPanelMove;
 | 
			
		||||
 | 
			
		||||
    /** You can assign a lambda to this callback object and it will be called when the panel is shown or hidden. */
 | 
			
		||||
    std::function<void (bool)> onPanelShowHide;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void moved() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics& g) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void parentHierarchyChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseUp (const MouseEvent&) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Component* parent = nullptr;
 | 
			
		||||
    OptionalScopedPointer<Component> contentComponent;
 | 
			
		||||
    OptionalScopedPointer<Component> titleBarComponent;
 | 
			
		||||
 | 
			
		||||
    Label titleLabel;
 | 
			
		||||
    ShapeButton dismissButton { "dismissButton", Colours::lightgrey, Colours::lightgrey, Colours::white };
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> shadowArea;
 | 
			
		||||
 | 
			
		||||
    bool isOnLeft = false;
 | 
			
		||||
    bool isShowing = false;
 | 
			
		||||
 | 
			
		||||
    int panelWidth = 0;
 | 
			
		||||
    int shadowWidth = 15;
 | 
			
		||||
    int titleBarHeight = 40;
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> startingBounds;
 | 
			
		||||
    bool shouldResize = false;
 | 
			
		||||
    int amountMoved = 0;
 | 
			
		||||
 | 
			
		||||
    bool shouldShowDismissButton = true;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
    void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
 | 
			
		||||
    void changeListenerCallback (ChangeBroadcaster*) override;
 | 
			
		||||
 | 
			
		||||
    Rectangle<int> calculateBoundsInParent (Component&) const;
 | 
			
		||||
    void calculateAndRemoveShadowBounds (Rectangle<int>& bounds);
 | 
			
		||||
 | 
			
		||||
    bool isMouseEventInThisOrChildren (Component*);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SidePanel)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,342 +1,342 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
StretchableLayoutManager::StretchableLayoutManager() {}
 | 
			
		||||
StretchableLayoutManager::~StretchableLayoutManager() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void StretchableLayoutManager::clearAllItems()
 | 
			
		||||
{
 | 
			
		||||
    items.clear();
 | 
			
		||||
    totalSize = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutManager::setItemLayout (const int itemIndex,
 | 
			
		||||
                                              const double minimumSize,
 | 
			
		||||
                                              const double maximumSize,
 | 
			
		||||
                                              const double preferredSize)
 | 
			
		||||
{
 | 
			
		||||
    auto* layout = getInfoFor (itemIndex);
 | 
			
		||||
 | 
			
		||||
    if (layout == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        layout = new ItemLayoutProperties();
 | 
			
		||||
        layout->itemIndex = itemIndex;
 | 
			
		||||
 | 
			
		||||
        int i;
 | 
			
		||||
        for (i = 0; i < items.size(); ++i)
 | 
			
		||||
            if (items.getUnchecked (i)->itemIndex > itemIndex)
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
        items.insert (i, layout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    layout->minSize = minimumSize;
 | 
			
		||||
    layout->maxSize = maximumSize;
 | 
			
		||||
    layout->preferredSize = preferredSize;
 | 
			
		||||
    layout->currentSize = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool StretchableLayoutManager::getItemLayout (const int itemIndex,
 | 
			
		||||
                                              double& minimumSize,
 | 
			
		||||
                                              double& maximumSize,
 | 
			
		||||
                                              double& preferredSize) const
 | 
			
		||||
{
 | 
			
		||||
    if (auto* layout = getInfoFor (itemIndex))
 | 
			
		||||
    {
 | 
			
		||||
        minimumSize = layout->minSize;
 | 
			
		||||
        maximumSize = layout->maxSize;
 | 
			
		||||
        preferredSize = layout->preferredSize;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void StretchableLayoutManager::setTotalSize (const int newTotalSize)
 | 
			
		||||
{
 | 
			
		||||
    totalSize = newTotalSize;
 | 
			
		||||
 | 
			
		||||
    fitComponentsIntoSpace (0, items.size(), totalSize, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::getItemCurrentPosition (const int itemIndex) const
 | 
			
		||||
{
 | 
			
		||||
    int pos = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < itemIndex; ++i)
 | 
			
		||||
        if (auto* layout = getInfoFor (i))
 | 
			
		||||
            pos += layout->currentSize;
 | 
			
		||||
 | 
			
		||||
    return pos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::getItemCurrentAbsoluteSize (const int itemIndex) const
 | 
			
		||||
{
 | 
			
		||||
    if (auto* layout = getInfoFor (itemIndex))
 | 
			
		||||
        return layout->currentSize;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double StretchableLayoutManager::getItemCurrentRelativeSize (const int itemIndex) const
 | 
			
		||||
{
 | 
			
		||||
    if (auto* layout = getInfoFor (itemIndex))
 | 
			
		||||
        return -layout->currentSize / (double) totalSize;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutManager::setItemPosition (const int itemIndex,
 | 
			
		||||
                                                int newPosition)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = items.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = items.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (layout->itemIndex == itemIndex)
 | 
			
		||||
        {
 | 
			
		||||
            auto realTotalSize = jmax (totalSize, getMinimumSizeOfItems (0, items.size()));
 | 
			
		||||
            auto minSizeAfterThisComp = getMinimumSizeOfItems (i, items.size());
 | 
			
		||||
            auto maxSizeAfterThisComp = getMaximumSizeOfItems (i + 1, items.size());
 | 
			
		||||
 | 
			
		||||
            newPosition = jmax (newPosition, totalSize - maxSizeAfterThisComp - layout->currentSize);
 | 
			
		||||
            newPosition = jmin (newPosition, realTotalSize - minSizeAfterThisComp);
 | 
			
		||||
 | 
			
		||||
            auto endPos = fitComponentsIntoSpace (0, i, newPosition, 0);
 | 
			
		||||
 | 
			
		||||
            endPos += layout->currentSize;
 | 
			
		||||
 | 
			
		||||
            fitComponentsIntoSpace (i + 1, items.size(), totalSize - endPos, endPos);
 | 
			
		||||
            updatePrefSizesToMatchCurrentPositions();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void StretchableLayoutManager::layOutComponents (Component** const components,
 | 
			
		||||
                                                 int numComponents,
 | 
			
		||||
                                                 int x, int y, int w, int h,
 | 
			
		||||
                                                 const bool vertically,
 | 
			
		||||
                                                 const bool resizeOtherDimension)
 | 
			
		||||
{
 | 
			
		||||
    setTotalSize (vertically ? h : w);
 | 
			
		||||
    int pos = vertically ? y : x;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numComponents; ++i)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* layout = getInfoFor (i))
 | 
			
		||||
        {
 | 
			
		||||
            if (auto* c = components[i])
 | 
			
		||||
            {
 | 
			
		||||
                if (i == numComponents - 1)
 | 
			
		||||
                {
 | 
			
		||||
                    // if it's the last item, crop it to exactly fit the available space..
 | 
			
		||||
                    if (resizeOtherDimension)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (vertically)
 | 
			
		||||
                            c->setBounds (x, pos, w, jmax (layout->currentSize, h - pos));
 | 
			
		||||
                        else
 | 
			
		||||
                            c->setBounds (pos, y, jmax (layout->currentSize, w - pos), h);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (vertically)
 | 
			
		||||
                            c->setBounds (c->getX(), pos, c->getWidth(), jmax (layout->currentSize, h - pos));
 | 
			
		||||
                        else
 | 
			
		||||
                            c->setBounds (pos, c->getY(), jmax (layout->currentSize, w - pos), c->getHeight());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    if (resizeOtherDimension)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (vertically)
 | 
			
		||||
                            c->setBounds (x, pos, w, layout->currentSize);
 | 
			
		||||
                        else
 | 
			
		||||
                            c->setBounds (pos, y, layout->currentSize, h);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (vertically)
 | 
			
		||||
                            c->setBounds (c->getX(), pos, c->getWidth(), layout->currentSize);
 | 
			
		||||
                        else
 | 
			
		||||
                            c->setBounds (pos, c->getY(), layout->currentSize, c->getHeight());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pos += layout->currentSize;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
StretchableLayoutManager::ItemLayoutProperties* StretchableLayoutManager::getInfoFor (const int itemIndex) const
 | 
			
		||||
{
 | 
			
		||||
    for (auto* i : items)
 | 
			
		||||
        if (i->itemIndex == itemIndex)
 | 
			
		||||
            return i;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::fitComponentsIntoSpace (const int startIndex,
 | 
			
		||||
                                                      const int endIndex,
 | 
			
		||||
                                                      const int availableSpace,
 | 
			
		||||
                                                      int startPos)
 | 
			
		||||
{
 | 
			
		||||
    // calculate the total sizes
 | 
			
		||||
    double totalIdealSize = 0.0;
 | 
			
		||||
    int totalMinimums = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = items.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
        layout->currentSize = sizeToRealSize (layout->minSize, totalSize);
 | 
			
		||||
 | 
			
		||||
        totalMinimums += layout->currentSize;
 | 
			
		||||
        totalIdealSize += sizeToRealSize (layout->preferredSize, totalSize);
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
    if (totalIdealSize <= 0)
 | 
			
		||||
        totalIdealSize = 1.0;
 | 
			
		||||
 | 
			
		||||
    // now calc the best sizes..
 | 
			
		||||
    int extraSpace = availableSpace - totalMinimums;
 | 
			
		||||
 | 
			
		||||
    while (extraSpace > 0)
 | 
			
		||||
    {
 | 
			
		||||
        int numWantingMoreSpace = 0;
 | 
			
		||||
        int numHavingTakenExtraSpace = 0;
 | 
			
		||||
 | 
			
		||||
        // first figure out how many comps want a slice of the extra space..
 | 
			
		||||
        for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto* layout = items.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
            auto sizeWanted = sizeToRealSize (layout->preferredSize, totalSize);
 | 
			
		||||
 | 
			
		||||
            auto bestSize = jlimit (layout->currentSize,
 | 
			
		||||
                                    jmax (layout->currentSize,
 | 
			
		||||
                                          sizeToRealSize (layout->maxSize, totalSize)),
 | 
			
		||||
                                    roundToInt (sizeWanted * availableSpace / totalIdealSize));
 | 
			
		||||
 | 
			
		||||
            if (bestSize > layout->currentSize)
 | 
			
		||||
                ++numWantingMoreSpace;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // ..share out the extra space..
 | 
			
		||||
        for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto* layout = items.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
            auto sizeWanted = sizeToRealSize (layout->preferredSize, totalSize);
 | 
			
		||||
 | 
			
		||||
            auto bestSize = jlimit (layout->currentSize,
 | 
			
		||||
                                    jmax (layout->currentSize, sizeToRealSize (layout->maxSize, totalSize)),
 | 
			
		||||
                                    roundToInt (sizeWanted * availableSpace / totalIdealSize));
 | 
			
		||||
 | 
			
		||||
            auto extraWanted = bestSize - layout->currentSize;
 | 
			
		||||
 | 
			
		||||
            if (extraWanted > 0)
 | 
			
		||||
            {
 | 
			
		||||
                auto extraAllowed = jmin (extraWanted,
 | 
			
		||||
                                          extraSpace / jmax (1, numWantingMoreSpace));
 | 
			
		||||
 | 
			
		||||
                if (extraAllowed > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    ++numHavingTakenExtraSpace;
 | 
			
		||||
                    --numWantingMoreSpace;
 | 
			
		||||
 | 
			
		||||
                    layout->currentSize += extraAllowed;
 | 
			
		||||
                    extraSpace -= extraAllowed;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (numHavingTakenExtraSpace <= 0)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ..and calculate the end position
 | 
			
		||||
    for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = items.getUnchecked(i);
 | 
			
		||||
        startPos += layout->currentSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return startPos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::getMinimumSizeOfItems (const int startIndex,
 | 
			
		||||
                                                     const int endIndex) const
 | 
			
		||||
{
 | 
			
		||||
    int totalMinimums = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
        totalMinimums += sizeToRealSize (items.getUnchecked (i)->minSize, totalSize);
 | 
			
		||||
 | 
			
		||||
    return totalMinimums;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::getMaximumSizeOfItems (const int startIndex, const int endIndex) const
 | 
			
		||||
{
 | 
			
		||||
    int totalMaximums = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
        totalMaximums += sizeToRealSize (items.getUnchecked (i)->maxSize, totalSize);
 | 
			
		||||
 | 
			
		||||
    return totalMaximums;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutManager::updatePrefSizesToMatchCurrentPositions()
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < items.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = items.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
        layout->preferredSize
 | 
			
		||||
            = (layout->preferredSize < 0) ? getItemCurrentRelativeSize (i)
 | 
			
		||||
                                          : getItemCurrentAbsoluteSize (i);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::sizeToRealSize (double size, int totalSpace)
 | 
			
		||||
{
 | 
			
		||||
    if (size < 0)
 | 
			
		||||
        size *= -totalSpace;
 | 
			
		||||
 | 
			
		||||
    return roundToInt (size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
StretchableLayoutManager::StretchableLayoutManager() {}
 | 
			
		||||
StretchableLayoutManager::~StretchableLayoutManager() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void StretchableLayoutManager::clearAllItems()
 | 
			
		||||
{
 | 
			
		||||
    items.clear();
 | 
			
		||||
    totalSize = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutManager::setItemLayout (const int itemIndex,
 | 
			
		||||
                                              const double minimumSize,
 | 
			
		||||
                                              const double maximumSize,
 | 
			
		||||
                                              const double preferredSize)
 | 
			
		||||
{
 | 
			
		||||
    auto* layout = getInfoFor (itemIndex);
 | 
			
		||||
 | 
			
		||||
    if (layout == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        layout = new ItemLayoutProperties();
 | 
			
		||||
        layout->itemIndex = itemIndex;
 | 
			
		||||
 | 
			
		||||
        int i;
 | 
			
		||||
        for (i = 0; i < items.size(); ++i)
 | 
			
		||||
            if (items.getUnchecked (i)->itemIndex > itemIndex)
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
        items.insert (i, layout);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    layout->minSize = minimumSize;
 | 
			
		||||
    layout->maxSize = maximumSize;
 | 
			
		||||
    layout->preferredSize = preferredSize;
 | 
			
		||||
    layout->currentSize = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool StretchableLayoutManager::getItemLayout (const int itemIndex,
 | 
			
		||||
                                              double& minimumSize,
 | 
			
		||||
                                              double& maximumSize,
 | 
			
		||||
                                              double& preferredSize) const
 | 
			
		||||
{
 | 
			
		||||
    if (auto* layout = getInfoFor (itemIndex))
 | 
			
		||||
    {
 | 
			
		||||
        minimumSize = layout->minSize;
 | 
			
		||||
        maximumSize = layout->maxSize;
 | 
			
		||||
        preferredSize = layout->preferredSize;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void StretchableLayoutManager::setTotalSize (const int newTotalSize)
 | 
			
		||||
{
 | 
			
		||||
    totalSize = newTotalSize;
 | 
			
		||||
 | 
			
		||||
    fitComponentsIntoSpace (0, items.size(), totalSize, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::getItemCurrentPosition (const int itemIndex) const
 | 
			
		||||
{
 | 
			
		||||
    int pos = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < itemIndex; ++i)
 | 
			
		||||
        if (auto* layout = getInfoFor (i))
 | 
			
		||||
            pos += layout->currentSize;
 | 
			
		||||
 | 
			
		||||
    return pos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::getItemCurrentAbsoluteSize (const int itemIndex) const
 | 
			
		||||
{
 | 
			
		||||
    if (auto* layout = getInfoFor (itemIndex))
 | 
			
		||||
        return layout->currentSize;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double StretchableLayoutManager::getItemCurrentRelativeSize (const int itemIndex) const
 | 
			
		||||
{
 | 
			
		||||
    if (auto* layout = getInfoFor (itemIndex))
 | 
			
		||||
        return -layout->currentSize / (double) totalSize;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutManager::setItemPosition (const int itemIndex,
 | 
			
		||||
                                                int newPosition)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = items.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = items.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (layout->itemIndex == itemIndex)
 | 
			
		||||
        {
 | 
			
		||||
            auto realTotalSize = jmax (totalSize, getMinimumSizeOfItems (0, items.size()));
 | 
			
		||||
            auto minSizeAfterThisComp = getMinimumSizeOfItems (i, items.size());
 | 
			
		||||
            auto maxSizeAfterThisComp = getMaximumSizeOfItems (i + 1, items.size());
 | 
			
		||||
 | 
			
		||||
            newPosition = jmax (newPosition, totalSize - maxSizeAfterThisComp - layout->currentSize);
 | 
			
		||||
            newPosition = jmin (newPosition, realTotalSize - minSizeAfterThisComp);
 | 
			
		||||
 | 
			
		||||
            auto endPos = fitComponentsIntoSpace (0, i, newPosition, 0);
 | 
			
		||||
 | 
			
		||||
            endPos += layout->currentSize;
 | 
			
		||||
 | 
			
		||||
            fitComponentsIntoSpace (i + 1, items.size(), totalSize - endPos, endPos);
 | 
			
		||||
            updatePrefSizesToMatchCurrentPositions();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void StretchableLayoutManager::layOutComponents (Component** const components,
 | 
			
		||||
                                                 int numComponents,
 | 
			
		||||
                                                 int x, int y, int w, int h,
 | 
			
		||||
                                                 const bool vertically,
 | 
			
		||||
                                                 const bool resizeOtherDimension)
 | 
			
		||||
{
 | 
			
		||||
    setTotalSize (vertically ? h : w);
 | 
			
		||||
    int pos = vertically ? y : x;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < numComponents; ++i)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* layout = getInfoFor (i))
 | 
			
		||||
        {
 | 
			
		||||
            if (auto* c = components[i])
 | 
			
		||||
            {
 | 
			
		||||
                if (i == numComponents - 1)
 | 
			
		||||
                {
 | 
			
		||||
                    // if it's the last item, crop it to exactly fit the available space..
 | 
			
		||||
                    if (resizeOtherDimension)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (vertically)
 | 
			
		||||
                            c->setBounds (x, pos, w, jmax (layout->currentSize, h - pos));
 | 
			
		||||
                        else
 | 
			
		||||
                            c->setBounds (pos, y, jmax (layout->currentSize, w - pos), h);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (vertically)
 | 
			
		||||
                            c->setBounds (c->getX(), pos, c->getWidth(), jmax (layout->currentSize, h - pos));
 | 
			
		||||
                        else
 | 
			
		||||
                            c->setBounds (pos, c->getY(), jmax (layout->currentSize, w - pos), c->getHeight());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    if (resizeOtherDimension)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (vertically)
 | 
			
		||||
                            c->setBounds (x, pos, w, layout->currentSize);
 | 
			
		||||
                        else
 | 
			
		||||
                            c->setBounds (pos, y, layout->currentSize, h);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        if (vertically)
 | 
			
		||||
                            c->setBounds (c->getX(), pos, c->getWidth(), layout->currentSize);
 | 
			
		||||
                        else
 | 
			
		||||
                            c->setBounds (pos, c->getY(), layout->currentSize, c->getHeight());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pos += layout->currentSize;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
StretchableLayoutManager::ItemLayoutProperties* StretchableLayoutManager::getInfoFor (const int itemIndex) const
 | 
			
		||||
{
 | 
			
		||||
    for (auto* i : items)
 | 
			
		||||
        if (i->itemIndex == itemIndex)
 | 
			
		||||
            return i;
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::fitComponentsIntoSpace (const int startIndex,
 | 
			
		||||
                                                      const int endIndex,
 | 
			
		||||
                                                      const int availableSpace,
 | 
			
		||||
                                                      int startPos)
 | 
			
		||||
{
 | 
			
		||||
    // calculate the total sizes
 | 
			
		||||
    double totalIdealSize = 0.0;
 | 
			
		||||
    int totalMinimums = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = items.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
        layout->currentSize = sizeToRealSize (layout->minSize, totalSize);
 | 
			
		||||
 | 
			
		||||
        totalMinimums += layout->currentSize;
 | 
			
		||||
        totalIdealSize += sizeToRealSize (layout->preferredSize, totalSize);
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
    if (totalIdealSize <= 0)
 | 
			
		||||
        totalIdealSize = 1.0;
 | 
			
		||||
 | 
			
		||||
    // now calc the best sizes..
 | 
			
		||||
    int extraSpace = availableSpace - totalMinimums;
 | 
			
		||||
 | 
			
		||||
    while (extraSpace > 0)
 | 
			
		||||
    {
 | 
			
		||||
        int numWantingMoreSpace = 0;
 | 
			
		||||
        int numHavingTakenExtraSpace = 0;
 | 
			
		||||
 | 
			
		||||
        // first figure out how many comps want a slice of the extra space..
 | 
			
		||||
        for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto* layout = items.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
            auto sizeWanted = sizeToRealSize (layout->preferredSize, totalSize);
 | 
			
		||||
 | 
			
		||||
            auto bestSize = jlimit (layout->currentSize,
 | 
			
		||||
                                    jmax (layout->currentSize,
 | 
			
		||||
                                          sizeToRealSize (layout->maxSize, totalSize)),
 | 
			
		||||
                                    roundToInt (sizeWanted * availableSpace / totalIdealSize));
 | 
			
		||||
 | 
			
		||||
            if (bestSize > layout->currentSize)
 | 
			
		||||
                ++numWantingMoreSpace;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // ..share out the extra space..
 | 
			
		||||
        for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto* layout = items.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
            auto sizeWanted = sizeToRealSize (layout->preferredSize, totalSize);
 | 
			
		||||
 | 
			
		||||
            auto bestSize = jlimit (layout->currentSize,
 | 
			
		||||
                                    jmax (layout->currentSize, sizeToRealSize (layout->maxSize, totalSize)),
 | 
			
		||||
                                    roundToInt (sizeWanted * availableSpace / totalIdealSize));
 | 
			
		||||
 | 
			
		||||
            auto extraWanted = bestSize - layout->currentSize;
 | 
			
		||||
 | 
			
		||||
            if (extraWanted > 0)
 | 
			
		||||
            {
 | 
			
		||||
                auto extraAllowed = jmin (extraWanted,
 | 
			
		||||
                                          extraSpace / jmax (1, numWantingMoreSpace));
 | 
			
		||||
 | 
			
		||||
                if (extraAllowed > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    ++numHavingTakenExtraSpace;
 | 
			
		||||
                    --numWantingMoreSpace;
 | 
			
		||||
 | 
			
		||||
                    layout->currentSize += extraAllowed;
 | 
			
		||||
                    extraSpace -= extraAllowed;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (numHavingTakenExtraSpace <= 0)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ..and calculate the end position
 | 
			
		||||
    for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = items.getUnchecked(i);
 | 
			
		||||
        startPos += layout->currentSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return startPos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::getMinimumSizeOfItems (const int startIndex,
 | 
			
		||||
                                                     const int endIndex) const
 | 
			
		||||
{
 | 
			
		||||
    int totalMinimums = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
        totalMinimums += sizeToRealSize (items.getUnchecked (i)->minSize, totalSize);
 | 
			
		||||
 | 
			
		||||
    return totalMinimums;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::getMaximumSizeOfItems (const int startIndex, const int endIndex) const
 | 
			
		||||
{
 | 
			
		||||
    int totalMaximums = 0;
 | 
			
		||||
 | 
			
		||||
    for (int i = startIndex; i < endIndex; ++i)
 | 
			
		||||
        totalMaximums += sizeToRealSize (items.getUnchecked (i)->maxSize, totalSize);
 | 
			
		||||
 | 
			
		||||
    return totalMaximums;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutManager::updatePrefSizesToMatchCurrentPositions()
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < items.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto* layout = items.getUnchecked (i);
 | 
			
		||||
 | 
			
		||||
        layout->preferredSize
 | 
			
		||||
            = (layout->preferredSize < 0) ? getItemCurrentRelativeSize (i)
 | 
			
		||||
                                          : getItemCurrentAbsoluteSize (i);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int StretchableLayoutManager::sizeToRealSize (double size, int totalSpace)
 | 
			
		||||
{
 | 
			
		||||
    if (size < 0)
 | 
			
		||||
        size *= -totalSpace;
 | 
			
		||||
 | 
			
		||||
    return roundToInt (size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,261 +1,261 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    For laying out a set of components, where the components have preferred sizes
 | 
			
		||||
    and size limits, but where they are allowed to stretch to fill the available
 | 
			
		||||
    space.
 | 
			
		||||
 | 
			
		||||
    For example, if you have a component containing several other components, and
 | 
			
		||||
    each one should be given a share of the total size, you could use one of these
 | 
			
		||||
    to resize the child components when the parent component is resized. Then
 | 
			
		||||
    you could add a StretchableLayoutResizerBar to easily let the user rescale them.
 | 
			
		||||
 | 
			
		||||
    A StretchableLayoutManager operates only in one dimension, so if you have a set
 | 
			
		||||
    of components stacked vertically on top of each other, you'd use one to manage their
 | 
			
		||||
    heights. To build up complex arrangements of components, e.g. for applications
 | 
			
		||||
    with multiple nested panels, you would use more than one StretchableLayoutManager.
 | 
			
		||||
    E.g. by using two (one vertical, one horizontal), you could create a resizable
 | 
			
		||||
    spreadsheet-style table.
 | 
			
		||||
 | 
			
		||||
    E.g.
 | 
			
		||||
    @code
 | 
			
		||||
    class MyComp  : public Component
 | 
			
		||||
    {
 | 
			
		||||
        StretchableLayoutManager myLayout;
 | 
			
		||||
 | 
			
		||||
        MyComp()
 | 
			
		||||
        {
 | 
			
		||||
            myLayout.setItemLayout (0,          // for item 0
 | 
			
		||||
                                    50, 100,    // must be between 50 and 100 pixels in size
 | 
			
		||||
                                    -0.6);      // and its preferred size is 60% of the total available space
 | 
			
		||||
 | 
			
		||||
            myLayout.setItemLayout (1,          // for item 1
 | 
			
		||||
                                    -0.2, -0.6, // size must be between 20% and 60% of the available space
 | 
			
		||||
                                    50);        // and its preferred size is 50 pixels
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void resized()
 | 
			
		||||
        {
 | 
			
		||||
            // make a list of two of our child components that we want to reposition
 | 
			
		||||
            Component* comps[] = { myComp1, myComp2 };
 | 
			
		||||
 | 
			
		||||
            // this will position the 2 components, one above the other, to fit
 | 
			
		||||
            // vertically into the rectangle provided.
 | 
			
		||||
            myLayout.layOutComponents (comps, 2,
 | 
			
		||||
                                       0, 0, getWidth(), getHeight(),
 | 
			
		||||
                                       true);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    @endcode
 | 
			
		||||
 | 
			
		||||
    @see StretchableLayoutResizerBar
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  StretchableLayoutManager
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty layout.
 | 
			
		||||
 | 
			
		||||
        You'll need to add some item properties to the layout before it can be used
 | 
			
		||||
        to resize things - see setItemLayout().
 | 
			
		||||
    */
 | 
			
		||||
    StretchableLayoutManager();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~StretchableLayoutManager();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** For a numbered item, this sets its size limits and preferred size.
 | 
			
		||||
 | 
			
		||||
        @param itemIndex        the index of the item to change.
 | 
			
		||||
        @param minimumSize      the minimum size that this item is allowed to be - a positive number
 | 
			
		||||
                                indicates an absolute size in pixels. A negative number indicates a
 | 
			
		||||
                                proportion of the available space (e.g -0.5 is 50%)
 | 
			
		||||
        @param maximumSize      the maximum size that this item is allowed to be - a positive number
 | 
			
		||||
                                indicates an absolute size in pixels. A negative number indicates a
 | 
			
		||||
                                proportion of the available space
 | 
			
		||||
        @param preferredSize    the size that this item would like to be, if there's enough room. A
 | 
			
		||||
                                positive number indicates an absolute size in pixels. A negative number
 | 
			
		||||
                                indicates a proportion of the available space
 | 
			
		||||
        @see getItemLayout
 | 
			
		||||
    */
 | 
			
		||||
    void setItemLayout (int itemIndex,
 | 
			
		||||
                        double minimumSize,
 | 
			
		||||
                        double maximumSize,
 | 
			
		||||
                        double preferredSize);
 | 
			
		||||
 | 
			
		||||
    /** For a numbered item, this returns its size limits and preferred size.
 | 
			
		||||
 | 
			
		||||
        @param itemIndex        the index of the item.
 | 
			
		||||
        @param minimumSize      the minimum size that this item is allowed to be - a positive number
 | 
			
		||||
                                indicates an absolute size in pixels. A negative number indicates a
 | 
			
		||||
                                proportion of the available space (e.g -0.5 is 50%)
 | 
			
		||||
        @param maximumSize      the maximum size that this item is allowed to be - a positive number
 | 
			
		||||
                                indicates an absolute size in pixels. A negative number indicates a
 | 
			
		||||
                                proportion of the available space
 | 
			
		||||
        @param preferredSize    the size that this item would like to be, if there's enough room. A
 | 
			
		||||
                                positive number indicates an absolute size in pixels. A negative number
 | 
			
		||||
                                indicates a proportion of the available space
 | 
			
		||||
        @returns false if the item's properties hadn't been set
 | 
			
		||||
        @see setItemLayout
 | 
			
		||||
    */
 | 
			
		||||
    bool getItemLayout (int itemIndex,
 | 
			
		||||
                        double& minimumSize,
 | 
			
		||||
                        double& maximumSize,
 | 
			
		||||
                        double& preferredSize) const;
 | 
			
		||||
 | 
			
		||||
    /** Clears all the properties that have been set with setItemLayout() and resets
 | 
			
		||||
        this object to its initial state.
 | 
			
		||||
    */
 | 
			
		||||
    void clearAllItems();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Takes a set of components that correspond to the layout's items, and positions
 | 
			
		||||
        them to fill a space.
 | 
			
		||||
 | 
			
		||||
        This will try to give each item its preferred size, whether that's a relative size
 | 
			
		||||
        or an absolute one.
 | 
			
		||||
 | 
			
		||||
        @param components       an array of components that correspond to each of the
 | 
			
		||||
                                numbered items that the StretchableLayoutManager object
 | 
			
		||||
                                has been told about with setItemLayout()
 | 
			
		||||
        @param numComponents    the number of components in the array that is passed-in. This
 | 
			
		||||
                                should be the same as the number of items this object has been
 | 
			
		||||
                                told about.
 | 
			
		||||
        @param x                the left of the rectangle in which the components should
 | 
			
		||||
                                be laid out
 | 
			
		||||
        @param y                the top of the rectangle in which the components should
 | 
			
		||||
                                be laid out
 | 
			
		||||
        @param width            the width of the rectangle in which the components should
 | 
			
		||||
                                be laid out
 | 
			
		||||
        @param height           the height of the rectangle in which the components should
 | 
			
		||||
                                be laid out
 | 
			
		||||
        @param vertically       if true, the components will be positioned in a vertical stack,
 | 
			
		||||
                                so that they fill the height of the rectangle. If false, they
 | 
			
		||||
                                will be placed side-by-side in a horizontal line, filling the
 | 
			
		||||
                                available width
 | 
			
		||||
        @param resizeOtherDimension     if true, this means that the components will have their
 | 
			
		||||
                                other dimension resized to fit the space - i.e. if the 'vertically'
 | 
			
		||||
                                parameter is true, their x-positions and widths are adjusted to fit
 | 
			
		||||
                                the x and width parameters; if 'vertically' is false, their y-positions
 | 
			
		||||
                                and heights are adjusted to fit the y and height parameters.
 | 
			
		||||
    */
 | 
			
		||||
    void layOutComponents (Component** components,
 | 
			
		||||
                           int numComponents,
 | 
			
		||||
                           int x, int y, int width, int height,
 | 
			
		||||
                           bool vertically,
 | 
			
		||||
                           bool resizeOtherDimension);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the current position of one of the items.
 | 
			
		||||
 | 
			
		||||
        This is only a valid call after layOutComponents() has been called, as it
 | 
			
		||||
        returns the last position that this item was placed at. If the layout was
 | 
			
		||||
        vertical, the value returned will be the y position of the top of the item,
 | 
			
		||||
        relative to the top of the rectangle in which the items were placed (so for
 | 
			
		||||
        example, item 0 will always have position of 0, even in the rectangle passed
 | 
			
		||||
        in to layOutComponents() wasn't at y = 0). If the layout was done horizontally,
 | 
			
		||||
        the position returned is the item's left-hand position, again relative to the
 | 
			
		||||
        x position of the rectangle used.
 | 
			
		||||
 | 
			
		||||
        @see getItemCurrentSize, setItemPosition
 | 
			
		||||
    */
 | 
			
		||||
    int getItemCurrentPosition (int itemIndex) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current size of one of the items.
 | 
			
		||||
 | 
			
		||||
        This is only meaningful after layOutComponents() has been called, as it
 | 
			
		||||
        returns the last size that this item was given. If the layout was done
 | 
			
		||||
        vertically, it'll return the item's height in pixels; if it was horizontal,
 | 
			
		||||
        it'll return its width.
 | 
			
		||||
 | 
			
		||||
        @see getItemCurrentRelativeSize
 | 
			
		||||
    */
 | 
			
		||||
    int getItemCurrentAbsoluteSize (int itemIndex) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current size of one of the items.
 | 
			
		||||
 | 
			
		||||
        This is only meaningful after layOutComponents() has been called, as it
 | 
			
		||||
        returns the last size that this item was given. If the layout was done
 | 
			
		||||
        vertically, it'll return a negative value representing the item's height relative
 | 
			
		||||
        to the last size used for laying the components out; if the layout was done
 | 
			
		||||
        horizontally it'll be the proportion of its width.
 | 
			
		||||
 | 
			
		||||
        @see getItemCurrentAbsoluteSize
 | 
			
		||||
    */
 | 
			
		||||
    double getItemCurrentRelativeSize (int itemIndex) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Moves one of the items, shifting along any other items as necessary in
 | 
			
		||||
        order to get it to the desired position.
 | 
			
		||||
 | 
			
		||||
        Calling this method will also update the preferred sizes of the items it
 | 
			
		||||
        shuffles along, so that they reflect their new positions.
 | 
			
		||||
 | 
			
		||||
        (This is the method that a StretchableLayoutResizerBar uses to shift the items
 | 
			
		||||
        about when it's dragged).
 | 
			
		||||
 | 
			
		||||
        @param itemIndex        the item to move
 | 
			
		||||
        @param newPosition      the absolute position that you'd like this item to move
 | 
			
		||||
                                to. The item might not be able to always reach exactly this position,
 | 
			
		||||
                                because other items may have minimum sizes that constrain how
 | 
			
		||||
                                far it can go
 | 
			
		||||
    */
 | 
			
		||||
    void setItemPosition (int itemIndex,
 | 
			
		||||
                          int newPosition);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct ItemLayoutProperties
 | 
			
		||||
    {
 | 
			
		||||
        int itemIndex;
 | 
			
		||||
        int currentSize;
 | 
			
		||||
        double minSize, maxSize, preferredSize;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<ItemLayoutProperties> items;
 | 
			
		||||
    int totalSize = 0;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static int sizeToRealSize (double size, int totalSpace);
 | 
			
		||||
    ItemLayoutProperties* getInfoFor (int itemIndex) const;
 | 
			
		||||
    void setTotalSize (int newTotalSize);
 | 
			
		||||
    int fitComponentsIntoSpace (int startIndex, int endIndex, int availableSpace, int startPos);
 | 
			
		||||
    int getMinimumSizeOfItems (int startIndex, int endIndex) const;
 | 
			
		||||
    int getMaximumSizeOfItems (int startIndex, int endIndex) const;
 | 
			
		||||
    void updatePrefSizesToMatchCurrentPositions();
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableLayoutManager)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    For laying out a set of components, where the components have preferred sizes
 | 
			
		||||
    and size limits, but where they are allowed to stretch to fill the available
 | 
			
		||||
    space.
 | 
			
		||||
 | 
			
		||||
    For example, if you have a component containing several other components, and
 | 
			
		||||
    each one should be given a share of the total size, you could use one of these
 | 
			
		||||
    to resize the child components when the parent component is resized. Then
 | 
			
		||||
    you could add a StretchableLayoutResizerBar to easily let the user rescale them.
 | 
			
		||||
 | 
			
		||||
    A StretchableLayoutManager operates only in one dimension, so if you have a set
 | 
			
		||||
    of components stacked vertically on top of each other, you'd use one to manage their
 | 
			
		||||
    heights. To build up complex arrangements of components, e.g. for applications
 | 
			
		||||
    with multiple nested panels, you would use more than one StretchableLayoutManager.
 | 
			
		||||
    E.g. by using two (one vertical, one horizontal), you could create a resizable
 | 
			
		||||
    spreadsheet-style table.
 | 
			
		||||
 | 
			
		||||
    E.g.
 | 
			
		||||
    @code
 | 
			
		||||
    class MyComp  : public Component
 | 
			
		||||
    {
 | 
			
		||||
        StretchableLayoutManager myLayout;
 | 
			
		||||
 | 
			
		||||
        MyComp()
 | 
			
		||||
        {
 | 
			
		||||
            myLayout.setItemLayout (0,          // for item 0
 | 
			
		||||
                                    50, 100,    // must be between 50 and 100 pixels in size
 | 
			
		||||
                                    -0.6);      // and its preferred size is 60% of the total available space
 | 
			
		||||
 | 
			
		||||
            myLayout.setItemLayout (1,          // for item 1
 | 
			
		||||
                                    -0.2, -0.6, // size must be between 20% and 60% of the available space
 | 
			
		||||
                                    50);        // and its preferred size is 50 pixels
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void resized()
 | 
			
		||||
        {
 | 
			
		||||
            // make a list of two of our child components that we want to reposition
 | 
			
		||||
            Component* comps[] = { myComp1, myComp2 };
 | 
			
		||||
 | 
			
		||||
            // this will position the 2 components, one above the other, to fit
 | 
			
		||||
            // vertically into the rectangle provided.
 | 
			
		||||
            myLayout.layOutComponents (comps, 2,
 | 
			
		||||
                                       0, 0, getWidth(), getHeight(),
 | 
			
		||||
                                       true);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    @endcode
 | 
			
		||||
 | 
			
		||||
    @see StretchableLayoutResizerBar
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  StretchableLayoutManager
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty layout.
 | 
			
		||||
 | 
			
		||||
        You'll need to add some item properties to the layout before it can be used
 | 
			
		||||
        to resize things - see setItemLayout().
 | 
			
		||||
    */
 | 
			
		||||
    StretchableLayoutManager();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~StretchableLayoutManager();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** For a numbered item, this sets its size limits and preferred size.
 | 
			
		||||
 | 
			
		||||
        @param itemIndex        the index of the item to change.
 | 
			
		||||
        @param minimumSize      the minimum size that this item is allowed to be - a positive number
 | 
			
		||||
                                indicates an absolute size in pixels. A negative number indicates a
 | 
			
		||||
                                proportion of the available space (e.g -0.5 is 50%)
 | 
			
		||||
        @param maximumSize      the maximum size that this item is allowed to be - a positive number
 | 
			
		||||
                                indicates an absolute size in pixels. A negative number indicates a
 | 
			
		||||
                                proportion of the available space
 | 
			
		||||
        @param preferredSize    the size that this item would like to be, if there's enough room. A
 | 
			
		||||
                                positive number indicates an absolute size in pixels. A negative number
 | 
			
		||||
                                indicates a proportion of the available space
 | 
			
		||||
        @see getItemLayout
 | 
			
		||||
    */
 | 
			
		||||
    void setItemLayout (int itemIndex,
 | 
			
		||||
                        double minimumSize,
 | 
			
		||||
                        double maximumSize,
 | 
			
		||||
                        double preferredSize);
 | 
			
		||||
 | 
			
		||||
    /** For a numbered item, this returns its size limits and preferred size.
 | 
			
		||||
 | 
			
		||||
        @param itemIndex        the index of the item.
 | 
			
		||||
        @param minimumSize      the minimum size that this item is allowed to be - a positive number
 | 
			
		||||
                                indicates an absolute size in pixels. A negative number indicates a
 | 
			
		||||
                                proportion of the available space (e.g -0.5 is 50%)
 | 
			
		||||
        @param maximumSize      the maximum size that this item is allowed to be - a positive number
 | 
			
		||||
                                indicates an absolute size in pixels. A negative number indicates a
 | 
			
		||||
                                proportion of the available space
 | 
			
		||||
        @param preferredSize    the size that this item would like to be, if there's enough room. A
 | 
			
		||||
                                positive number indicates an absolute size in pixels. A negative number
 | 
			
		||||
                                indicates a proportion of the available space
 | 
			
		||||
        @returns false if the item's properties hadn't been set
 | 
			
		||||
        @see setItemLayout
 | 
			
		||||
    */
 | 
			
		||||
    bool getItemLayout (int itemIndex,
 | 
			
		||||
                        double& minimumSize,
 | 
			
		||||
                        double& maximumSize,
 | 
			
		||||
                        double& preferredSize) const;
 | 
			
		||||
 | 
			
		||||
    /** Clears all the properties that have been set with setItemLayout() and resets
 | 
			
		||||
        this object to its initial state.
 | 
			
		||||
    */
 | 
			
		||||
    void clearAllItems();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Takes a set of components that correspond to the layout's items, and positions
 | 
			
		||||
        them to fill a space.
 | 
			
		||||
 | 
			
		||||
        This will try to give each item its preferred size, whether that's a relative size
 | 
			
		||||
        or an absolute one.
 | 
			
		||||
 | 
			
		||||
        @param components       an array of components that correspond to each of the
 | 
			
		||||
                                numbered items that the StretchableLayoutManager object
 | 
			
		||||
                                has been told about with setItemLayout()
 | 
			
		||||
        @param numComponents    the number of components in the array that is passed-in. This
 | 
			
		||||
                                should be the same as the number of items this object has been
 | 
			
		||||
                                told about.
 | 
			
		||||
        @param x                the left of the rectangle in which the components should
 | 
			
		||||
                                be laid out
 | 
			
		||||
        @param y                the top of the rectangle in which the components should
 | 
			
		||||
                                be laid out
 | 
			
		||||
        @param width            the width of the rectangle in which the components should
 | 
			
		||||
                                be laid out
 | 
			
		||||
        @param height           the height of the rectangle in which the components should
 | 
			
		||||
                                be laid out
 | 
			
		||||
        @param vertically       if true, the components will be positioned in a vertical stack,
 | 
			
		||||
                                so that they fill the height of the rectangle. If false, they
 | 
			
		||||
                                will be placed side-by-side in a horizontal line, filling the
 | 
			
		||||
                                available width
 | 
			
		||||
        @param resizeOtherDimension     if true, this means that the components will have their
 | 
			
		||||
                                other dimension resized to fit the space - i.e. if the 'vertically'
 | 
			
		||||
                                parameter is true, their x-positions and widths are adjusted to fit
 | 
			
		||||
                                the x and width parameters; if 'vertically' is false, their y-positions
 | 
			
		||||
                                and heights are adjusted to fit the y and height parameters.
 | 
			
		||||
    */
 | 
			
		||||
    void layOutComponents (Component** components,
 | 
			
		||||
                           int numComponents,
 | 
			
		||||
                           int x, int y, int width, int height,
 | 
			
		||||
                           bool vertically,
 | 
			
		||||
                           bool resizeOtherDimension);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the current position of one of the items.
 | 
			
		||||
 | 
			
		||||
        This is only a valid call after layOutComponents() has been called, as it
 | 
			
		||||
        returns the last position that this item was placed at. If the layout was
 | 
			
		||||
        vertical, the value returned will be the y position of the top of the item,
 | 
			
		||||
        relative to the top of the rectangle in which the items were placed (so for
 | 
			
		||||
        example, item 0 will always have position of 0, even in the rectangle passed
 | 
			
		||||
        in to layOutComponents() wasn't at y = 0). If the layout was done horizontally,
 | 
			
		||||
        the position returned is the item's left-hand position, again relative to the
 | 
			
		||||
        x position of the rectangle used.
 | 
			
		||||
 | 
			
		||||
        @see getItemCurrentSize, setItemPosition
 | 
			
		||||
    */
 | 
			
		||||
    int getItemCurrentPosition (int itemIndex) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current size of one of the items.
 | 
			
		||||
 | 
			
		||||
        This is only meaningful after layOutComponents() has been called, as it
 | 
			
		||||
        returns the last size that this item was given. If the layout was done
 | 
			
		||||
        vertically, it'll return the item's height in pixels; if it was horizontal,
 | 
			
		||||
        it'll return its width.
 | 
			
		||||
 | 
			
		||||
        @see getItemCurrentRelativeSize
 | 
			
		||||
    */
 | 
			
		||||
    int getItemCurrentAbsoluteSize (int itemIndex) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current size of one of the items.
 | 
			
		||||
 | 
			
		||||
        This is only meaningful after layOutComponents() has been called, as it
 | 
			
		||||
        returns the last size that this item was given. If the layout was done
 | 
			
		||||
        vertically, it'll return a negative value representing the item's height relative
 | 
			
		||||
        to the last size used for laying the components out; if the layout was done
 | 
			
		||||
        horizontally it'll be the proportion of its width.
 | 
			
		||||
 | 
			
		||||
        @see getItemCurrentAbsoluteSize
 | 
			
		||||
    */
 | 
			
		||||
    double getItemCurrentRelativeSize (int itemIndex) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Moves one of the items, shifting along any other items as necessary in
 | 
			
		||||
        order to get it to the desired position.
 | 
			
		||||
 | 
			
		||||
        Calling this method will also update the preferred sizes of the items it
 | 
			
		||||
        shuffles along, so that they reflect their new positions.
 | 
			
		||||
 | 
			
		||||
        (This is the method that a StretchableLayoutResizerBar uses to shift the items
 | 
			
		||||
        about when it's dragged).
 | 
			
		||||
 | 
			
		||||
        @param itemIndex        the item to move
 | 
			
		||||
        @param newPosition      the absolute position that you'd like this item to move
 | 
			
		||||
                                to. The item might not be able to always reach exactly this position,
 | 
			
		||||
                                because other items may have minimum sizes that constrain how
 | 
			
		||||
                                far it can go
 | 
			
		||||
    */
 | 
			
		||||
    void setItemPosition (int itemIndex,
 | 
			
		||||
                          int newPosition);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct ItemLayoutProperties
 | 
			
		||||
    {
 | 
			
		||||
        int itemIndex;
 | 
			
		||||
        int currentSize;
 | 
			
		||||
        double minSize, maxSize, preferredSize;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<ItemLayoutProperties> items;
 | 
			
		||||
    int totalSize = 0;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    static int sizeToRealSize (double size, int totalSpace);
 | 
			
		||||
    ItemLayoutProperties* getInfoFor (int itemIndex) const;
 | 
			
		||||
    void setTotalSize (int newTotalSize);
 | 
			
		||||
    int fitComponentsIntoSpace (int startIndex, int endIndex, int availableSpace, int startPos);
 | 
			
		||||
    int getMinimumSizeOfItems (int startIndex, int endIndex) const;
 | 
			
		||||
    int getMaximumSizeOfItems (int startIndex, int endIndex) const;
 | 
			
		||||
    void updatePrefSizesToMatchCurrentPositions();
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableLayoutManager)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,79 +1,79 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
StretchableLayoutResizerBar::StretchableLayoutResizerBar (StretchableLayoutManager* layout_,
 | 
			
		||||
                                                          const int index,
 | 
			
		||||
                                                          const bool vertical)
 | 
			
		||||
    : layout (layout_),
 | 
			
		||||
      itemIndex (index),
 | 
			
		||||
      isVertical (vertical)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setMouseCursor (vertical ? MouseCursor::LeftRightResizeCursor
 | 
			
		||||
                             : MouseCursor::UpDownResizeCursor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StretchableLayoutResizerBar::~StretchableLayoutResizerBar()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void StretchableLayoutResizerBar::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawStretchableLayoutResizerBar (g,
 | 
			
		||||
                                                      getWidth(), getHeight(),
 | 
			
		||||
                                                      isVertical,
 | 
			
		||||
                                                      isMouseOver(),
 | 
			
		||||
                                                      isMouseButtonDown());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutResizerBar::mouseDown (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    mouseDownPos = layout->getItemCurrentPosition (itemIndex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutResizerBar::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    const int desiredPos = mouseDownPos + (isVertical ? e.getDistanceFromDragStartX()
 | 
			
		||||
                                                      : e.getDistanceFromDragStartY());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (layout->getItemCurrentPosition (itemIndex) != desiredPos)
 | 
			
		||||
    {
 | 
			
		||||
        layout->setItemPosition (itemIndex, desiredPos);
 | 
			
		||||
        hasBeenMoved();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutResizerBar::hasBeenMoved()
 | 
			
		||||
{
 | 
			
		||||
    if (Component* parent = getParentComponent())
 | 
			
		||||
        parent->resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
StretchableLayoutResizerBar::StretchableLayoutResizerBar (StretchableLayoutManager* layout_,
 | 
			
		||||
                                                          const int index,
 | 
			
		||||
                                                          const bool vertical)
 | 
			
		||||
    : layout (layout_),
 | 
			
		||||
      itemIndex (index),
 | 
			
		||||
      isVertical (vertical)
 | 
			
		||||
{
 | 
			
		||||
    setRepaintsOnMouseActivity (true);
 | 
			
		||||
    setMouseCursor (vertical ? MouseCursor::LeftRightResizeCursor
 | 
			
		||||
                             : MouseCursor::UpDownResizeCursor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StretchableLayoutResizerBar::~StretchableLayoutResizerBar()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void StretchableLayoutResizerBar::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    getLookAndFeel().drawStretchableLayoutResizerBar (g,
 | 
			
		||||
                                                      getWidth(), getHeight(),
 | 
			
		||||
                                                      isVertical,
 | 
			
		||||
                                                      isMouseOver(),
 | 
			
		||||
                                                      isMouseButtonDown());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutResizerBar::mouseDown (const MouseEvent&)
 | 
			
		||||
{
 | 
			
		||||
    mouseDownPos = layout->getItemCurrentPosition (itemIndex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutResizerBar::mouseDrag (const MouseEvent& e)
 | 
			
		||||
{
 | 
			
		||||
    const int desiredPos = mouseDownPos + (isVertical ? e.getDistanceFromDragStartX()
 | 
			
		||||
                                                      : e.getDistanceFromDragStartY());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (layout->getItemCurrentPosition (itemIndex) != desiredPos)
 | 
			
		||||
    {
 | 
			
		||||
        layout->setItemPosition (itemIndex, desiredPos);
 | 
			
		||||
        hasBeenMoved();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableLayoutResizerBar::hasBeenMoved()
 | 
			
		||||
{
 | 
			
		||||
    if (Component* parent = getParentComponent())
 | 
			
		||||
        parent->resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,105 +1,105 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that acts as one of the vertical or horizontal bars you see being
 | 
			
		||||
    used to resize panels in a window.
 | 
			
		||||
 | 
			
		||||
    One of these acts with a StretchableLayoutManager to resize the other components.
 | 
			
		||||
 | 
			
		||||
    @see StretchableLayoutManager
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  StretchableLayoutResizerBar  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a resizer bar for use on a specified layout.
 | 
			
		||||
 | 
			
		||||
        @param layoutToUse          the layout that will be affected when this bar
 | 
			
		||||
                                    is dragged
 | 
			
		||||
        @param itemIndexInLayout    the item index in the layout that corresponds to
 | 
			
		||||
                                    this bar component. You'll need to set up the item
 | 
			
		||||
                                    properties in a suitable way for a divider bar, e.g.
 | 
			
		||||
                                    for an 8-pixel wide bar which, you could call
 | 
			
		||||
                                    myLayout->setItemLayout (barIndex, 8, 8, 8)
 | 
			
		||||
        @param isBarVertical        true if it's an upright bar that you drag left and
 | 
			
		||||
                                    right; false for a horizontal one that you drag up and
 | 
			
		||||
                                    down
 | 
			
		||||
    */
 | 
			
		||||
    StretchableLayoutResizerBar (StretchableLayoutManager* layoutToUse,
 | 
			
		||||
                                 int itemIndexInLayout,
 | 
			
		||||
                                 bool isBarVertical);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~StretchableLayoutResizerBar() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This is called when the bar is dragged.
 | 
			
		||||
 | 
			
		||||
        This method must update the positions of any components whose position is
 | 
			
		||||
        determined by the StretchableLayoutManager, because they might have just
 | 
			
		||||
        moved.
 | 
			
		||||
 | 
			
		||||
        The default implementation calls the resized() method of this component's
 | 
			
		||||
        parent component, because that's often where you're likely to apply the
 | 
			
		||||
        layout, but it can be overridden for more specific needs.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void hasBeenMoved();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes. */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual void drawStretchableLayoutResizerBar (Graphics&, int w, int h,
 | 
			
		||||
                                                      bool isVerticalBar, bool isMouseOver, bool isMouseDragging) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    StretchableLayoutManager* layout;
 | 
			
		||||
    int itemIndex, mouseDownPos;
 | 
			
		||||
    bool isVertical;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableLayoutResizerBar)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component that acts as one of the vertical or horizontal bars you see being
 | 
			
		||||
    used to resize panels in a window.
 | 
			
		||||
 | 
			
		||||
    One of these acts with a StretchableLayoutManager to resize the other components.
 | 
			
		||||
 | 
			
		||||
    @see StretchableLayoutManager
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  StretchableLayoutResizerBar  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a resizer bar for use on a specified layout.
 | 
			
		||||
 | 
			
		||||
        @param layoutToUse          the layout that will be affected when this bar
 | 
			
		||||
                                    is dragged
 | 
			
		||||
        @param itemIndexInLayout    the item index in the layout that corresponds to
 | 
			
		||||
                                    this bar component. You'll need to set up the item
 | 
			
		||||
                                    properties in a suitable way for a divider bar, e.g.
 | 
			
		||||
                                    for an 8-pixel wide bar which, you could call
 | 
			
		||||
                                    myLayout->setItemLayout (barIndex, 8, 8, 8)
 | 
			
		||||
        @param isBarVertical        true if it's an upright bar that you drag left and
 | 
			
		||||
                                    right; false for a horizontal one that you drag up and
 | 
			
		||||
                                    down
 | 
			
		||||
    */
 | 
			
		||||
    StretchableLayoutResizerBar (StretchableLayoutManager* layoutToUse,
 | 
			
		||||
                                 int itemIndexInLayout,
 | 
			
		||||
                                 bool isBarVertical);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~StretchableLayoutResizerBar() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This is called when the bar is dragged.
 | 
			
		||||
 | 
			
		||||
        This method must update the positions of any components whose position is
 | 
			
		||||
        determined by the StretchableLayoutManager, because they might have just
 | 
			
		||||
        moved.
 | 
			
		||||
 | 
			
		||||
        The default implementation calls the resized() method of this component's
 | 
			
		||||
        parent component, because that's often where you're likely to apply the
 | 
			
		||||
        layout, but it can be overridden for more specific needs.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void hasBeenMoved();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes. */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual void drawStretchableLayoutResizerBar (Graphics&, int w, int h,
 | 
			
		||||
                                                      bool isVerticalBar, bool isMouseOver, bool isMouseDragging) = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDrag (const MouseEvent&) override;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    StretchableLayoutManager* layout;
 | 
			
		||||
    int itemIndex, mouseDownPos;
 | 
			
		||||
    bool isVertical;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableLayoutResizerBar)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,122 +1,122 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
StretchableObjectResizer::StretchableObjectResizer() {}
 | 
			
		||||
StretchableObjectResizer::~StretchableObjectResizer() {}
 | 
			
		||||
 | 
			
		||||
void StretchableObjectResizer::addItem (const double size,
 | 
			
		||||
                                        const double minSize, const double maxSize,
 | 
			
		||||
                                        const int order)
 | 
			
		||||
{
 | 
			
		||||
    // the order must be >= 0 but less than the maximum integer value.
 | 
			
		||||
    jassert (order >= 0 && order < std::numeric_limits<int>::max());
 | 
			
		||||
    jassert (maxSize >= minSize);
 | 
			
		||||
 | 
			
		||||
    Item item;
 | 
			
		||||
    item.size = size;
 | 
			
		||||
    item.minSize = minSize;
 | 
			
		||||
    item.maxSize = maxSize;
 | 
			
		||||
    item.order = order;
 | 
			
		||||
    items.add (item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double StretchableObjectResizer::getItemSize (const int index) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return isPositiveAndBelow (index, items.size()) ? items.getReference (index).size
 | 
			
		||||
                                                    : 0.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableObjectResizer::resizeToFit (const double targetSize)
 | 
			
		||||
{
 | 
			
		||||
    int order = 0;
 | 
			
		||||
 | 
			
		||||
    for (;;)
 | 
			
		||||
    {
 | 
			
		||||
        double currentSize = 0;
 | 
			
		||||
        double minSize = 0;
 | 
			
		||||
        double maxSize = 0;
 | 
			
		||||
 | 
			
		||||
        int nextHighestOrder = std::numeric_limits<int>::max();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < items.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            const Item& it = items.getReference(i);
 | 
			
		||||
            currentSize += it.size;
 | 
			
		||||
 | 
			
		||||
            if (it.order <= order)
 | 
			
		||||
            {
 | 
			
		||||
                minSize += it.minSize;
 | 
			
		||||
                maxSize += it.maxSize;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                minSize += it.size;
 | 
			
		||||
                maxSize += it.size;
 | 
			
		||||
                nextHighestOrder = jmin (nextHighestOrder, it.order);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const double thisIterationTarget = jlimit (minSize, maxSize, targetSize);
 | 
			
		||||
 | 
			
		||||
        if (thisIterationTarget >= currentSize)
 | 
			
		||||
        {
 | 
			
		||||
            const double availableExtraSpace = maxSize - currentSize;
 | 
			
		||||
            const double targetAmountOfExtraSpace = thisIterationTarget - currentSize;
 | 
			
		||||
            const double scale = availableExtraSpace > 0 ? targetAmountOfExtraSpace / availableExtraSpace : 1.0;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < items.size(); ++i)
 | 
			
		||||
            {
 | 
			
		||||
                Item& it = items.getReference(i);
 | 
			
		||||
 | 
			
		||||
                if (it.order <= order)
 | 
			
		||||
                    it.size = jlimit (it.minSize, it.maxSize, it.size + (it.maxSize - it.size) * scale);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            const double amountOfSlack = currentSize - minSize;
 | 
			
		||||
            const double targetAmountOfSlack = thisIterationTarget - minSize;
 | 
			
		||||
            const double scale = targetAmountOfSlack / amountOfSlack;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < items.size(); ++i)
 | 
			
		||||
            {
 | 
			
		||||
                Item& it = items.getReference(i);
 | 
			
		||||
 | 
			
		||||
                if (it.order <= order)
 | 
			
		||||
                    it.size = jmax (it.minSize, it.minSize + (it.size - it.minSize) * scale);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (nextHighestOrder < std::numeric_limits<int>::max())
 | 
			
		||||
            order = nextHighestOrder;
 | 
			
		||||
        else
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
StretchableObjectResizer::StretchableObjectResizer() {}
 | 
			
		||||
StretchableObjectResizer::~StretchableObjectResizer() {}
 | 
			
		||||
 | 
			
		||||
void StretchableObjectResizer::addItem (const double size,
 | 
			
		||||
                                        const double minSize, const double maxSize,
 | 
			
		||||
                                        const int order)
 | 
			
		||||
{
 | 
			
		||||
    // the order must be >= 0 but less than the maximum integer value.
 | 
			
		||||
    jassert (order >= 0 && order < std::numeric_limits<int>::max());
 | 
			
		||||
    jassert (maxSize >= minSize);
 | 
			
		||||
 | 
			
		||||
    Item item;
 | 
			
		||||
    item.size = size;
 | 
			
		||||
    item.minSize = minSize;
 | 
			
		||||
    item.maxSize = maxSize;
 | 
			
		||||
    item.order = order;
 | 
			
		||||
    items.add (item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double StretchableObjectResizer::getItemSize (const int index) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return isPositiveAndBelow (index, items.size()) ? items.getReference (index).size
 | 
			
		||||
                                                    : 0.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StretchableObjectResizer::resizeToFit (const double targetSize)
 | 
			
		||||
{
 | 
			
		||||
    int order = 0;
 | 
			
		||||
 | 
			
		||||
    for (;;)
 | 
			
		||||
    {
 | 
			
		||||
        double currentSize = 0;
 | 
			
		||||
        double minSize = 0;
 | 
			
		||||
        double maxSize = 0;
 | 
			
		||||
 | 
			
		||||
        int nextHighestOrder = std::numeric_limits<int>::max();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < items.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            const Item& it = items.getReference(i);
 | 
			
		||||
            currentSize += it.size;
 | 
			
		||||
 | 
			
		||||
            if (it.order <= order)
 | 
			
		||||
            {
 | 
			
		||||
                minSize += it.minSize;
 | 
			
		||||
                maxSize += it.maxSize;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                minSize += it.size;
 | 
			
		||||
                maxSize += it.size;
 | 
			
		||||
                nextHighestOrder = jmin (nextHighestOrder, it.order);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const double thisIterationTarget = jlimit (minSize, maxSize, targetSize);
 | 
			
		||||
 | 
			
		||||
        if (thisIterationTarget >= currentSize)
 | 
			
		||||
        {
 | 
			
		||||
            const double availableExtraSpace = maxSize - currentSize;
 | 
			
		||||
            const double targetAmountOfExtraSpace = thisIterationTarget - currentSize;
 | 
			
		||||
            const double scale = availableExtraSpace > 0 ? targetAmountOfExtraSpace / availableExtraSpace : 1.0;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < items.size(); ++i)
 | 
			
		||||
            {
 | 
			
		||||
                Item& it = items.getReference(i);
 | 
			
		||||
 | 
			
		||||
                if (it.order <= order)
 | 
			
		||||
                    it.size = jlimit (it.minSize, it.maxSize, it.size + (it.maxSize - it.size) * scale);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            const double amountOfSlack = currentSize - minSize;
 | 
			
		||||
            const double targetAmountOfSlack = thisIterationTarget - minSize;
 | 
			
		||||
            const double scale = targetAmountOfSlack / amountOfSlack;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < items.size(); ++i)
 | 
			
		||||
            {
 | 
			
		||||
                Item& it = items.getReference(i);
 | 
			
		||||
 | 
			
		||||
                if (it.order <= order)
 | 
			
		||||
                    it.size = jmax (it.minSize, it.minSize + (it.size - it.minSize) * scale);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (nextHighestOrder < std::numeric_limits<int>::max())
 | 
			
		||||
            order = nextHighestOrder;
 | 
			
		||||
        else
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,102 +1,102 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A utility class for fitting a set of objects whose sizes can vary between
 | 
			
		||||
    a minimum and maximum size, into a space.
 | 
			
		||||
 | 
			
		||||
    This is a trickier algorithm than it would first seem, so I've put it in this
 | 
			
		||||
    class to allow it to be shared by various bits of code.
 | 
			
		||||
 | 
			
		||||
    To use it, create one of these objects, call addItem() to add the list of items
 | 
			
		||||
    you need, then call resizeToFit(), which will change all their sizes. You can
 | 
			
		||||
    then retrieve the new sizes with getItemSize() and getNumItems().
 | 
			
		||||
 | 
			
		||||
    It's currently used by the TableHeaderComponent for stretching out the table
 | 
			
		||||
    headings to fill the table's width.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class StretchableObjectResizer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty object resizer. */
 | 
			
		||||
    StretchableObjectResizer();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~StretchableObjectResizer();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Adds an item to the list.
 | 
			
		||||
 | 
			
		||||
        The order parameter lets you specify groups of items that are resized first when some
 | 
			
		||||
        space needs to be found. Those items with an order of 0 will be the first ones to be
 | 
			
		||||
        resized, and if that doesn't provide enough space to meet the requirements, the algorithm
 | 
			
		||||
        will then try resizing the items with an order of 1, then 2, and so on.
 | 
			
		||||
    */
 | 
			
		||||
    void addItem (double currentSize,
 | 
			
		||||
                  double minSize,
 | 
			
		||||
                  double maxSize,
 | 
			
		||||
                  int order = 0);
 | 
			
		||||
 | 
			
		||||
    /** Resizes all the items to fit this amount of space.
 | 
			
		||||
 | 
			
		||||
        This will attempt to fit them in without exceeding each item's minimum and
 | 
			
		||||
        maximum sizes. In cases where none of the items can be expanded or enlarged any
 | 
			
		||||
        further, the final size may be greater or less than the size passed in.
 | 
			
		||||
 | 
			
		||||
        After calling this method, you can retrieve the new sizes with the getItemSize()
 | 
			
		||||
        method.
 | 
			
		||||
    */
 | 
			
		||||
    void resizeToFit (double targetSize);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of items that have been added. */
 | 
			
		||||
    int getNumItems() const noexcept                        { return items.size(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the size of one of the items. */
 | 
			
		||||
    double getItemSize (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct Item
 | 
			
		||||
    {
 | 
			
		||||
        double size;
 | 
			
		||||
        double minSize;
 | 
			
		||||
        double maxSize;
 | 
			
		||||
        int order;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Array<Item> items;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableObjectResizer)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A utility class for fitting a set of objects whose sizes can vary between
 | 
			
		||||
    a minimum and maximum size, into a space.
 | 
			
		||||
 | 
			
		||||
    This is a trickier algorithm than it would first seem, so I've put it in this
 | 
			
		||||
    class to allow it to be shared by various bits of code.
 | 
			
		||||
 | 
			
		||||
    To use it, create one of these objects, call addItem() to add the list of items
 | 
			
		||||
    you need, then call resizeToFit(), which will change all their sizes. You can
 | 
			
		||||
    then retrieve the new sizes with getItemSize() and getNumItems().
 | 
			
		||||
 | 
			
		||||
    It's currently used by the TableHeaderComponent for stretching out the table
 | 
			
		||||
    headings to fill the table's width.
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class StretchableObjectResizer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an empty object resizer. */
 | 
			
		||||
    StretchableObjectResizer();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~StretchableObjectResizer();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Adds an item to the list.
 | 
			
		||||
 | 
			
		||||
        The order parameter lets you specify groups of items that are resized first when some
 | 
			
		||||
        space needs to be found. Those items with an order of 0 will be the first ones to be
 | 
			
		||||
        resized, and if that doesn't provide enough space to meet the requirements, the algorithm
 | 
			
		||||
        will then try resizing the items with an order of 1, then 2, and so on.
 | 
			
		||||
    */
 | 
			
		||||
    void addItem (double currentSize,
 | 
			
		||||
                  double minSize,
 | 
			
		||||
                  double maxSize,
 | 
			
		||||
                  int order = 0);
 | 
			
		||||
 | 
			
		||||
    /** Resizes all the items to fit this amount of space.
 | 
			
		||||
 | 
			
		||||
        This will attempt to fit them in without exceeding each item's minimum and
 | 
			
		||||
        maximum sizes. In cases where none of the items can be expanded or enlarged any
 | 
			
		||||
        further, the final size may be greater or less than the size passed in.
 | 
			
		||||
 | 
			
		||||
        After calling this method, you can retrieve the new sizes with the getItemSize()
 | 
			
		||||
        method.
 | 
			
		||||
    */
 | 
			
		||||
    void resizeToFit (double targetSize);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of items that have been added. */
 | 
			
		||||
    int getNumItems() const noexcept                        { return items.size(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the size of one of the items. */
 | 
			
		||||
    double getItemSize (int index) const noexcept;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    struct Item
 | 
			
		||||
    {
 | 
			
		||||
        double size;
 | 
			
		||||
        double minSize;
 | 
			
		||||
        double maxSize;
 | 
			
		||||
        int order;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Array<Item> items;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableObjectResizer)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,374 +1,372 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class TabbedButtonBar;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** In a TabbedButtonBar, this component is used for each of the buttons.
 | 
			
		||||
 | 
			
		||||
    If you want to create a TabbedButtonBar with custom tab components, derive
 | 
			
		||||
    your component from this class, and override the TabbedButtonBar::createTabButton()
 | 
			
		||||
    method to create it instead of the default one.
 | 
			
		||||
 | 
			
		||||
    @see TabbedButtonBar
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  TabBarButton  : public Button
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates the tab button. */
 | 
			
		||||
    TabBarButton (const String& name, TabbedButtonBar& ownerBar);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~TabBarButton() override;
 | 
			
		||||
 | 
			
		||||
    /** Returns the bar that contains this button. */
 | 
			
		||||
    TabbedButtonBar& getTabbedButtonBar() const   { return owner; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** When adding an extra component to the tab, this indicates which side of
 | 
			
		||||
        the text it should be placed on. */
 | 
			
		||||
    enum ExtraComponentPlacement
 | 
			
		||||
    {
 | 
			
		||||
        beforeText,
 | 
			
		||||
        afterText,
 | 
			
		||||
        aboveText,
 | 
			
		||||
        belowText
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Sets an extra component that will be shown in the tab.
 | 
			
		||||
 | 
			
		||||
        This optional component will be positioned inside the tab, either to the left or right
 | 
			
		||||
        of the text. You could use this to implement things like a close button or a graphical
 | 
			
		||||
        status indicator. If a non-null component is passed-in, the TabbedButtonBar will take
 | 
			
		||||
        ownership of it and delete it when required.
 | 
			
		||||
     */
 | 
			
		||||
    void setExtraComponent (Component* extraTabComponent,
 | 
			
		||||
                            ExtraComponentPlacement extraComponentPlacement);
 | 
			
		||||
 | 
			
		||||
    /** Returns the custom component, if there is one. */
 | 
			
		||||
    Component* getExtraComponent() const noexcept                           { return extraComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the placement of the custom component, if there is one. */
 | 
			
		||||
    ExtraComponentPlacement getExtraComponentPlacement() const noexcept     { return extraCompPlacement; }
 | 
			
		||||
 | 
			
		||||
    /** Returns an area of the component that's safe to draw in.
 | 
			
		||||
 | 
			
		||||
        This deals with the orientation of the tabs, which affects which side is
 | 
			
		||||
        touching the tabbed box's content component.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<int> getActiveArea() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the area of the component that should contain its text. */
 | 
			
		||||
    Rectangle<int> getTextArea() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns this tab's index in its tab bar. */
 | 
			
		||||
    int getIndex() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the colour of the tab. */
 | 
			
		||||
    Colour getTabBackgroundColour() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if this is the frontmost (selected) tab. */
 | 
			
		||||
    bool isFrontTab() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Chooses the best length for the tab, given the specified depth.
 | 
			
		||||
 | 
			
		||||
        If the tab is horizontal, this should return its width, and the depth
 | 
			
		||||
        specifies its height. If it's vertical, it should return the height, and
 | 
			
		||||
        the depth is actually its width.
 | 
			
		||||
    */
 | 
			
		||||
    virtual int getBestTabLength (int depth);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paintButton (Graphics&, bool, bool) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void clicked (const ModifierKeys&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool hitTest (int x, int y) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void childBoundsChanged (Component*) override;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    friend class TabbedButtonBar;
 | 
			
		||||
    TabbedButtonBar& owner;
 | 
			
		||||
    int overlapPixels = 0;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Component> extraComponent;
 | 
			
		||||
    ExtraComponentPlacement extraCompPlacement = afterText;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    using Button::clicked;
 | 
			
		||||
    void calcAreas (Rectangle<int>&, Rectangle<int>&) const;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabBarButton)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A vertical or horizontal bar containing tabs that you can select.
 | 
			
		||||
 | 
			
		||||
    You can use one of these to generate things like a dialog box that has
 | 
			
		||||
    tabbed pages you can flip between. Attach a ChangeListener to the
 | 
			
		||||
    button bar to be told when the user changes the page.
 | 
			
		||||
 | 
			
		||||
    An easier method than doing this is to use a TabbedComponent, which
 | 
			
		||||
    contains its own TabbedButtonBar and which takes care of the layout
 | 
			
		||||
    and other housekeeping.
 | 
			
		||||
 | 
			
		||||
    @see TabbedComponent
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  TabbedButtonBar  : public Component,
 | 
			
		||||
                                   public ChangeBroadcaster
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The placement of the tab-bar
 | 
			
		||||
        @see setOrientation, getOrientation
 | 
			
		||||
    */
 | 
			
		||||
    enum Orientation
 | 
			
		||||
    {
 | 
			
		||||
        TabsAtTop,
 | 
			
		||||
        TabsAtBottom,
 | 
			
		||||
        TabsAtLeft,
 | 
			
		||||
        TabsAtRight
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a TabbedButtonBar with a given orientation.
 | 
			
		||||
        You can change the orientation later if you need to.
 | 
			
		||||
    */
 | 
			
		||||
    TabbedButtonBar (Orientation orientation);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~TabbedButtonBar() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the bar's orientation.
 | 
			
		||||
 | 
			
		||||
        This won't change the bar's actual size - you'll need to do that yourself,
 | 
			
		||||
        but this determines which direction the tabs go in, and which side they're
 | 
			
		||||
        stuck to.
 | 
			
		||||
    */
 | 
			
		||||
    void setOrientation (Orientation orientation);
 | 
			
		||||
 | 
			
		||||
    /** Returns the bar's current orientation.
 | 
			
		||||
        @see setOrientation
 | 
			
		||||
    */
 | 
			
		||||
    Orientation getOrientation() const noexcept         { return orientation; }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the orientation is TabsAtLeft or TabsAtRight. */
 | 
			
		||||
    bool isVertical() const noexcept                    { return orientation == TabsAtLeft || orientation == TabsAtRight; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the thickness of the bar, which may be its width or height, depending on the orientation. */
 | 
			
		||||
    int getThickness() const noexcept                   { return isVertical() ? getWidth() : getHeight(); }
 | 
			
		||||
 | 
			
		||||
    /** Changes the minimum scale factor to which the tabs can be compressed when trying to
 | 
			
		||||
        fit a lot of tabs on-screen.
 | 
			
		||||
    */
 | 
			
		||||
    void setMinimumTabScaleFactor (double newMinimumScale);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Deletes all the tabs from the bar.
 | 
			
		||||
        @see addTab
 | 
			
		||||
    */
 | 
			
		||||
    void clearTabs();
 | 
			
		||||
 | 
			
		||||
    /** Adds a tab to the bar.
 | 
			
		||||
        Tabs are added in left-to-right reading order.
 | 
			
		||||
        If this is the first tab added, it'll also be automatically selected.
 | 
			
		||||
    */
 | 
			
		||||
    void addTab (const String& tabName,
 | 
			
		||||
                 Colour tabBackgroundColour,
 | 
			
		||||
                 int insertIndex);
 | 
			
		||||
 | 
			
		||||
    /** Changes the name of one of the tabs. */
 | 
			
		||||
    void setTabName (int tabIndex, const String& newName);
 | 
			
		||||
 | 
			
		||||
    /** Gets rid of one of the tabs. */
 | 
			
		||||
    void removeTab (int tabIndex, bool animate = false);
 | 
			
		||||
 | 
			
		||||
    /** Moves a tab to a new index in the list.
 | 
			
		||||
        Pass -1 as the index to move it to the end of the list.
 | 
			
		||||
    */
 | 
			
		||||
    void moveTab (int currentIndex, int newIndex, bool animate = false);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of tabs in the bar. */
 | 
			
		||||
    int getNumTabs() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns a list of all the tab names in the bar. */
 | 
			
		||||
    StringArray getTabNames() const;
 | 
			
		||||
 | 
			
		||||
    /** Changes the currently selected tab.
 | 
			
		||||
        This will send a change message and cause a synchronous callback to
 | 
			
		||||
        the currentTabChanged() method. (But if the given tab is already selected,
 | 
			
		||||
        nothing will be done).
 | 
			
		||||
 | 
			
		||||
        To deselect all the tabs, use an index of -1.
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentTabIndex (int newTabIndex, bool sendChangeMessage = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the name of the currently selected tab.
 | 
			
		||||
        This could be an empty string if none are selected.
 | 
			
		||||
    */
 | 
			
		||||
    String getCurrentTabName() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the index of the currently selected tab.
 | 
			
		||||
        This could return -1 if none are selected.
 | 
			
		||||
    */
 | 
			
		||||
    int getCurrentTabIndex() const noexcept             { return currentTabIndex; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the button for a specific tab.
 | 
			
		||||
        The button that is returned may be deleted later by this component, so don't hang
 | 
			
		||||
        on to the pointer that is returned. A null pointer may be returned if the index is
 | 
			
		||||
        out of range.
 | 
			
		||||
    */
 | 
			
		||||
    TabBarButton* getTabButton (int index) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the index of a TabBarButton if it belongs to this bar. */
 | 
			
		||||
    int indexOfTabButton (const TabBarButton* button) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the final bounds of this button if it is currently being animated. */
 | 
			
		||||
    Rectangle<int> getTargetBounds (TabBarButton* button) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Callback method to indicate the selected tab has been changed.
 | 
			
		||||
        @see setCurrentTabIndex
 | 
			
		||||
    */
 | 
			
		||||
    virtual void currentTabChanged (int newCurrentTabIndex,
 | 
			
		||||
                                    const String& newCurrentTabName);
 | 
			
		||||
 | 
			
		||||
    /** Callback method to indicate that the user has right-clicked on a tab. */
 | 
			
		||||
    virtual void popupMenuClickOnTab (int tabIndex, const String& tabName);
 | 
			
		||||
 | 
			
		||||
    /** Returns the colour of a tab.
 | 
			
		||||
        This is the colour that was specified in addTab().
 | 
			
		||||
    */
 | 
			
		||||
    Colour getTabBackgroundColour (int tabIndex);
 | 
			
		||||
 | 
			
		||||
    /** Changes the background colour of a tab.
 | 
			
		||||
        @see addTab, getTabBackgroundColour
 | 
			
		||||
    */
 | 
			
		||||
    void setTabBackgroundColour (int tabIndex, Colour newColour);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the component.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        tabOutlineColourId              = 0x1005812,    /**< The colour to use to draw an outline around the tabs.  */
 | 
			
		||||
        tabTextColourId                 = 0x1005813,    /**< The colour to use to draw the tab names. If this isn't specified,
 | 
			
		||||
                                                             the look and feel will choose an appropriate colour. */
 | 
			
		||||
        frontOutlineColourId            = 0x1005814,    /**< The colour to use to draw an outline around the currently-selected tab.  */
 | 
			
		||||
        frontTextColourId               = 0x1005815,    /**< The colour to use to draw the currently-selected tab name. If
 | 
			
		||||
                                                             this isn't specified, the look and feel will choose an appropriate
 | 
			
		||||
                                                             colour. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes to provide
 | 
			
		||||
        window drawing functionality.
 | 
			
		||||
    */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual int getTabButtonSpaceAroundImage() = 0;
 | 
			
		||||
        virtual int getTabButtonOverlap (int tabDepth) = 0;
 | 
			
		||||
        virtual int getTabButtonBestWidth (TabBarButton&, int tabDepth) = 0;
 | 
			
		||||
        virtual Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle<int>& textArea, Component& extraComp) = 0;
 | 
			
		||||
 | 
			
		||||
        virtual void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
 | 
			
		||||
        virtual Font getTabButtonFont (TabBarButton&, float height) = 0;
 | 
			
		||||
        virtual void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
 | 
			
		||||
        virtual void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) = 0;
 | 
			
		||||
        virtual void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h) = 0;
 | 
			
		||||
 | 
			
		||||
        virtual void createTabButtonShape (TabBarButton&, Path& path,  bool isMouseOver, bool isMouseDown) = 0;
 | 
			
		||||
        virtual void fillTabButtonShape (TabBarButton&, Graphics&, const Path& path, bool isMouseOver, bool isMouseDown) = 0;
 | 
			
		||||
 | 
			
		||||
        virtual Button* createTabBarExtrasButton() = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This creates one of the tabs.
 | 
			
		||||
 | 
			
		||||
        If you need to use custom tab components, you can override this method and
 | 
			
		||||
        return your own class instead of the default.
 | 
			
		||||
    */
 | 
			
		||||
    virtual TabBarButton* createTabButton (const String& tabName, int tabIndex);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct TabInfo
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<TabBarButton> button;
 | 
			
		||||
        String name;
 | 
			
		||||
        Colour colour;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<TabInfo> tabs;
 | 
			
		||||
 | 
			
		||||
    Orientation orientation;
 | 
			
		||||
    double minimumScale = 0.7;
 | 
			
		||||
    int currentTabIndex = -1;
 | 
			
		||||
 | 
			
		||||
    class BehindFrontTabComp;
 | 
			
		||||
    std::unique_ptr<BehindFrontTabComp> behindFrontTab;
 | 
			
		||||
    std::unique_ptr<Button> extraTabsButton;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void showExtraItemsMenu();
 | 
			
		||||
    void updateTabPositions (bool animate);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedButtonBar)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class TabbedButtonBar;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** In a TabbedButtonBar, this component is used for each of the buttons.
 | 
			
		||||
 | 
			
		||||
    If you want to create a TabbedButtonBar with custom tab components, derive
 | 
			
		||||
    your component from this class, and override the TabbedButtonBar::createTabButton()
 | 
			
		||||
    method to create it instead of the default one.
 | 
			
		||||
 | 
			
		||||
    @see TabbedButtonBar
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  TabBarButton  : public Button
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates the tab button. */
 | 
			
		||||
    TabBarButton (const String& name, TabbedButtonBar& ownerBar);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~TabBarButton() override;
 | 
			
		||||
 | 
			
		||||
    /** Returns the bar that contains this button. */
 | 
			
		||||
    TabbedButtonBar& getTabbedButtonBar() const   { return owner; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** When adding an extra component to the tab, this indicates which side of
 | 
			
		||||
        the text it should be placed on. */
 | 
			
		||||
    enum ExtraComponentPlacement
 | 
			
		||||
    {
 | 
			
		||||
        beforeText,
 | 
			
		||||
        afterText
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Sets an extra component that will be shown in the tab.
 | 
			
		||||
 | 
			
		||||
        This optional component will be positioned inside the tab, either to the left or right
 | 
			
		||||
        of the text. You could use this to implement things like a close button or a graphical
 | 
			
		||||
        status indicator. If a non-null component is passed-in, the TabbedButtonBar will take
 | 
			
		||||
        ownership of it and delete it when required.
 | 
			
		||||
     */
 | 
			
		||||
    void setExtraComponent (Component* extraTabComponent,
 | 
			
		||||
                            ExtraComponentPlacement extraComponentPlacement);
 | 
			
		||||
 | 
			
		||||
    /** Returns the custom component, if there is one. */
 | 
			
		||||
    Component* getExtraComponent() const noexcept                           { return extraComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the placement of the custom component, if there is one. */
 | 
			
		||||
    ExtraComponentPlacement getExtraComponentPlacement() const noexcept     { return extraCompPlacement; }
 | 
			
		||||
 | 
			
		||||
    /** Returns an area of the component that's safe to draw in.
 | 
			
		||||
 | 
			
		||||
        This deals with the orientation of the tabs, which affects which side is
 | 
			
		||||
        touching the tabbed box's content component.
 | 
			
		||||
    */
 | 
			
		||||
    Rectangle<int> getActiveArea() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the area of the component that should contain its text. */
 | 
			
		||||
    Rectangle<int> getTextArea() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns this tab's index in its tab bar. */
 | 
			
		||||
    int getIndex() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the colour of the tab. */
 | 
			
		||||
    Colour getTabBackgroundColour() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if this is the frontmost (selected) tab. */
 | 
			
		||||
    bool isFrontTab() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Chooses the best length for the tab, given the specified depth.
 | 
			
		||||
 | 
			
		||||
        If the tab is horizontal, this should return its width, and the depth
 | 
			
		||||
        specifies its height. If it's vertical, it should return the height, and
 | 
			
		||||
        the depth is actually its width.
 | 
			
		||||
    */
 | 
			
		||||
    virtual int getBestTabLength (int depth);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paintButton (Graphics&, bool, bool) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void clicked (const ModifierKeys&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool hitTest (int x, int y) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void childBoundsChanged (Component*) override;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    friend class TabbedButtonBar;
 | 
			
		||||
    TabbedButtonBar& owner;
 | 
			
		||||
    int overlapPixels = 0;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Component> extraComponent;
 | 
			
		||||
    ExtraComponentPlacement extraCompPlacement = afterText;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    using Button::clicked;
 | 
			
		||||
    void calcAreas (Rectangle<int>&, Rectangle<int>&) const;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabBarButton)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A vertical or horizontal bar containing tabs that you can select.
 | 
			
		||||
 | 
			
		||||
    You can use one of these to generate things like a dialog box that has
 | 
			
		||||
    tabbed pages you can flip between. Attach a ChangeListener to the
 | 
			
		||||
    button bar to be told when the user changes the page.
 | 
			
		||||
 | 
			
		||||
    An easier method than doing this is to use a TabbedComponent, which
 | 
			
		||||
    contains its own TabbedButtonBar and which takes care of the layout
 | 
			
		||||
    and other housekeeping.
 | 
			
		||||
 | 
			
		||||
    @see TabbedComponent
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  TabbedButtonBar  : public Component,
 | 
			
		||||
                                   public ChangeBroadcaster
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The placement of the tab-bar
 | 
			
		||||
        @see setOrientation, getOrientation
 | 
			
		||||
    */
 | 
			
		||||
    enum Orientation
 | 
			
		||||
    {
 | 
			
		||||
        TabsAtTop,
 | 
			
		||||
        TabsAtBottom,
 | 
			
		||||
        TabsAtLeft,
 | 
			
		||||
        TabsAtRight
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a TabbedButtonBar with a given orientation.
 | 
			
		||||
        You can change the orientation later if you need to.
 | 
			
		||||
    */
 | 
			
		||||
    TabbedButtonBar (Orientation orientation);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~TabbedButtonBar() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the bar's orientation.
 | 
			
		||||
 | 
			
		||||
        This won't change the bar's actual size - you'll need to do that yourself,
 | 
			
		||||
        but this determines which direction the tabs go in, and which side they're
 | 
			
		||||
        stuck to.
 | 
			
		||||
    */
 | 
			
		||||
    void setOrientation (Orientation orientation);
 | 
			
		||||
 | 
			
		||||
    /** Returns the bar's current orientation.
 | 
			
		||||
        @see setOrientation
 | 
			
		||||
    */
 | 
			
		||||
    Orientation getOrientation() const noexcept         { return orientation; }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the orientation is TabsAtLeft or TabsAtRight. */
 | 
			
		||||
    bool isVertical() const noexcept                    { return orientation == TabsAtLeft || orientation == TabsAtRight; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the thickness of the bar, which may be its width or height, depending on the orientation. */
 | 
			
		||||
    int getThickness() const noexcept                   { return isVertical() ? getWidth() : getHeight(); }
 | 
			
		||||
 | 
			
		||||
    /** Changes the minimum scale factor to which the tabs can be compressed when trying to
 | 
			
		||||
        fit a lot of tabs on-screen.
 | 
			
		||||
    */
 | 
			
		||||
    void setMinimumTabScaleFactor (double newMinimumScale);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Deletes all the tabs from the bar.
 | 
			
		||||
        @see addTab
 | 
			
		||||
    */
 | 
			
		||||
    void clearTabs();
 | 
			
		||||
 | 
			
		||||
    /** Adds a tab to the bar.
 | 
			
		||||
        Tabs are added in left-to-right reading order.
 | 
			
		||||
        If this is the first tab added, it'll also be automatically selected.
 | 
			
		||||
    */
 | 
			
		||||
    void addTab (const String& tabName,
 | 
			
		||||
                 Colour tabBackgroundColour,
 | 
			
		||||
                 int insertIndex);
 | 
			
		||||
 | 
			
		||||
    /** Changes the name of one of the tabs. */
 | 
			
		||||
    void setTabName (int tabIndex, const String& newName);
 | 
			
		||||
 | 
			
		||||
    /** Gets rid of one of the tabs. */
 | 
			
		||||
    void removeTab (int tabIndex, bool animate = false);
 | 
			
		||||
 | 
			
		||||
    /** Moves a tab to a new index in the list.
 | 
			
		||||
        Pass -1 as the index to move it to the end of the list.
 | 
			
		||||
    */
 | 
			
		||||
    void moveTab (int currentIndex, int newIndex, bool animate = false);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of tabs in the bar. */
 | 
			
		||||
    int getNumTabs() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns a list of all the tab names in the bar. */
 | 
			
		||||
    StringArray getTabNames() const;
 | 
			
		||||
 | 
			
		||||
    /** Changes the currently selected tab.
 | 
			
		||||
        This will send a change message and cause a synchronous callback to
 | 
			
		||||
        the currentTabChanged() method. (But if the given tab is already selected,
 | 
			
		||||
        nothing will be done).
 | 
			
		||||
 | 
			
		||||
        To deselect all the tabs, use an index of -1.
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentTabIndex (int newTabIndex, bool sendChangeMessage = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the name of the currently selected tab.
 | 
			
		||||
        This could be an empty string if none are selected.
 | 
			
		||||
    */
 | 
			
		||||
    String getCurrentTabName() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the index of the currently selected tab.
 | 
			
		||||
        This could return -1 if none are selected.
 | 
			
		||||
    */
 | 
			
		||||
    int getCurrentTabIndex() const noexcept             { return currentTabIndex; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the button for a specific tab.
 | 
			
		||||
        The button that is returned may be deleted later by this component, so don't hang
 | 
			
		||||
        on to the pointer that is returned. A null pointer may be returned if the index is
 | 
			
		||||
        out of range.
 | 
			
		||||
    */
 | 
			
		||||
    TabBarButton* getTabButton (int index) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the index of a TabBarButton if it belongs to this bar. */
 | 
			
		||||
    int indexOfTabButton (const TabBarButton* button) const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the final bounds of this button if it is currently being animated. */
 | 
			
		||||
    Rectangle<int> getTargetBounds (TabBarButton* button) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Callback method to indicate the selected tab has been changed.
 | 
			
		||||
        @see setCurrentTabIndex
 | 
			
		||||
    */
 | 
			
		||||
    virtual void currentTabChanged (int newCurrentTabIndex,
 | 
			
		||||
                                    const String& newCurrentTabName);
 | 
			
		||||
 | 
			
		||||
    /** Callback method to indicate that the user has right-clicked on a tab. */
 | 
			
		||||
    virtual void popupMenuClickOnTab (int tabIndex, const String& tabName);
 | 
			
		||||
 | 
			
		||||
    /** Returns the colour of a tab.
 | 
			
		||||
        This is the colour that was specified in addTab().
 | 
			
		||||
    */
 | 
			
		||||
    Colour getTabBackgroundColour (int tabIndex);
 | 
			
		||||
 | 
			
		||||
    /** Changes the background colour of a tab.
 | 
			
		||||
        @see addTab, getTabBackgroundColour
 | 
			
		||||
    */
 | 
			
		||||
    void setTabBackgroundColour (int tabIndex, Colour newColour);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the component.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        tabOutlineColourId              = 0x1005812,    /**< The colour to use to draw an outline around the tabs.  */
 | 
			
		||||
        tabTextColourId                 = 0x1005813,    /**< The colour to use to draw the tab names. If this isn't specified,
 | 
			
		||||
                                                             the look and feel will choose an appropriate colour. */
 | 
			
		||||
        frontOutlineColourId            = 0x1005814,    /**< The colour to use to draw an outline around the currently-selected tab.  */
 | 
			
		||||
        frontTextColourId               = 0x1005815,    /**< The colour to use to draw the currently-selected tab name. If
 | 
			
		||||
                                                             this isn't specified, the look and feel will choose an appropriate
 | 
			
		||||
                                                             colour. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This abstract base class is implemented by LookAndFeel classes to provide
 | 
			
		||||
        window drawing functionality.
 | 
			
		||||
    */
 | 
			
		||||
    struct JUCE_API  LookAndFeelMethods
 | 
			
		||||
    {
 | 
			
		||||
        virtual ~LookAndFeelMethods() = default;
 | 
			
		||||
 | 
			
		||||
        virtual int getTabButtonSpaceAroundImage() = 0;
 | 
			
		||||
        virtual int getTabButtonOverlap (int tabDepth) = 0;
 | 
			
		||||
        virtual int getTabButtonBestWidth (TabBarButton&, int tabDepth) = 0;
 | 
			
		||||
        virtual Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle<int>& textArea, Component& extraComp) = 0;
 | 
			
		||||
 | 
			
		||||
        virtual void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
 | 
			
		||||
        virtual Font getTabButtonFont (TabBarButton&, float height) = 0;
 | 
			
		||||
        virtual void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
 | 
			
		||||
        virtual void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) = 0;
 | 
			
		||||
        virtual void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h) = 0;
 | 
			
		||||
 | 
			
		||||
        virtual void createTabButtonShape (TabBarButton&, Path& path,  bool isMouseOver, bool isMouseDown) = 0;
 | 
			
		||||
        virtual void fillTabButtonShape (TabBarButton&, Graphics&, const Path& path, bool isMouseOver, bool isMouseDown) = 0;
 | 
			
		||||
 | 
			
		||||
        virtual Button* createTabBarExtrasButton() = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This creates one of the tabs.
 | 
			
		||||
 | 
			
		||||
        If you need to use custom tab components, you can override this method and
 | 
			
		||||
        return your own class instead of the default.
 | 
			
		||||
    */
 | 
			
		||||
    virtual TabBarButton* createTabButton (const String& tabName, int tabIndex);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct TabInfo
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<TabBarButton> button;
 | 
			
		||||
        String name;
 | 
			
		||||
        Colour colour;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<TabInfo> tabs;
 | 
			
		||||
 | 
			
		||||
    Orientation orientation;
 | 
			
		||||
    double minimumScale = 0.7;
 | 
			
		||||
    int currentTabIndex = -1;
 | 
			
		||||
 | 
			
		||||
    class BehindFrontTabComp;
 | 
			
		||||
    std::unique_ptr<BehindFrontTabComp> behindFrontTab;
 | 
			
		||||
    std::unique_ptr<Button> extraTabsButton;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void showExtraItemsMenu();
 | 
			
		||||
    void updateTabPositions (bool animate);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedButtonBar)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,324 +1,319 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace TabbedComponentHelpers
 | 
			
		||||
{
 | 
			
		||||
    const Identifier deleteComponentId ("deleteByTabComp_");
 | 
			
		||||
 | 
			
		||||
    static void deleteIfNecessary (Component* comp)
 | 
			
		||||
    {
 | 
			
		||||
        if (comp != nullptr && (bool) comp->getProperties() [deleteComponentId])
 | 
			
		||||
            delete comp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Rectangle<int> getTabArea (Rectangle<int>& content, BorderSize<int>& outline,
 | 
			
		||||
                                      TabbedButtonBar::Orientation orientation, int tabDepth)
 | 
			
		||||
    {
 | 
			
		||||
        switch (orientation)
 | 
			
		||||
        {
 | 
			
		||||
            case TabbedButtonBar::TabsAtTop:    outline.setTop (0);     return content.removeFromTop (tabDepth);
 | 
			
		||||
            case TabbedButtonBar::TabsAtBottom: outline.setBottom (0);  return content.removeFromBottom (tabDepth);
 | 
			
		||||
            case TabbedButtonBar::TabsAtLeft:   outline.setLeft (0);    return content.removeFromLeft (tabDepth);
 | 
			
		||||
            case TabbedButtonBar::TabsAtRight:  outline.setRight (0);   return content.removeFromRight (tabDepth);
 | 
			
		||||
            default: jassertfalse; break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Rectangle<int>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct TabbedComponent::ButtonBar  : public TabbedButtonBar
 | 
			
		||||
{
 | 
			
		||||
    ButtonBar (TabbedComponent& tabComp, TabbedButtonBar::Orientation o)
 | 
			
		||||
        : TabbedButtonBar (o), owner (tabComp)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void currentTabChanged (int newCurrentTabIndex, const String& newTabName)
 | 
			
		||||
    {
 | 
			
		||||
        owner.changeCallback (newCurrentTabIndex, newTabName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void popupMenuClickOnTab (int tabIndex, const String& tabName)
 | 
			
		||||
    {
 | 
			
		||||
        owner.popupMenuClickOnTab (tabIndex, tabName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Colour getTabBackgroundColour (int tabIndex)
 | 
			
		||||
    {
 | 
			
		||||
        return owner.tabs->getTabBackgroundColour (tabIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TabBarButton* createTabButton (const String& tabName, int tabIndex)
 | 
			
		||||
    {
 | 
			
		||||
        return owner.createTabButton (tabName, tabIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TabbedComponent& owner;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonBar)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
TabbedComponent::TabbedComponent (TabbedButtonBar::Orientation orientation)
 | 
			
		||||
{
 | 
			
		||||
    tabs.reset (new ButtonBar (*this, orientation));
 | 
			
		||||
    addAndMakeVisible (tabs.get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TabbedComponent::~TabbedComponent()
 | 
			
		||||
{
 | 
			
		||||
    clearTabs();
 | 
			
		||||
    tabs.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void TabbedComponent::setOrientation (TabbedButtonBar::Orientation orientation)
 | 
			
		||||
{
 | 
			
		||||
    tabs->setOrientation (orientation);
 | 
			
		||||
    resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TabbedButtonBar::Orientation TabbedComponent::getOrientation() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getOrientation();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setTabBarDepth (int newDepth)
 | 
			
		||||
{
 | 
			
		||||
    if (tabDepth != newDepth)
 | 
			
		||||
    {
 | 
			
		||||
        tabDepth = newDepth;
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TabBarButton* TabbedComponent::createTabButton (const String& tabName, int /*tabIndex*/)
 | 
			
		||||
{
 | 
			
		||||
    return new TabBarButton (tabName, *tabs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void TabbedComponent::clearTabs()
 | 
			
		||||
{
 | 
			
		||||
    if (panelComponent != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        panelComponent->setVisible (false);
 | 
			
		||||
        removeChildComponent (panelComponent.get());
 | 
			
		||||
        panelComponent = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tabs->clearTabs();
 | 
			
		||||
 | 
			
		||||
    for (int i = contentComponents.size(); --i >= 0;)
 | 
			
		||||
        TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (i));
 | 
			
		||||
 | 
			
		||||
    contentComponents.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::addTab (const String& tabName,
 | 
			
		||||
                              Colour tabBackgroundColour,
 | 
			
		||||
                              Component* contentComponent,
 | 
			
		||||
                              bool deleteComponentWhenNotNeeded,
 | 
			
		||||
                              int insertIndex)
 | 
			
		||||
{
 | 
			
		||||
    contentComponents.insert (insertIndex, WeakReference<Component> (contentComponent));
 | 
			
		||||
 | 
			
		||||
    if (deleteComponentWhenNotNeeded && contentComponent != nullptr)
 | 
			
		||||
        contentComponent->getProperties().set (TabbedComponentHelpers::deleteComponentId, true);
 | 
			
		||||
 | 
			
		||||
    tabs->addTab (tabName, tabBackgroundColour, insertIndex);
 | 
			
		||||
    resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setTabDeleteComponentWhenNotNeeded (int tabIndex, bool flag)
 | 
			
		||||
{
 | 
			
		||||
    contentComponents [tabIndex]->getProperties().set (TabbedComponentHelpers::deleteComponentId, flag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setTabName (int tabIndex, const String& newName)
 | 
			
		||||
{
 | 
			
		||||
    tabs->setTabName (tabIndex, newName);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::removeTab (int tabIndex)
 | 
			
		||||
{
 | 
			
		||||
    if (isPositiveAndBelow (tabIndex, contentComponents.size()))
 | 
			
		||||
    {
 | 
			
		||||
        TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (tabIndex).get());
 | 
			
		||||
        contentComponents.remove (tabIndex);
 | 
			
		||||
        tabs->removeTab (tabIndex);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::moveTab (int currentIndex, int newIndex, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    contentComponents.move (currentIndex, newIndex);
 | 
			
		||||
    tabs->moveTab (currentIndex, newIndex, animate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int TabbedComponent::getNumTabs() const
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getNumTabs();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StringArray TabbedComponent::getTabNames() const
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getTabNames();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* TabbedComponent::getTabContentComponent (int tabIndex) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return contentComponents[tabIndex].get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Colour TabbedComponent::getTabBackgroundColour (int tabIndex) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getTabBackgroundColour (tabIndex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setTabBackgroundColour (int tabIndex, Colour newColour)
 | 
			
		||||
{
 | 
			
		||||
    tabs->setTabBackgroundColour (tabIndex, newColour);
 | 
			
		||||
 | 
			
		||||
    if (getCurrentTabIndex() == tabIndex)
 | 
			
		||||
        repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setCurrentTabIndex (int newTabIndex, bool sendChangeMessage)
 | 
			
		||||
{
 | 
			
		||||
    tabs->setCurrentTabIndex (newTabIndex, sendChangeMessage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int TabbedComponent::getCurrentTabIndex() const
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getCurrentTabIndex();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String TabbedComponent::getCurrentTabName() const
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getCurrentTabName();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setOutline (int thickness)
 | 
			
		||||
{
 | 
			
		||||
    outlineThickness = thickness;
 | 
			
		||||
    resized();
 | 
			
		||||
    repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setIndent (int indentThickness)
 | 
			
		||||
{
 | 
			
		||||
    edgeIndent = indentThickness;
 | 
			
		||||
    resized();
 | 
			
		||||
    repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    g.fillAll (findColour (backgroundColourId));
 | 
			
		||||
 | 
			
		||||
    auto content = getLocalBounds();
 | 
			
		||||
    BorderSize<int> outline (outlineThickness);
 | 
			
		||||
    TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth);
 | 
			
		||||
 | 
			
		||||
    g.reduceClipRegion (content);
 | 
			
		||||
    g.fillAll (tabs->getTabBackgroundColour (getCurrentTabIndex()));
 | 
			
		||||
 | 
			
		||||
    if (outlineThickness > 0)
 | 
			
		||||
    {
 | 
			
		||||
        RectangleList<int> rl (content);
 | 
			
		||||
        rl.subtract (outline.subtractedFrom (content));
 | 
			
		||||
 | 
			
		||||
        g.reduceClipRegion (rl);
 | 
			
		||||
        g.fillAll (findColour (outlineColourId));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::resized()
 | 
			
		||||
{
 | 
			
		||||
    auto content = getLocalBounds();
 | 
			
		||||
    BorderSize<int> outline (outlineThickness);
 | 
			
		||||
 | 
			
		||||
    tabs->setBounds (TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth));
 | 
			
		||||
    content = BorderSize<int> (edgeIndent).subtractedFrom (outline.subtractedFrom (content));
 | 
			
		||||
 | 
			
		||||
    for (auto& c : contentComponents)
 | 
			
		||||
        if (auto comp = c.get())
 | 
			
		||||
            comp->setBounds (content);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::lookAndFeelChanged()
 | 
			
		||||
{
 | 
			
		||||
    for (auto& c : contentComponents)
 | 
			
		||||
        if (auto comp = c.get())
 | 
			
		||||
          comp->lookAndFeelChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::changeCallback (int newCurrentTabIndex, const String& newTabName)
 | 
			
		||||
{
 | 
			
		||||
    auto* newPanelComp = getTabContentComponent (getCurrentTabIndex());
 | 
			
		||||
 | 
			
		||||
    if (newPanelComp != panelComponent)
 | 
			
		||||
    {
 | 
			
		||||
        if (panelComponent != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            panelComponent->setVisible (false);
 | 
			
		||||
            removeChildComponent (panelComponent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        panelComponent = newPanelComp;
 | 
			
		||||
 | 
			
		||||
        if (panelComponent != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            // do these ops as two stages instead of addAndMakeVisible() so that the
 | 
			
		||||
            // component has always got a parent when it gets the visibilityChanged() callback
 | 
			
		||||
            addChildComponent (panelComponent);
 | 
			
		||||
            panelComponent->sendLookAndFeelChange();
 | 
			
		||||
            panelComponent->setVisible (true);
 | 
			
		||||
            panelComponent->toFront (true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resized();
 | 
			
		||||
    currentTabChanged (newCurrentTabIndex, newTabName);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::currentTabChanged (int, const String&) {}
 | 
			
		||||
void TabbedComponent::popupMenuClickOnTab (int, const String&) {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> TabbedComponent::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace TabbedComponentHelpers
 | 
			
		||||
{
 | 
			
		||||
    const Identifier deleteComponentId ("deleteByTabComp_");
 | 
			
		||||
 | 
			
		||||
    static void deleteIfNecessary (Component* comp)
 | 
			
		||||
    {
 | 
			
		||||
        if (comp != nullptr && (bool) comp->getProperties() [deleteComponentId])
 | 
			
		||||
            delete comp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Rectangle<int> getTabArea (Rectangle<int>& content, BorderSize<int>& outline,
 | 
			
		||||
                                      TabbedButtonBar::Orientation orientation, int tabDepth)
 | 
			
		||||
    {
 | 
			
		||||
        switch (orientation)
 | 
			
		||||
        {
 | 
			
		||||
            case TabbedButtonBar::TabsAtTop:    outline.setTop (0);     return content.removeFromTop (tabDepth);
 | 
			
		||||
            case TabbedButtonBar::TabsAtBottom: outline.setBottom (0);  return content.removeFromBottom (tabDepth);
 | 
			
		||||
            case TabbedButtonBar::TabsAtLeft:   outline.setLeft (0);    return content.removeFromLeft (tabDepth);
 | 
			
		||||
            case TabbedButtonBar::TabsAtRight:  outline.setRight (0);   return content.removeFromRight (tabDepth);
 | 
			
		||||
            default: jassertfalse; break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Rectangle<int>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct TabbedComponent::ButtonBar  : public TabbedButtonBar
 | 
			
		||||
{
 | 
			
		||||
    ButtonBar (TabbedComponent& tabComp, TabbedButtonBar::Orientation o)
 | 
			
		||||
        : TabbedButtonBar (o), owner (tabComp)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void currentTabChanged (int newCurrentTabIndex, const String& newTabName)
 | 
			
		||||
    {
 | 
			
		||||
        owner.changeCallback (newCurrentTabIndex, newTabName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void popupMenuClickOnTab (int tabIndex, const String& tabName)
 | 
			
		||||
    {
 | 
			
		||||
        owner.popupMenuClickOnTab (tabIndex, tabName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Colour getTabBackgroundColour (int tabIndex)
 | 
			
		||||
    {
 | 
			
		||||
        return owner.tabs->getTabBackgroundColour (tabIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TabBarButton* createTabButton (const String& tabName, int tabIndex)
 | 
			
		||||
    {
 | 
			
		||||
        return owner.createTabButton (tabName, tabIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TabbedComponent& owner;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonBar)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
TabbedComponent::TabbedComponent (TabbedButtonBar::Orientation orientation)
 | 
			
		||||
{
 | 
			
		||||
    tabs.reset (new ButtonBar (*this, orientation));
 | 
			
		||||
    addAndMakeVisible (tabs.get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TabbedComponent::~TabbedComponent()
 | 
			
		||||
{
 | 
			
		||||
    clearTabs();
 | 
			
		||||
    tabs.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void TabbedComponent::setOrientation (TabbedButtonBar::Orientation orientation)
 | 
			
		||||
{
 | 
			
		||||
    tabs->setOrientation (orientation);
 | 
			
		||||
    resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TabbedButtonBar::Orientation TabbedComponent::getOrientation() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getOrientation();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setTabBarDepth (int newDepth)
 | 
			
		||||
{
 | 
			
		||||
    if (tabDepth != newDepth)
 | 
			
		||||
    {
 | 
			
		||||
        tabDepth = newDepth;
 | 
			
		||||
        resized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TabBarButton* TabbedComponent::createTabButton (const String& tabName, int /*tabIndex*/)
 | 
			
		||||
{
 | 
			
		||||
    return new TabBarButton (tabName, *tabs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void TabbedComponent::clearTabs()
 | 
			
		||||
{
 | 
			
		||||
    if (panelComponent != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        panelComponent->setVisible (false);
 | 
			
		||||
        removeChildComponent (panelComponent.get());
 | 
			
		||||
        panelComponent = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tabs->clearTabs();
 | 
			
		||||
 | 
			
		||||
    for (int i = contentComponents.size(); --i >= 0;)
 | 
			
		||||
        TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (i));
 | 
			
		||||
 | 
			
		||||
    contentComponents.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::addTab (const String& tabName,
 | 
			
		||||
                              Colour tabBackgroundColour,
 | 
			
		||||
                              Component* contentComponent,
 | 
			
		||||
                              bool deleteComponentWhenNotNeeded,
 | 
			
		||||
                              int insertIndex)
 | 
			
		||||
{
 | 
			
		||||
    contentComponents.insert (insertIndex, WeakReference<Component> (contentComponent));
 | 
			
		||||
 | 
			
		||||
    if (deleteComponentWhenNotNeeded && contentComponent != nullptr)
 | 
			
		||||
        contentComponent->getProperties().set (TabbedComponentHelpers::deleteComponentId, true);
 | 
			
		||||
 | 
			
		||||
    tabs->addTab (tabName, tabBackgroundColour, insertIndex);
 | 
			
		||||
    resized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setTabName (int tabIndex, const String& newName)
 | 
			
		||||
{
 | 
			
		||||
    tabs->setTabName (tabIndex, newName);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::removeTab (int tabIndex)
 | 
			
		||||
{
 | 
			
		||||
    if (isPositiveAndBelow (tabIndex, contentComponents.size()))
 | 
			
		||||
    {
 | 
			
		||||
        TabbedComponentHelpers::deleteIfNecessary (contentComponents.getReference (tabIndex).get());
 | 
			
		||||
        contentComponents.remove (tabIndex);
 | 
			
		||||
        tabs->removeTab (tabIndex);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::moveTab (int currentIndex, int newIndex, bool animate)
 | 
			
		||||
{
 | 
			
		||||
    contentComponents.move (currentIndex, newIndex);
 | 
			
		||||
    tabs->moveTab (currentIndex, newIndex, animate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int TabbedComponent::getNumTabs() const
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getNumTabs();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StringArray TabbedComponent::getTabNames() const
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getTabNames();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Component* TabbedComponent::getTabContentComponent (int tabIndex) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return contentComponents[tabIndex].get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Colour TabbedComponent::getTabBackgroundColour (int tabIndex) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getTabBackgroundColour (tabIndex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setTabBackgroundColour (int tabIndex, Colour newColour)
 | 
			
		||||
{
 | 
			
		||||
    tabs->setTabBackgroundColour (tabIndex, newColour);
 | 
			
		||||
 | 
			
		||||
    if (getCurrentTabIndex() == tabIndex)
 | 
			
		||||
        repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setCurrentTabIndex (int newTabIndex, bool sendChangeMessage)
 | 
			
		||||
{
 | 
			
		||||
    tabs->setCurrentTabIndex (newTabIndex, sendChangeMessage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int TabbedComponent::getCurrentTabIndex() const
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getCurrentTabIndex();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String TabbedComponent::getCurrentTabName() const
 | 
			
		||||
{
 | 
			
		||||
    return tabs->getCurrentTabName();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setOutline (int thickness)
 | 
			
		||||
{
 | 
			
		||||
    outlineThickness = thickness;
 | 
			
		||||
    resized();
 | 
			
		||||
    repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::setIndent (int indentThickness)
 | 
			
		||||
{
 | 
			
		||||
    edgeIndent = indentThickness;
 | 
			
		||||
    resized();
 | 
			
		||||
    repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::paint (Graphics& g)
 | 
			
		||||
{
 | 
			
		||||
    g.fillAll (findColour (backgroundColourId));
 | 
			
		||||
 | 
			
		||||
    auto content = getLocalBounds();
 | 
			
		||||
    BorderSize<int> outline (outlineThickness);
 | 
			
		||||
    TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth);
 | 
			
		||||
 | 
			
		||||
    g.reduceClipRegion (content);
 | 
			
		||||
    g.fillAll (tabs->getTabBackgroundColour (getCurrentTabIndex()));
 | 
			
		||||
 | 
			
		||||
    if (outlineThickness > 0)
 | 
			
		||||
    {
 | 
			
		||||
        RectangleList<int> rl (content);
 | 
			
		||||
        rl.subtract (outline.subtractedFrom (content));
 | 
			
		||||
 | 
			
		||||
        g.reduceClipRegion (rl);
 | 
			
		||||
        g.fillAll (findColour (outlineColourId));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::resized()
 | 
			
		||||
{
 | 
			
		||||
    auto content = getLocalBounds();
 | 
			
		||||
    BorderSize<int> outline (outlineThickness);
 | 
			
		||||
 | 
			
		||||
    tabs->setBounds (TabbedComponentHelpers::getTabArea (content, outline, getOrientation(), tabDepth));
 | 
			
		||||
    content = BorderSize<int> (edgeIndent).subtractedFrom (outline.subtractedFrom (content));
 | 
			
		||||
 | 
			
		||||
    for (auto& c : contentComponents)
 | 
			
		||||
        if (auto comp = c.get())
 | 
			
		||||
            comp->setBounds (content);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::lookAndFeelChanged()
 | 
			
		||||
{
 | 
			
		||||
    for (auto& c : contentComponents)
 | 
			
		||||
        if (auto comp = c.get())
 | 
			
		||||
          comp->lookAndFeelChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::changeCallback (int newCurrentTabIndex, const String& newTabName)
 | 
			
		||||
{
 | 
			
		||||
    auto* newPanelComp = getTabContentComponent (getCurrentTabIndex());
 | 
			
		||||
 | 
			
		||||
    if (newPanelComp != panelComponent)
 | 
			
		||||
    {
 | 
			
		||||
        if (panelComponent != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            panelComponent->setVisible (false);
 | 
			
		||||
            removeChildComponent (panelComponent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        panelComponent = newPanelComp;
 | 
			
		||||
 | 
			
		||||
        if (panelComponent != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            // do these ops as two stages instead of addAndMakeVisible() so that the
 | 
			
		||||
            // component has always got a parent when it gets the visibilityChanged() callback
 | 
			
		||||
            addChildComponent (panelComponent);
 | 
			
		||||
            panelComponent->sendLookAndFeelChange();
 | 
			
		||||
            panelComponent->setVisible (true);
 | 
			
		||||
            panelComponent->toFront (true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    resized();
 | 
			
		||||
    currentTabChanged (newCurrentTabIndex, newTabName);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TabbedComponent::currentTabChanged (int, const String&) {}
 | 
			
		||||
void TabbedComponent::popupMenuClickOnTab (int, const String&) {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
std::unique_ptr<AccessibilityHandler> TabbedComponent::createAccessibilityHandler()
 | 
			
		||||
{
 | 
			
		||||
    return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,230 +1,226 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component with a TabbedButtonBar along one of its sides.
 | 
			
		||||
 | 
			
		||||
    This makes it easy to create a set of tabbed pages, just add a bunch of tabs
 | 
			
		||||
    with addTab(), and this will take care of showing the pages for you when the
 | 
			
		||||
    user clicks on a different tab.
 | 
			
		||||
 | 
			
		||||
    @see TabbedButtonBar
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  TabbedComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a TabbedComponent, specifying where the tabs should be placed.
 | 
			
		||||
        Once created, add some tabs with the addTab() method.
 | 
			
		||||
    */
 | 
			
		||||
    explicit TabbedComponent (TabbedButtonBar::Orientation orientation);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~TabbedComponent() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the placement of the tabs.
 | 
			
		||||
 | 
			
		||||
        This will rearrange the layout to place the tabs along the appropriate
 | 
			
		||||
        side of this component, and will shift the content component accordingly.
 | 
			
		||||
 | 
			
		||||
        @see TabbedButtonBar::setOrientation
 | 
			
		||||
    */
 | 
			
		||||
    void setOrientation (TabbedButtonBar::Orientation orientation);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current tab placement.
 | 
			
		||||
        @see setOrientation, TabbedButtonBar::getOrientation
 | 
			
		||||
    */
 | 
			
		||||
    TabbedButtonBar::Orientation getOrientation() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Specifies how many pixels wide or high the tab-bar should be.
 | 
			
		||||
 | 
			
		||||
        If the tabs are placed along the top or bottom, this specified the height
 | 
			
		||||
        of the bar; if they're along the left or right edges, it'll be the width
 | 
			
		||||
        of the bar.
 | 
			
		||||
    */
 | 
			
		||||
    void setTabBarDepth (int newDepth);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current thickness of the tab bar.
 | 
			
		||||
        @see setTabBarDepth
 | 
			
		||||
    */
 | 
			
		||||
    int getTabBarDepth() const noexcept                         { return tabDepth; }
 | 
			
		||||
 | 
			
		||||
    /** Specifies the thickness of an outline that should be drawn around the content component.
 | 
			
		||||
 | 
			
		||||
        If this thickness is > 0, a line will be drawn around the three sides of the content
 | 
			
		||||
        component which don't touch the tab-bar, and the content component will be inset by this amount.
 | 
			
		||||
 | 
			
		||||
        To set the colour of the line, use setColour (outlineColourId, ...).
 | 
			
		||||
    */
 | 
			
		||||
    void setOutline (int newThickness);
 | 
			
		||||
 | 
			
		||||
    /** Specifies a gap to leave around the edge of the content component.
 | 
			
		||||
        Each edge of the content component will be indented by the given number of pixels.
 | 
			
		||||
    */
 | 
			
		||||
    void setIndent (int indentThickness);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Removes all the tabs from the bar.
 | 
			
		||||
        @see TabbedButtonBar::clearTabs
 | 
			
		||||
    */
 | 
			
		||||
    void clearTabs();
 | 
			
		||||
 | 
			
		||||
    /** Adds a tab to the tab-bar.
 | 
			
		||||
 | 
			
		||||
        The component passed in will be shown for the tab. If deleteComponentWhenNotNeeded
 | 
			
		||||
        is true, then the TabbedComponent will take ownership of the component and will delete
 | 
			
		||||
        it when the tab is removed or when this object is deleted.
 | 
			
		||||
 | 
			
		||||
        @see TabbedButtonBar::addTab
 | 
			
		||||
    */
 | 
			
		||||
    void addTab (const String& tabName,
 | 
			
		||||
                 Colour tabBackgroundColour,
 | 
			
		||||
                 Component* contentComponent,
 | 
			
		||||
                 bool deleteComponentWhenNotNeeded,
 | 
			
		||||
                 int insertIndex = -1);
 | 
			
		||||
 | 
			
		||||
    /** Changes the name of one of the tabs. */
 | 
			
		||||
    void setTabName (int tabIndex, const String& newName);
 | 
			
		||||
 | 
			
		||||
    /** Gets rid of one of the tabs. */
 | 
			
		||||
    void removeTab (int tabIndex);
 | 
			
		||||
 | 
			
		||||
    /** Moves a tab to a new index in the list.
 | 
			
		||||
        Pass -1 as the index to move it to the end of the list.
 | 
			
		||||
    */
 | 
			
		||||
    void moveTab (int currentIndex, int newIndex, bool animate = false);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of tabs in the bar. */
 | 
			
		||||
    int getNumTabs() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns a list of all the tab names in the bar. */
 | 
			
		||||
    StringArray getTabNames() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the content component that was added for the given index.
 | 
			
		||||
        Be careful not to reposition or delete the components that are returned, as
 | 
			
		||||
        this will interfere with the TabbedComponent's behaviour.
 | 
			
		||||
    */
 | 
			
		||||
    Component* getTabContentComponent (int tabIndex) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the colour of one of the tabs. */
 | 
			
		||||
    Colour getTabBackgroundColour (int tabIndex) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Changes the background colour of one of the tabs. */
 | 
			
		||||
    void setTabBackgroundColour (int tabIndex, Colour newColour);
 | 
			
		||||
 | 
			
		||||
    /** Set deletion policy on removal */
 | 
			
		||||
    void setTabDeleteComponentWhenNotNeeded (int tabIndex, bool flag);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the currently-selected tab.
 | 
			
		||||
        To deselect all the tabs, pass -1 as the index.
 | 
			
		||||
        @see TabbedButtonBar::setCurrentTabIndex
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentTabIndex (int newTabIndex, bool sendChangeMessage = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the index of the currently selected tab.
 | 
			
		||||
        @see addTab, TabbedButtonBar::getCurrentTabIndex()
 | 
			
		||||
    */
 | 
			
		||||
    int getCurrentTabIndex() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the name of the currently selected tab.
 | 
			
		||||
        @see addTab, TabbedButtonBar::getCurrentTabName()
 | 
			
		||||
    */
 | 
			
		||||
    String getCurrentTabName() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current component that's filling the panel.
 | 
			
		||||
        This will return nullptr if there isn't one.
 | 
			
		||||
    */
 | 
			
		||||
    Component* getCurrentContentComponent() const noexcept          { return panelComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Callback method to indicate the selected tab has been changed.
 | 
			
		||||
        @see setCurrentTabIndex
 | 
			
		||||
    */
 | 
			
		||||
    virtual void currentTabChanged (int newCurrentTabIndex, const String& newCurrentTabName);
 | 
			
		||||
 | 
			
		||||
    /** Callback method to indicate that the user has right-clicked on a tab. */
 | 
			
		||||
    virtual void popupMenuClickOnTab (int tabIndex, const String& tabName);
 | 
			
		||||
 | 
			
		||||
    /** Returns the tab button bar component that is being used. */
 | 
			
		||||
    TabbedButtonBar& getTabbedButtonBar() const noexcept            { return *tabs; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the component.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        backgroundColourId          = 0x1005800,    /**< The colour to fill the background behind the tabs. */
 | 
			
		||||
        outlineColourId             = 0x1005801,    /**< The colour to use to draw an outline around the content.
 | 
			
		||||
                                                         (See setOutline)  */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This creates one of the tab buttons.
 | 
			
		||||
 | 
			
		||||
        If you need to use custom tab components, you can override this method and
 | 
			
		||||
        return your own class instead of the default.
 | 
			
		||||
    */
 | 
			
		||||
    virtual TabBarButton* createTabButton (const String& tabName, int tabIndex);
 | 
			
		||||
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    std::unique_ptr<TabbedButtonBar> tabs;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Array<WeakReference<Component>> contentComponents;
 | 
			
		||||
    WeakReference<Component> panelComponent;
 | 
			
		||||
    int tabDepth = 30, outlineThickness = 1, edgeIndent = 0;
 | 
			
		||||
 | 
			
		||||
    struct ButtonBar;
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void changeCallback (int newCurrentTabIndex, const String& newTabName);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A component with a TabbedButtonBar along one of its sides.
 | 
			
		||||
 | 
			
		||||
    This makes it easy to create a set of tabbed pages, just add a bunch of tabs
 | 
			
		||||
    with addTab(), and this will take care of showing the pages for you when the
 | 
			
		||||
    user clicks on a different tab.
 | 
			
		||||
 | 
			
		||||
    @see TabbedButtonBar
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  TabbedComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a TabbedComponent, specifying where the tabs should be placed.
 | 
			
		||||
        Once created, add some tabs with the addTab() method.
 | 
			
		||||
    */
 | 
			
		||||
    explicit TabbedComponent (TabbedButtonBar::Orientation orientation);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~TabbedComponent() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the placement of the tabs.
 | 
			
		||||
 | 
			
		||||
        This will rearrange the layout to place the tabs along the appropriate
 | 
			
		||||
        side of this component, and will shift the content component accordingly.
 | 
			
		||||
 | 
			
		||||
        @see TabbedButtonBar::setOrientation
 | 
			
		||||
    */
 | 
			
		||||
    void setOrientation (TabbedButtonBar::Orientation orientation);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current tab placement.
 | 
			
		||||
        @see setOrientation, TabbedButtonBar::getOrientation
 | 
			
		||||
    */
 | 
			
		||||
    TabbedButtonBar::Orientation getOrientation() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Specifies how many pixels wide or high the tab-bar should be.
 | 
			
		||||
 | 
			
		||||
        If the tabs are placed along the top or bottom, this specified the height
 | 
			
		||||
        of the bar; if they're along the left or right edges, it'll be the width
 | 
			
		||||
        of the bar.
 | 
			
		||||
    */
 | 
			
		||||
    void setTabBarDepth (int newDepth);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current thickness of the tab bar.
 | 
			
		||||
        @see setTabBarDepth
 | 
			
		||||
    */
 | 
			
		||||
    int getTabBarDepth() const noexcept                         { return tabDepth; }
 | 
			
		||||
 | 
			
		||||
    /** Specifies the thickness of an outline that should be drawn around the content component.
 | 
			
		||||
 | 
			
		||||
        If this thickness is > 0, a line will be drawn around the three sides of the content
 | 
			
		||||
        component which don't touch the tab-bar, and the content component will be inset by this amount.
 | 
			
		||||
 | 
			
		||||
        To set the colour of the line, use setColour (outlineColourId, ...).
 | 
			
		||||
    */
 | 
			
		||||
    void setOutline (int newThickness);
 | 
			
		||||
 | 
			
		||||
    /** Specifies a gap to leave around the edge of the content component.
 | 
			
		||||
        Each edge of the content component will be indented by the given number of pixels.
 | 
			
		||||
    */
 | 
			
		||||
    void setIndent (int indentThickness);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Removes all the tabs from the bar.
 | 
			
		||||
        @see TabbedButtonBar::clearTabs
 | 
			
		||||
    */
 | 
			
		||||
    void clearTabs();
 | 
			
		||||
 | 
			
		||||
    /** Adds a tab to the tab-bar.
 | 
			
		||||
 | 
			
		||||
        The component passed in will be shown for the tab. If deleteComponentWhenNotNeeded
 | 
			
		||||
        is true, then the TabbedComponent will take ownership of the component and will delete
 | 
			
		||||
        it when the tab is removed or when this object is deleted.
 | 
			
		||||
 | 
			
		||||
        @see TabbedButtonBar::addTab
 | 
			
		||||
    */
 | 
			
		||||
    void addTab (const String& tabName,
 | 
			
		||||
                 Colour tabBackgroundColour,
 | 
			
		||||
                 Component* contentComponent,
 | 
			
		||||
                 bool deleteComponentWhenNotNeeded,
 | 
			
		||||
                 int insertIndex = -1);
 | 
			
		||||
 | 
			
		||||
    /** Changes the name of one of the tabs. */
 | 
			
		||||
    void setTabName (int tabIndex, const String& newName);
 | 
			
		||||
 | 
			
		||||
    /** Gets rid of one of the tabs. */
 | 
			
		||||
    void removeTab (int tabIndex);
 | 
			
		||||
 | 
			
		||||
    /** Moves a tab to a new index in the list.
 | 
			
		||||
        Pass -1 as the index to move it to the end of the list.
 | 
			
		||||
    */
 | 
			
		||||
    void moveTab (int currentIndex, int newIndex, bool animate = false);
 | 
			
		||||
 | 
			
		||||
    /** Returns the number of tabs in the bar. */
 | 
			
		||||
    int getNumTabs() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns a list of all the tab names in the bar. */
 | 
			
		||||
    StringArray getTabNames() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the content component that was added for the given index.
 | 
			
		||||
        Be careful not to reposition or delete the components that are returned, as
 | 
			
		||||
        this will interfere with the TabbedComponent's behaviour.
 | 
			
		||||
    */
 | 
			
		||||
    Component* getTabContentComponent (int tabIndex) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the colour of one of the tabs. */
 | 
			
		||||
    Colour getTabBackgroundColour (int tabIndex) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Changes the background colour of one of the tabs. */
 | 
			
		||||
    void setTabBackgroundColour (int tabIndex, Colour newColour);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the currently-selected tab.
 | 
			
		||||
        To deselect all the tabs, pass -1 as the index.
 | 
			
		||||
        @see TabbedButtonBar::setCurrentTabIndex
 | 
			
		||||
    */
 | 
			
		||||
    void setCurrentTabIndex (int newTabIndex, bool sendChangeMessage = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the index of the currently selected tab.
 | 
			
		||||
        @see addTab, TabbedButtonBar::getCurrentTabIndex()
 | 
			
		||||
    */
 | 
			
		||||
    int getCurrentTabIndex() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the name of the currently selected tab.
 | 
			
		||||
        @see addTab, TabbedButtonBar::getCurrentTabName()
 | 
			
		||||
    */
 | 
			
		||||
    String getCurrentTabName() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the current component that's filling the panel.
 | 
			
		||||
        This will return nullptr if there isn't one.
 | 
			
		||||
    */
 | 
			
		||||
    Component* getCurrentContentComponent() const noexcept          { return panelComponent.get(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Callback method to indicate the selected tab has been changed.
 | 
			
		||||
        @see setCurrentTabIndex
 | 
			
		||||
    */
 | 
			
		||||
    virtual void currentTabChanged (int newCurrentTabIndex, const String& newCurrentTabName);
 | 
			
		||||
 | 
			
		||||
    /** Callback method to indicate that the user has right-clicked on a tab. */
 | 
			
		||||
    virtual void popupMenuClickOnTab (int tabIndex, const String& tabName);
 | 
			
		||||
 | 
			
		||||
    /** Returns the tab button bar component that is being used. */
 | 
			
		||||
    TabbedButtonBar& getTabbedButtonBar() const noexcept            { return *tabs; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** A set of colour IDs to use to change the colour of various aspects of the component.
 | 
			
		||||
 | 
			
		||||
        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
			
		||||
        methods.
 | 
			
		||||
 | 
			
		||||
        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
			
		||||
    */
 | 
			
		||||
    enum ColourIds
 | 
			
		||||
    {
 | 
			
		||||
        backgroundColourId          = 0x1005800,    /**< The colour to fill the background behind the tabs. */
 | 
			
		||||
        outlineColourId             = 0x1005801,    /**< The colour to use to draw an outline around the content.
 | 
			
		||||
                                                         (See setOutline)  */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void paint (Graphics&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This creates one of the tab buttons.
 | 
			
		||||
 | 
			
		||||
        If you need to use custom tab components, you can override this method and
 | 
			
		||||
        return your own class instead of the default.
 | 
			
		||||
    */
 | 
			
		||||
    virtual TabBarButton* createTabButton (const String& tabName, int tabIndex);
 | 
			
		||||
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    std::unique_ptr<TabbedButtonBar> tabs;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Array<WeakReference<Component>> contentComponents;
 | 
			
		||||
    WeakReference<Component> panelComponent;
 | 
			
		||||
    int tabDepth = 30, outlineThickness = 1, edgeIndent = 0;
 | 
			
		||||
 | 
			
		||||
    struct ButtonBar;
 | 
			
		||||
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
 | 
			
		||||
    void changeCallback (int newCurrentTabIndex, const String& newTabName);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,339 +1,373 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A Viewport is used to contain a larger child component, and allows the child
 | 
			
		||||
    to be automatically scrolled around.
 | 
			
		||||
 | 
			
		||||
    To use a Viewport, just create one and set the component that goes inside it
 | 
			
		||||
    using the setViewedComponent() method. When the child component changes size,
 | 
			
		||||
    the Viewport will adjust its scrollbars accordingly.
 | 
			
		||||
 | 
			
		||||
    A subclass of the viewport can be created which will receive calls to its
 | 
			
		||||
    visibleAreaChanged() method when the subcomponent changes position or size.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  Viewport  : public Component,
 | 
			
		||||
                            private ComponentListener,
 | 
			
		||||
                            private ScrollBar::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a Viewport.
 | 
			
		||||
 | 
			
		||||
        The viewport is initially empty - use the setViewedComponent() method to
 | 
			
		||||
        add a child component for it to manage.
 | 
			
		||||
    */
 | 
			
		||||
    explicit Viewport (const String& componentName = String());
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~Viewport() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the component that this viewport will contain and scroll around.
 | 
			
		||||
 | 
			
		||||
        This will add the given component to this Viewport and position it at (0, 0).
 | 
			
		||||
 | 
			
		||||
        (Don't add or remove any child components directly using the normal
 | 
			
		||||
        Component::addChildComponent() methods).
 | 
			
		||||
 | 
			
		||||
        @param newViewedComponent   the component to add to this viewport, or null to remove
 | 
			
		||||
                                    the current component.
 | 
			
		||||
        @param deleteComponentWhenNoLongerNeeded    if true, the component will be deleted
 | 
			
		||||
                                    automatically when the viewport is deleted or when a different
 | 
			
		||||
                                    component is added. If false, the caller must manage the lifetime
 | 
			
		||||
                                    of the component
 | 
			
		||||
        @see getViewedComponent
 | 
			
		||||
    */
 | 
			
		||||
    void setViewedComponent (Component* newViewedComponent,
 | 
			
		||||
                             bool deleteComponentWhenNoLongerNeeded = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that's currently being used inside the Viewport.
 | 
			
		||||
 | 
			
		||||
        @see setViewedComponent
 | 
			
		||||
    */
 | 
			
		||||
    Component* getViewedComponent() const noexcept                  { return contentComp.get(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the position of the viewed component.
 | 
			
		||||
 | 
			
		||||
        The inner component will be moved so that the pixel at the top left of
 | 
			
		||||
        the viewport will be the pixel at position (xPixelsOffset, yPixelsOffset)
 | 
			
		||||
        within the inner component.
 | 
			
		||||
 | 
			
		||||
        This will update the scrollbars and might cause a call to visibleAreaChanged().
 | 
			
		||||
 | 
			
		||||
        @see getViewPositionX, getViewPositionY, setViewPositionProportionately
 | 
			
		||||
    */
 | 
			
		||||
    void setViewPosition (int xPixelsOffset, int yPixelsOffset);
 | 
			
		||||
 | 
			
		||||
    /** Changes the position of the viewed component.
 | 
			
		||||
 | 
			
		||||
        The inner component will be moved so that the pixel at the top left of
 | 
			
		||||
        the viewport will be the pixel at the specified coordinates within the
 | 
			
		||||
        inner component.
 | 
			
		||||
 | 
			
		||||
        This will update the scrollbars and might cause a call to visibleAreaChanged().
 | 
			
		||||
 | 
			
		||||
        @see getViewPositionX, getViewPositionY, setViewPositionProportionately
 | 
			
		||||
    */
 | 
			
		||||
    void setViewPosition (Point<int> newPosition);
 | 
			
		||||
 | 
			
		||||
    /** Changes the view position as a proportion of the distance it can move.
 | 
			
		||||
 | 
			
		||||
        The values here are from 0.0 to 1.0 - where (0, 0) would put the
 | 
			
		||||
        visible area in the top-left, and (1, 1) would put it as far down and
 | 
			
		||||
        to the right as it's possible to go whilst keeping the child component
 | 
			
		||||
        on-screen.
 | 
			
		||||
    */
 | 
			
		||||
    void setViewPositionProportionately (double proportionX, double proportionY);
 | 
			
		||||
 | 
			
		||||
    /** If the specified position is at the edges of the viewport, this method scrolls
 | 
			
		||||
        the viewport to bring that position nearer to the centre.
 | 
			
		||||
 | 
			
		||||
        Call this if you're dragging an object inside a viewport and want to make it scroll
 | 
			
		||||
        when the user approaches an edge. You might also find Component::beginDragAutoRepeat()
 | 
			
		||||
        useful when auto-scrolling.
 | 
			
		||||
 | 
			
		||||
        @param mouseX       the x position, relative to the Viewport's top-left
 | 
			
		||||
        @param mouseY       the y position, relative to the Viewport's top-left
 | 
			
		||||
        @param distanceFromEdge     specifies how close to an edge the position needs to be
 | 
			
		||||
                            before the viewport should scroll in that direction
 | 
			
		||||
        @param maximumSpeed the maximum number of pixels that the viewport is allowed
 | 
			
		||||
                            to scroll by.
 | 
			
		||||
        @returns            true if the viewport was scrolled
 | 
			
		||||
    */
 | 
			
		||||
    bool autoScroll (int mouseX, int mouseY, int distanceFromEdge, int maximumSpeed);
 | 
			
		||||
 | 
			
		||||
    /** Returns the position within the child component of the top-left of its visible area. */
 | 
			
		||||
    Point<int> getViewPosition() const noexcept             { return lastVisibleArea.getPosition(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the visible area of the child component, relative to its top-left */
 | 
			
		||||
    Rectangle<int> getViewArea() const noexcept             { return lastVisibleArea; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the position within the child component of the top-left of its visible area.
 | 
			
		||||
        @see getViewWidth, setViewPosition
 | 
			
		||||
    */
 | 
			
		||||
    int getViewPositionX() const noexcept                   { return lastVisibleArea.getX(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the position within the child component of the top-left of its visible area.
 | 
			
		||||
        @see getViewHeight, setViewPosition
 | 
			
		||||
    */
 | 
			
		||||
    int getViewPositionY() const noexcept                   { return lastVisibleArea.getY(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the width of the visible area of the child component.
 | 
			
		||||
 | 
			
		||||
        This may be less than the width of this Viewport if there's a vertical scrollbar
 | 
			
		||||
        or if the child component is itself smaller.
 | 
			
		||||
    */
 | 
			
		||||
    int getViewWidth() const noexcept                       { return lastVisibleArea.getWidth(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the height of the visible area of the child component.
 | 
			
		||||
 | 
			
		||||
        This may be less than the height of this Viewport if there's a horizontal scrollbar
 | 
			
		||||
        or if the child component is itself smaller.
 | 
			
		||||
    */
 | 
			
		||||
    int getViewHeight() const noexcept                      { return lastVisibleArea.getHeight(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the width available within this component for the contents.
 | 
			
		||||
 | 
			
		||||
        This will be the width of the viewport component minus the width of a
 | 
			
		||||
        vertical scrollbar (if visible).
 | 
			
		||||
    */
 | 
			
		||||
    int getMaximumVisibleWidth() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the height available within this component for the contents.
 | 
			
		||||
 | 
			
		||||
        This will be the height of the viewport component minus the space taken up
 | 
			
		||||
        by a horizontal scrollbar (if visible).
 | 
			
		||||
    */
 | 
			
		||||
    int getMaximumVisibleHeight() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Callback method that is called when the visible area changes.
 | 
			
		||||
 | 
			
		||||
        This will be called when the visible area is moved either be scrolling or
 | 
			
		||||
        by calls to setViewPosition(), etc.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void visibleAreaChanged (const Rectangle<int>& newVisibleArea);
 | 
			
		||||
 | 
			
		||||
    /** Callback method that is called when the viewed component is added, removed or swapped. */
 | 
			
		||||
    virtual void viewedComponentChanged (Component* newComponent);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Turns scrollbars on or off.
 | 
			
		||||
 | 
			
		||||
        If set to false, the scrollbars won't ever appear. When true (the default)
 | 
			
		||||
        they will appear only when needed.
 | 
			
		||||
 | 
			
		||||
        The allowVerticalScrollingWithoutScrollbar parameters allow you to enable
 | 
			
		||||
        mouse-wheel scrolling even when there the scrollbars are hidden. When the
 | 
			
		||||
        scrollbars are visible, these parameters are ignored.
 | 
			
		||||
    */
 | 
			
		||||
    void setScrollBarsShown (bool showVerticalScrollbarIfNeeded,
 | 
			
		||||
                             bool showHorizontalScrollbarIfNeeded,
 | 
			
		||||
                             bool allowVerticalScrollingWithoutScrollbar = false,
 | 
			
		||||
                             bool allowHorizontalScrollingWithoutScrollbar = false);
 | 
			
		||||
 | 
			
		||||
    /** Changes where the scroll bars are positioned
 | 
			
		||||
 | 
			
		||||
        If verticalScrollbarOnRight is set to true, then the vertical scrollbar will
 | 
			
		||||
        appear on the right side of the view port's content (this is the default),
 | 
			
		||||
        otherwise it will be on the left side of the content.
 | 
			
		||||
 | 
			
		||||
        If horizontalScrollbarAtBottom is set to true, then the horizontal scrollbar
 | 
			
		||||
        will appear at the bottom of the view port's content (this is the default),
 | 
			
		||||
        otherwise it will be at the top.
 | 
			
		||||
    */
 | 
			
		||||
    void setScrollBarPosition (bool verticalScrollbarOnRight,
 | 
			
		||||
                               bool horizontalScrollbarAtBottom);
 | 
			
		||||
 | 
			
		||||
    /** True if the vertical scrollbar will appear on the right side of the content */
 | 
			
		||||
    bool isVerticalScrollbarOnTheRight() const noexcept         { return vScrollbarRight; }
 | 
			
		||||
 | 
			
		||||
    /** True if the horizontal scrollbar will appear at the bottom of the content */
 | 
			
		||||
    bool isHorizontalScrollbarAtBottom() const noexcept         { return hScrollbarBottom; }
 | 
			
		||||
 | 
			
		||||
    /** True if the vertical scrollbar is enabled.
 | 
			
		||||
        @see setScrollBarsShown
 | 
			
		||||
    */
 | 
			
		||||
    bool isVerticalScrollBarShown() const noexcept              { return showVScrollbar; }
 | 
			
		||||
 | 
			
		||||
    /** True if the horizontal scrollbar is enabled.
 | 
			
		||||
        @see setScrollBarsShown
 | 
			
		||||
    */
 | 
			
		||||
    bool isHorizontalScrollBarShown() const noexcept            { return showHScrollbar; }
 | 
			
		||||
 | 
			
		||||
    /** Changes the width of the scrollbars.
 | 
			
		||||
        If this isn't specified, the default width from the LookAndFeel class will be used.
 | 
			
		||||
        @see LookAndFeel::getDefaultScrollbarWidth
 | 
			
		||||
    */
 | 
			
		||||
    void setScrollBarThickness (int thickness);
 | 
			
		||||
 | 
			
		||||
    /** Returns the thickness of the scrollbars.
 | 
			
		||||
        @see setScrollBarThickness
 | 
			
		||||
    */
 | 
			
		||||
    int getScrollBarThickness() const;
 | 
			
		||||
 | 
			
		||||
    /** Changes the distance that a single-step click on a scrollbar button
 | 
			
		||||
        will move the viewport.
 | 
			
		||||
    */
 | 
			
		||||
    void setSingleStepSizes (int stepX, int stepY);
 | 
			
		||||
 | 
			
		||||
    /** Returns a reference to the scrollbar component being used.
 | 
			
		||||
        Handy if you need to customise the bar somehow.
 | 
			
		||||
    */
 | 
			
		||||
    ScrollBar& getVerticalScrollBar() noexcept                  { return *verticalScrollBar; }
 | 
			
		||||
 | 
			
		||||
    /** Returns a reference to the scrollbar component being used.
 | 
			
		||||
        Handy if you need to customise the bar somehow.
 | 
			
		||||
    */
 | 
			
		||||
    ScrollBar& getHorizontalScrollBar() noexcept                { return *horizontalScrollBar; }
 | 
			
		||||
 | 
			
		||||
    /** Re-instantiates the scrollbars, which is only really useful if you've overridden createScrollBarComponent(). */
 | 
			
		||||
    void recreateScrollbars();
 | 
			
		||||
 | 
			
		||||
    /** True if there's any off-screen content that could be scrolled vertically,
 | 
			
		||||
        or false if everything is currently visible.
 | 
			
		||||
    */
 | 
			
		||||
    bool canScrollVertically() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** True if there's any off-screen content that could be scrolled horizontally,
 | 
			
		||||
        or false if everything is currently visible.
 | 
			
		||||
    */
 | 
			
		||||
    bool canScrollHorizontally() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Enables or disables drag-to-scroll functionality in the viewport.
 | 
			
		||||
 | 
			
		||||
        If your viewport contains a Component that you don't want to receive mouse events when the
 | 
			
		||||
        user is drag-scrolling, you can disable this with the Component::setViewportIgnoreDragFlag()
 | 
			
		||||
        method.
 | 
			
		||||
    */
 | 
			
		||||
    void setScrollOnDragEnabled (bool shouldScrollOnDrag);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if drag-to-scroll functionality is enabled. */
 | 
			
		||||
    bool isScrollOnDragEnabled() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the user is currently dragging-to-scroll.
 | 
			
		||||
        @see setScrollOnDragEnabled
 | 
			
		||||
    */
 | 
			
		||||
    bool isCurrentlyScrollingOnDrag() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void scrollBarMoved (ScrollBar*, double newRangeStart) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyPressed (const KeyPress&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool useMouseWheelMoveIfNeeded (const MouseEvent&, const MouseWheelDetails&);
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    static bool respondsToKey (const KeyPress&);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates the Scrollbar components that will be added to the Viewport.
 | 
			
		||||
        Subclasses can override this if they need to customise the scrollbars in some way.
 | 
			
		||||
    */
 | 
			
		||||
    virtual ScrollBar* createScrollBarComponent (bool isVertical);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    std::unique_ptr<ScrollBar> verticalScrollBar, horizontalScrollBar;
 | 
			
		||||
    Component contentHolder;
 | 
			
		||||
    WeakReference<Component> contentComp;
 | 
			
		||||
    Rectangle<int> lastVisibleArea;
 | 
			
		||||
    int scrollBarThickness = 0;
 | 
			
		||||
    int singleStepX = 16, singleStepY = 16;
 | 
			
		||||
    bool showHScrollbar = true, showVScrollbar = true, deleteContent = true;
 | 
			
		||||
    bool customScrollBarThickness = false;
 | 
			
		||||
    bool allowScrollingWithoutScrollbarV = false, allowScrollingWithoutScrollbarH = false;
 | 
			
		||||
    bool vScrollbarRight = true, hScrollbarBottom = true;
 | 
			
		||||
 | 
			
		||||
    struct DragToScrollListener;
 | 
			
		||||
    std::unique_ptr<DragToScrollListener> dragToScrollListener;
 | 
			
		||||
 | 
			
		||||
    Point<int> viewportPosToCompPos (Point<int>) const;
 | 
			
		||||
 | 
			
		||||
    void updateVisibleArea();
 | 
			
		||||
    void deleteOrRemoveContentComp();
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Viewport)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   www.gnu.org/licenses).
 | 
			
		||||
 | 
			
		||||
   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
			
		||||
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
			
		||||
   DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace juce
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A Viewport is used to contain a larger child component, and allows the child
 | 
			
		||||
    to be automatically scrolled around.
 | 
			
		||||
 | 
			
		||||
    To use a Viewport, just create one and set the component that goes inside it
 | 
			
		||||
    using the setViewedComponent() method. When the child component changes size,
 | 
			
		||||
    the Viewport will adjust its scrollbars accordingly.
 | 
			
		||||
 | 
			
		||||
    A subclass of the viewport can be created which will receive calls to its
 | 
			
		||||
    visibleAreaChanged() method when the subcomponent changes position or size.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  Viewport  : public Component,
 | 
			
		||||
                            private ComponentListener,
 | 
			
		||||
                            private ScrollBar::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a Viewport.
 | 
			
		||||
 | 
			
		||||
        The viewport is initially empty - use the setViewedComponent() method to
 | 
			
		||||
        add a child component for it to manage.
 | 
			
		||||
    */
 | 
			
		||||
    explicit Viewport (const String& componentName = String());
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~Viewport() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets the component that this viewport will contain and scroll around.
 | 
			
		||||
 | 
			
		||||
        This will add the given component to this Viewport and position it at (0, 0).
 | 
			
		||||
 | 
			
		||||
        (Don't add or remove any child components directly using the normal
 | 
			
		||||
        Component::addChildComponent() methods).
 | 
			
		||||
 | 
			
		||||
        @param newViewedComponent   the component to add to this viewport, or null to remove
 | 
			
		||||
                                    the current component.
 | 
			
		||||
        @param deleteComponentWhenNoLongerNeeded    if true, the component will be deleted
 | 
			
		||||
                                    automatically when the viewport is deleted or when a different
 | 
			
		||||
                                    component is added. If false, the caller must manage the lifetime
 | 
			
		||||
                                    of the component
 | 
			
		||||
        @see getViewedComponent
 | 
			
		||||
    */
 | 
			
		||||
    void setViewedComponent (Component* newViewedComponent,
 | 
			
		||||
                             bool deleteComponentWhenNoLongerNeeded = true);
 | 
			
		||||
 | 
			
		||||
    /** Returns the component that's currently being used inside the Viewport.
 | 
			
		||||
 | 
			
		||||
        @see setViewedComponent
 | 
			
		||||
    */
 | 
			
		||||
    Component* getViewedComponent() const noexcept                  { return contentComp.get(); }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Changes the position of the viewed component.
 | 
			
		||||
 | 
			
		||||
        The inner component will be moved so that the pixel at the top left of
 | 
			
		||||
        the viewport will be the pixel at position (xPixelsOffset, yPixelsOffset)
 | 
			
		||||
        within the inner component.
 | 
			
		||||
 | 
			
		||||
        This will update the scrollbars and might cause a call to visibleAreaChanged().
 | 
			
		||||
 | 
			
		||||
        @see getViewPositionX, getViewPositionY, setViewPositionProportionately
 | 
			
		||||
    */
 | 
			
		||||
    void setViewPosition (int xPixelsOffset, int yPixelsOffset);
 | 
			
		||||
 | 
			
		||||
    /** Changes the position of the viewed component.
 | 
			
		||||
 | 
			
		||||
        The inner component will be moved so that the pixel at the top left of
 | 
			
		||||
        the viewport will be the pixel at the specified coordinates within the
 | 
			
		||||
        inner component.
 | 
			
		||||
 | 
			
		||||
        This will update the scrollbars and might cause a call to visibleAreaChanged().
 | 
			
		||||
 | 
			
		||||
        @see getViewPositionX, getViewPositionY, setViewPositionProportionately
 | 
			
		||||
    */
 | 
			
		||||
    void setViewPosition (Point<int> newPosition);
 | 
			
		||||
 | 
			
		||||
    /** Changes the view position as a proportion of the distance it can move.
 | 
			
		||||
 | 
			
		||||
        The values here are from 0.0 to 1.0 - where (0, 0) would put the
 | 
			
		||||
        visible area in the top-left, and (1, 1) would put it as far down and
 | 
			
		||||
        to the right as it's possible to go whilst keeping the child component
 | 
			
		||||
        on-screen.
 | 
			
		||||
    */
 | 
			
		||||
    void setViewPositionProportionately (double proportionX, double proportionY);
 | 
			
		||||
 | 
			
		||||
    /** If the specified position is at the edges of the viewport, this method scrolls
 | 
			
		||||
        the viewport to bring that position nearer to the centre.
 | 
			
		||||
 | 
			
		||||
        Call this if you're dragging an object inside a viewport and want to make it scroll
 | 
			
		||||
        when the user approaches an edge. You might also find Component::beginDragAutoRepeat()
 | 
			
		||||
        useful when auto-scrolling.
 | 
			
		||||
 | 
			
		||||
        @param mouseX       the x position, relative to the Viewport's top-left
 | 
			
		||||
        @param mouseY       the y position, relative to the Viewport's top-left
 | 
			
		||||
        @param distanceFromEdge     specifies how close to an edge the position needs to be
 | 
			
		||||
                            before the viewport should scroll in that direction
 | 
			
		||||
        @param maximumSpeed the maximum number of pixels that the viewport is allowed
 | 
			
		||||
                            to scroll by.
 | 
			
		||||
        @returns            true if the viewport was scrolled
 | 
			
		||||
    */
 | 
			
		||||
    bool autoScroll (int mouseX, int mouseY, int distanceFromEdge, int maximumSpeed);
 | 
			
		||||
 | 
			
		||||
    /** Returns the position within the child component of the top-left of its visible area. */
 | 
			
		||||
    Point<int> getViewPosition() const noexcept             { return lastVisibleArea.getPosition(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the visible area of the child component, relative to its top-left */
 | 
			
		||||
    Rectangle<int> getViewArea() const noexcept             { return lastVisibleArea; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the position within the child component of the top-left of its visible area.
 | 
			
		||||
        @see getViewWidth, setViewPosition
 | 
			
		||||
    */
 | 
			
		||||
    int getViewPositionX() const noexcept                   { return lastVisibleArea.getX(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the position within the child component of the top-left of its visible area.
 | 
			
		||||
        @see getViewHeight, setViewPosition
 | 
			
		||||
    */
 | 
			
		||||
    int getViewPositionY() const noexcept                   { return lastVisibleArea.getY(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the width of the visible area of the child component.
 | 
			
		||||
 | 
			
		||||
        This may be less than the width of this Viewport if there's a vertical scrollbar
 | 
			
		||||
        or if the child component is itself smaller.
 | 
			
		||||
    */
 | 
			
		||||
    int getViewWidth() const noexcept                       { return lastVisibleArea.getWidth(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the height of the visible area of the child component.
 | 
			
		||||
 | 
			
		||||
        This may be less than the height of this Viewport if there's a horizontal scrollbar
 | 
			
		||||
        or if the child component is itself smaller.
 | 
			
		||||
    */
 | 
			
		||||
    int getViewHeight() const noexcept                      { return lastVisibleArea.getHeight(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the width available within this component for the contents.
 | 
			
		||||
 | 
			
		||||
        This will be the width of the viewport component minus the width of a
 | 
			
		||||
        vertical scrollbar (if visible).
 | 
			
		||||
    */
 | 
			
		||||
    int getMaximumVisibleWidth() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns the height available within this component for the contents.
 | 
			
		||||
 | 
			
		||||
        This will be the height of the viewport component minus the space taken up
 | 
			
		||||
        by a horizontal scrollbar (if visible).
 | 
			
		||||
    */
 | 
			
		||||
    int getMaximumVisibleHeight() const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Callback method that is called when the visible area changes.
 | 
			
		||||
 | 
			
		||||
        This will be called when the visible area is moved either be scrolling or
 | 
			
		||||
        by calls to setViewPosition(), etc.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void visibleAreaChanged (const Rectangle<int>& newVisibleArea);
 | 
			
		||||
 | 
			
		||||
    /** Callback method that is called when the viewed component is added, removed or swapped. */
 | 
			
		||||
    virtual void viewedComponentChanged (Component* newComponent);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Turns scrollbars on or off.
 | 
			
		||||
 | 
			
		||||
        If set to false, the scrollbars won't ever appear. When true (the default)
 | 
			
		||||
        they will appear only when needed.
 | 
			
		||||
 | 
			
		||||
        The allowVerticalScrollingWithoutScrollbar parameters allow you to enable
 | 
			
		||||
        mouse-wheel scrolling even when there the scrollbars are hidden. When the
 | 
			
		||||
        scrollbars are visible, these parameters are ignored.
 | 
			
		||||
    */
 | 
			
		||||
    void setScrollBarsShown (bool showVerticalScrollbarIfNeeded,
 | 
			
		||||
                             bool showHorizontalScrollbarIfNeeded,
 | 
			
		||||
                             bool allowVerticalScrollingWithoutScrollbar = false,
 | 
			
		||||
                             bool allowHorizontalScrollingWithoutScrollbar = false);
 | 
			
		||||
 | 
			
		||||
    /** Changes where the scroll bars are positioned
 | 
			
		||||
 | 
			
		||||
        If verticalScrollbarOnRight is set to true, then the vertical scrollbar will
 | 
			
		||||
        appear on the right side of the view port's content (this is the default),
 | 
			
		||||
        otherwise it will be on the left side of the content.
 | 
			
		||||
 | 
			
		||||
        If horizontalScrollbarAtBottom is set to true, then the horizontal scrollbar
 | 
			
		||||
        will appear at the bottom of the view port's content (this is the default),
 | 
			
		||||
        otherwise it will be at the top.
 | 
			
		||||
    */
 | 
			
		||||
    void setScrollBarPosition (bool verticalScrollbarOnRight,
 | 
			
		||||
                               bool horizontalScrollbarAtBottom);
 | 
			
		||||
 | 
			
		||||
    /** True if the vertical scrollbar will appear on the right side of the content */
 | 
			
		||||
    bool isVerticalScrollbarOnTheRight() const noexcept         { return vScrollbarRight; }
 | 
			
		||||
 | 
			
		||||
    /** True if the horizontal scrollbar will appear at the bottom of the content */
 | 
			
		||||
    bool isHorizontalScrollbarAtBottom() const noexcept         { return hScrollbarBottom; }
 | 
			
		||||
 | 
			
		||||
    /** True if the vertical scrollbar is enabled.
 | 
			
		||||
        @see setScrollBarsShown
 | 
			
		||||
    */
 | 
			
		||||
    bool isVerticalScrollBarShown() const noexcept              { return showVScrollbar; }
 | 
			
		||||
 | 
			
		||||
    /** True if the horizontal scrollbar is enabled.
 | 
			
		||||
        @see setScrollBarsShown
 | 
			
		||||
    */
 | 
			
		||||
    bool isHorizontalScrollBarShown() const noexcept            { return showHScrollbar; }
 | 
			
		||||
 | 
			
		||||
    /** Changes the width of the scrollbars.
 | 
			
		||||
        If this isn't specified, the default width from the LookAndFeel class will be used.
 | 
			
		||||
        @see LookAndFeel::getDefaultScrollbarWidth
 | 
			
		||||
    */
 | 
			
		||||
    void setScrollBarThickness (int thickness);
 | 
			
		||||
 | 
			
		||||
    /** Returns the thickness of the scrollbars.
 | 
			
		||||
        @see setScrollBarThickness
 | 
			
		||||
    */
 | 
			
		||||
    int getScrollBarThickness() const;
 | 
			
		||||
 | 
			
		||||
    /** Changes the distance that a single-step click on a scrollbar button
 | 
			
		||||
        will move the viewport.
 | 
			
		||||
    */
 | 
			
		||||
    void setSingleStepSizes (int stepX, int stepY);
 | 
			
		||||
 | 
			
		||||
    /** Returns a reference to the scrollbar component being used.
 | 
			
		||||
        Handy if you need to customise the bar somehow.
 | 
			
		||||
    */
 | 
			
		||||
    ScrollBar& getVerticalScrollBar() noexcept                  { return *verticalScrollBar; }
 | 
			
		||||
 | 
			
		||||
    /** Returns a reference to the scrollbar component being used.
 | 
			
		||||
        Handy if you need to customise the bar somehow.
 | 
			
		||||
    */
 | 
			
		||||
    ScrollBar& getHorizontalScrollBar() noexcept                { return *horizontalScrollBar; }
 | 
			
		||||
 | 
			
		||||
    /** Re-instantiates the scrollbars, which is only really useful if you've overridden createScrollBarComponent(). */
 | 
			
		||||
    void recreateScrollbars();
 | 
			
		||||
 | 
			
		||||
    /** True if there's any off-screen content that could be scrolled vertically,
 | 
			
		||||
        or false if everything is currently visible.
 | 
			
		||||
    */
 | 
			
		||||
    bool canScrollVertically() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** True if there's any off-screen content that could be scrolled horizontally,
 | 
			
		||||
        or false if everything is currently visible.
 | 
			
		||||
    */
 | 
			
		||||
    bool canScrollHorizontally() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Enables or disables drag-to-scroll functionality for mouse sources in the viewport.
 | 
			
		||||
 | 
			
		||||
        If your viewport contains a Component that you don't want to receive mouse events when the
 | 
			
		||||
        user is drag-scrolling, you can disable this with the Component::setViewportIgnoreDragFlag()
 | 
			
		||||
        method.
 | 
			
		||||
    */
 | 
			
		||||
    [[deprecated ("Use setScrollOnDragMode instead.")]]
 | 
			
		||||
    void setScrollOnDragEnabled (bool shouldScrollOnDrag)
 | 
			
		||||
    {
 | 
			
		||||
        setScrollOnDragMode (shouldScrollOnDrag ? ScrollOnDragMode::all : ScrollOnDragMode::never);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if drag-to-scroll functionality is enabled for mouse input sources. */
 | 
			
		||||
    [[deprecated ("Use getScrollOnDragMode instead.")]]
 | 
			
		||||
    bool isScrollOnDragEnabled() const noexcept { return getScrollOnDragMode() == ScrollOnDragMode::all; }
 | 
			
		||||
 | 
			
		||||
    enum class ScrollOnDragMode
 | 
			
		||||
    {
 | 
			
		||||
        never,          /**< Dragging will never scroll the viewport. */
 | 
			
		||||
        nonHover,       /**< Dragging will only scroll the viewport if the input source cannot hover. */
 | 
			
		||||
        all             /**< Dragging will always scroll the viewport. */
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** Sets the current scroll-on-drag mode. The default is ScrollOnDragMode::nonHover.
 | 
			
		||||
 | 
			
		||||
        If your viewport contains a Component that you don't want to receive mouse events when the
 | 
			
		||||
        user is drag-scrolling, you can disable this with the Component::setViewportIgnoreDragFlag()
 | 
			
		||||
        method.
 | 
			
		||||
    */
 | 
			
		||||
    void setScrollOnDragMode (ScrollOnDragMode scrollOnDragMode);
 | 
			
		||||
 | 
			
		||||
    /** Returns the current scroll-on-drag mode. */
 | 
			
		||||
    ScrollOnDragMode getScrollOnDragMode() const { return scrollOnDragMode; }
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the user is currently dragging-to-scroll.
 | 
			
		||||
        @see setScrollOnDragEnabled
 | 
			
		||||
    */
 | 
			
		||||
    bool isCurrentlyScrollingOnDrag() const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void resized() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void scrollBarMoved (ScrollBar*, double newRangeStart) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void mouseDown (const MouseEvent& e) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyPressed (const KeyPress&) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void lookAndFeelChanged() override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool useMouseWheelMoveIfNeeded (const MouseEvent&, const MouseWheelDetails&);
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    static bool respondsToKey (const KeyPress&);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates the Scrollbar components that will be added to the Viewport.
 | 
			
		||||
        Subclasses can override this if they need to customise the scrollbars in some way.
 | 
			
		||||
    */
 | 
			
		||||
    virtual ScrollBar* createScrollBarComponent (bool isVertical);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class AccessibilityIgnoredComponent : public Component
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
 | 
			
		||||
        {
 | 
			
		||||
            return createIgnoredAccessibilityHandler (*this);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<ScrollBar> verticalScrollBar, horizontalScrollBar;
 | 
			
		||||
    AccessibilityIgnoredComponent contentHolder;
 | 
			
		||||
    WeakReference<Component> contentComp;
 | 
			
		||||
    Rectangle<int> lastVisibleArea;
 | 
			
		||||
    int scrollBarThickness = 0;
 | 
			
		||||
    int singleStepX = 16, singleStepY = 16;
 | 
			
		||||
    ScrollOnDragMode scrollOnDragMode = ScrollOnDragMode::nonHover;
 | 
			
		||||
    bool showHScrollbar = true, showVScrollbar = true, deleteContent = true;
 | 
			
		||||
    bool customScrollBarThickness = false;
 | 
			
		||||
    bool allowScrollingWithoutScrollbarV = false, allowScrollingWithoutScrollbarH = false;
 | 
			
		||||
    bool vScrollbarRight = true, hScrollbarBottom = true;
 | 
			
		||||
 | 
			
		||||
    struct DragToScrollListener;
 | 
			
		||||
    std::unique_ptr<DragToScrollListener> dragToScrollListener;
 | 
			
		||||
 | 
			
		||||
    Point<int> viewportPosToCompPos (Point<int>) const;
 | 
			
		||||
 | 
			
		||||
    void updateVisibleArea();
 | 
			
		||||
    void deleteOrRemoveContentComp();
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Viewport)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user