text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

/**
    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().

    @see FlexItem
*/
class JUCE_API  FlexBox  final
{
public:
    /** Possible values for the flexDirection property. */
    enum class Direction       { row, rowReverse, column, columnReverse };
    /** Possible values for the flexWrap property. */
    enum class Wrap            { noWrap, wrap, wrapReverse };
    /** Possible values for the alignContent property. */
    enum class AlignContent    { stretch, flexStart, flexEnd, center, spaceBetween, spaceAround };
    /** Possible values for the alignItems property. */
    enum class AlignItems      { stretch, flexStart, flexEnd, center };
    /** Possible values for the justifyContent property. */
    enum class JustifyContent  { flexStart, flexEnd, center, spaceBetween, spaceAround };

    //==============================================================================
    /** 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


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

/**
    Defines an item in a Grid
    @see Grid
*/
class JUCE_API  GridItem
{
public:
    enum class Keyword { autoValue };

    //==============================================================================
    /** */
    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 juce::String& nameToUse) : Span (numberToUse)
        {
            /* Name must not be empty */
            jassert (nameToUse.isNotEmpty());
            name = nameToUse;
        }

        explicit Span (const juce::String& nameToUse) : name (nameToUse)
        {
            /* Name must not be empty */
            jassert (nameToUse.isNotEmpty());
        }

        int number = 1;
        juce::String name;
    };

    //==============================================================================
    /** */
    struct Property
    {
        /** */
        Property() noexcept;

        /** */
        Property (Keyword keyword) noexcept;

        /** */
        Property (const char* lineNameToUse) noexcept;

        /** */
        Property (const juce::String& lineNameToUse) noexcept;

        /** */
        Property (int numberToUse) noexcept;

        /** */
        Property (int numberToUse, const juce::String& lineNameToUse) noexcept;

        /** */
        Property (Span spanToUse) noexcept;

    private:
        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(); }

        friend class Grid;

        juce::String name;
        int number = 1; /** Either an absolute line number or number of lines to span across. */
        bool isSpan = false;
        bool isAuto = false;
    };

    //==============================================================================
    /** */
    struct StartAndEndProperty { Property start, end; };

    //==============================================================================
    /** */
    enum class JustifySelf : int { start = 0, end, center, stretch, autoValue };
    /** */
    enum class AlignSelf   : int { start = 0, end, center, stretch, autoValue };

    /** */
    GridItem() noexcept;
    /** */
    GridItem (juce::Component& componentToUse) noexcept;
    /** */
    GridItem (juce::Component* componentToUse) noexcept;

    /** Destructor. */
    ~GridItem() noexcept;

    //==============================================================================
    /** */
    juce::Component* associatedComponent = nullptr;

    //==============================================================================
    /** */
    int order = 0;

    /** */
    JustifySelf  justifySelf = JustifySelf::autoValue;
    /** */
    AlignSelf    alignSelf   = AlignSelf::autoValue;

    /** */
    StartAndEndProperty column = { Keyword::autoValue, Keyword::autoValue };
    /** */
    StartAndEndProperty row    = { Keyword::autoValue, Keyword::autoValue };

    /** */
    juce::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;
    float maxWidth = notAssigned;

    float height = notAssigned;
    float minHeight = 0;
    float maxHeight = notAssigned;

    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;
    };

    /** */
    Margin margin;

    /** */
    juce::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 juce::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 juce::String& areaName)  const noexcept;

    /**  */
    GridItem withRow (StartAndEndProperty row) const noexcept;

    /**  */
    GridItem withColumn (StartAndEndProperty column) const noexcept;

    /** */
    GridItem withAlignSelf (AlignSelf newAlignSelf) const noexcept;

    /** */
    GridItem withJustifySelf (JustifySelf newJustifySelf) const noexcept;

    /** */
    GridItem withWidth (float newWidth) const noexcept;

    /** */
    GridItem withHeight (float newHeight) const noexcept;

    /** */
    GridItem withSize (float newWidth, float newHeight) const noexcept;

    /** */
    GridItem withMargin (Margin newMargin) const noexcept;

    /** Returns a copy of this object with a new order. */
    GridItem withOrder (int newOrder) const noexcept;
};

} // namespace juce

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/** 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
*/
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();

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


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

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().getDisplayContaining (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


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

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
*/
class JUCE_API  TabBarButton  : public Button
{
public:
    //==============================================================================
    /** Creates the tab button. */
    TabBarButton (const String& name, TabbedButtonBar& ownerBar);

    /** Destructor. */
    ~TabBarButton();

    /** 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; }

    /** 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 isMouseOverButton, bool isButtonDown) 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;

    ScopedPointer<Component> extraComponent;
    ExtraComponentPlacement extraCompPlacement = afterText;

private:
    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
*/
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();

    //==============================================================================
    /** 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() {}

        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
    {
        ScopedPointer<TabBarButton> button;
        String name;
        Colour colour;
    };

    OwnedArray<TabInfo> tabs;

    Orientation orientation;
    double minimumScale = 0.7;
    int currentTabIndex = -1;

    class BehindFrontTabComp;
    friend class BehindFrontTabComp;
    friend struct ContainerDeletePolicy<BehindFrontTabComp>;
    ScopedPointer<BehindFrontTabComp> behindFrontTab;
    ScopedPointer<Button> extraTabsButton;

    void showExtraItemsMenu();
    static void extraItemsMenuCallback (int, TabbedButtonBar*);
    void updateTabPositions (bool animate);

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TabbedButtonBar)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

MultiDocumentPanelWindow::MultiDocumentPanelWindow (Colour backgroundColour)
    : DocumentWindow (String(), backgroundColour,
                      DocumentWindow::maximiseButton | DocumentWindow::closeButton, false)
{
}

MultiDocumentPanelWindow::~MultiDocumentPanelWindow()
{
}

//==============================================================================
void MultiDocumentPanelWindow::maximiseButtonPressed()
{
    if (auto* owner = getOwner())
        owner->setLayoutMode (MultiDocumentPanel::MaximisedWindowsWithTabs);
    else
        jassertfalse; // these windows are only designed to be used inside a MultiDocumentPanel!
}

void MultiDocumentPanelWindow::closeButtonPressed()
{
    if (auto* owner = getOwner())
        owner->closeDocument (getContentComponent(), true);
    else
        jassertfalse; // these windows are only designed to be used inside a MultiDocumentPanel!
}

void MultiDocumentPanelWindow::activeWindowStatusChanged()
{
    DocumentWindow::activeWindowStatusChanged();
    updateOrder();
}

void MultiDocumentPanelWindow::broughtToFront()
{
    DocumentWindow::broughtToFront();
    updateOrder();
}

void MultiDocumentPanelWindow::updateOrder()
{
    if (auto* owner = getOwner())
        owner->updateOrder();
}

MultiDocumentPanel* MultiDocumentPanelWindow::getOwner() const noexcept
{
    return findParentComponentOfClass<MultiDocumentPanel>();
}

//==============================================================================
struct MultiDocumentPanel::TabbedComponentInternal   : public TabbedComponent
{
    TabbedComponentInternal() : TabbedComponent (TabbedButtonBar::TabsAtTop) {}

    void currentTabChanged (int, const String&) override
    {
        if (auto* owner = findParentComponentOfClass<MultiDocumentPanel>())
            owner->updateOrder();
    }
};

//==============================================================================
MultiDocumentPanel::MultiDocumentPanel()
{
    setOpaque (true);
}

MultiDocumentPanel::~MultiDocumentPanel()
{
    closeAllDocuments (false);
}

//==============================================================================
namespace MultiDocHelpers
{
    static bool shouldDeleteComp (Component* const c)
    {
        return c->getProperties() ["mdiDocumentDelete_"];
    }
}

bool MultiDocumentPanel::closeAllDocuments (const bool checkItsOkToCloseFirst)
{
    while (! components.isEmpty())
        if (! closeDocument (components.getLast(), checkItsOkToCloseFirst))
            return false;

    return true;
}

MultiDocumentPanelWindow* MultiDocumentPanel::createNewDocumentWindow()
{
    return new MultiDocumentPanelWindow (backgroundColour);
}

void MultiDocumentPanel::addWindow (Component* component)
{
    auto* dw = createNewDocumentWindow();

    dw->setResizable (true, false);
    dw->setContentNonOwned (component, true);
    dw->setName (component->getName());

    auto bkg = component->getProperties() ["mdiDocumentBkg_"];
    dw->setBackgroundColour (bkg.isVoid() ? backgroundColour : Colour ((uint32) static_cast<int> (bkg)));

    int x = 4;

    if (auto* topComp = getChildren().getLast())
        if (topComp->getX() == x && topComp->getY() == x)
            x += 16;

    dw->setTopLeftPosition (x, x);

    auto pos = component->getProperties() ["mdiDocumentPos_"];
    if (pos.toString().isNotEmpty())
        dw->restoreWindowStateFromString (pos.toString());

    addAndMakeVisible (dw);
    dw->toFront (true);
}

bool MultiDocumentPanel::addDocument (Component* const component,
                                      Colour docColour,
                                      const bool deleteWhenRemoved)
{
    // If you try passing a full DocumentWindow or ResizableWindow in here, you'll end up
    // with a frame-within-a-frame! Just pass in the bare content component.
    jassert (dynamic_cast<ResizableWindow*> (component) == nullptr);

    if (component == nullptr || (maximumNumDocuments > 0 && components.size() >= maximumNumDocuments))
        return false;

    components.add (component);
    component->getProperties().set ("mdiDocumentDelete_", deleteWhenRemoved);
    component->getProperties().set ("mdiDocumentBkg_", (int) docColour.getARGB());
    component->addComponentListener (this);

    if (mode == FloatingWindows)
    {
        if (isFullscreenWhenOneDocument())
        {
            if (components.size() == 1)
            {
                addAndMakeVisible (component);
            }
            else
            {
                if (components.size() == 2)
                    addWindow (components.getFirst());

                addWindow (component);
            }
        }
        else
        {
           addWindow (component);
        }
    }
    else
    {
        if (tabComponent == nullptr && components.size() > numDocsBeforeTabsUsed)
        {
            addAndMakeVisible (tabComponent = new TabbedComponentInternal());

            auto temp = components;

            for (auto& c : temp)
                tabComponent->addTab (c->getName(), docColour, c, false);

            resized();
        }
        else
        {
            if (tabComponent != nullptr)
                tabComponent->addTab (component->getName(), docColour, component, false);
            else
                addAndMakeVisible (component);
        }

        setActiveDocument (component);
    }

    resized();
    activeDocumentChanged();
    return true;
}

bool MultiDocumentPanel::closeDocument (Component* component,
                                        const bool checkItsOkToCloseFirst)
{
    if (components.contains (component))
    {
        if (checkItsOkToCloseFirst && ! tryToCloseDocument (component))
            return false;

        component->removeComponentListener (this);

        const bool shouldDelete = MultiDocHelpers::shouldDeleteComp (component);
        component->getProperties().remove ("mdiDocumentDelete_");
        component->getProperties().remove ("mdiDocumentBkg_");

        if (mode == FloatingWindows)
        {
            for (auto* child : getChildren())
            {
                if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
                {
                    if (dw->getContentComponent() == component)
                    {
                        ScopedPointer<MultiDocumentPanelWindow> (dw)->clearContentComponent();
                        break;
                    }
                }
            }

            if (shouldDelete)
                delete component;

            components.removeFirstMatchingValue (component);

            if (isFullscreenWhenOneDocument() && components.size() == 1)
            {
                for (int i = getNumChildComponents(); --i >= 0;)
                {
                    ScopedPointer<MultiDocumentPanelWindow> dw (dynamic_cast<MultiDocumentPanelWindow*> (getChildComponent (i)));

                    if (dw != nullptr)
                        dw->clearContentComponent();
                }

                addAndMakeVisible (components.getFirst());
            }
        }
        else
        {
            jassert (components.indexOf (component) >= 0);

            if (tabComponent != nullptr)
            {
                for (int i = tabComponent->getNumTabs(); --i >= 0;)
                    if (tabComponent->getTabContentComponent (i) == component)
                        tabComponent->removeTab (i);
            }
            else
            {
                removeChildComponent (component);
            }

            if (shouldDelete)
                delete component;

            if (tabComponent != nullptr && tabComponent->getNumTabs() <= numDocsBeforeTabsUsed)
                tabComponent = nullptr;

            components.removeFirstMatchingValue (component);

            if (components.size() > 0 && tabComponent == nullptr)
                addAndMakeVisible (components.getFirst());
        }

        resized();

        // This ensures that the active tab is painted properly when a tab is closed!
        if (auto* activeComponent = getActiveDocument())
            setActiveDocument (activeComponent);

        activeDocumentChanged();
    }
    else
    {
        jassertfalse;
    }

    return true;
}

int MultiDocumentPanel::getNumDocuments() const noexcept
{
    return components.size();
}

Component* MultiDocumentPanel::getDocument (const int index) const noexcept
{
    return components [index];
}

Component* MultiDocumentPanel::getActiveDocument() const noexcept
{
    if (mode == FloatingWindows)
    {
        for (auto* child : getChildren())
            if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
                if (dw->isActiveWindow())
                    return dw->getContentComponent();
    }

    return components.getLast();
}

void MultiDocumentPanel::setActiveDocument (Component* component)
{
    jassert (component != nullptr);

    if (mode == FloatingWindows)
    {
        component = getContainerComp (component);

        if (component != nullptr)
            component->toFront (true);
    }
    else if (tabComponent != nullptr)
    {
        jassert (components.indexOf (component) >= 0);

        for (int i = tabComponent->getNumTabs(); --i >= 0;)
        {
            if (tabComponent->getTabContentComponent (i) == component)
            {
                tabComponent->setCurrentTabIndex (i);
                break;
            }
        }
    }
    else
    {
        component->grabKeyboardFocus();
    }
}

void MultiDocumentPanel::activeDocumentChanged()
{
}

void MultiDocumentPanel::setMaximumNumDocuments (const int newNumber)
{
    maximumNumDocuments = newNumber;
}

void MultiDocumentPanel::useFullscreenWhenOneDocument (const bool shouldUseTabs)
{
    numDocsBeforeTabsUsed = shouldUseTabs ? 1 : 0;
}

bool MultiDocumentPanel::isFullscreenWhenOneDocument() const noexcept
{
    return numDocsBeforeTabsUsed != 0;
}

//==============================================================================
void MultiDocumentPanel::setLayoutMode (const LayoutMode newLayoutMode)
{
    if (mode != newLayoutMode)
    {
        mode = newLayoutMode;

        if (mode == FloatingWindows)
        {
            tabComponent = nullptr;
        }
        else
        {
            for (int i = getNumChildComponents(); --i >= 0;)
            {
                ScopedPointer<MultiDocumentPanelWindow> dw (dynamic_cast<MultiDocumentPanelWindow*> (getChildComponent (i)));

                if (dw != nullptr)
                {
                    dw->getContentComponent()->getProperties().set ("mdiDocumentPos_", dw->getWindowStateAsString());
                    dw->clearContentComponent();
                }
            }
        }

        resized();

        auto tempComps = components;
        components.clear();

        for (auto* c : tempComps)
            addDocument (c,
                         Colour ((uint32) static_cast<int> (c->getProperties().getWithDefault ("mdiDocumentBkg_", (int) Colours::white.getARGB()))),
                         MultiDocHelpers::shouldDeleteComp (c));
    }
}

void MultiDocumentPanel::setBackgroundColour (Colour newBackgroundColour)
{
    if (backgroundColour != newBackgroundColour)
    {
        backgroundColour = newBackgroundColour;
        setOpaque (newBackgroundColour.isOpaque());
        repaint();
    }
}

//==============================================================================
void MultiDocumentPanel::paint (Graphics& g)
{
    g.fillAll (backgroundColour);
}

void MultiDocumentPanel::resized()
{
    if (mode == MaximisedWindowsWithTabs || components.size() == numDocsBeforeTabsUsed)
    {
        for (auto* child : getChildren())
            child->setBounds (getLocalBounds());
    }

    setWantsKeyboardFocus (components.size() == 0);
}

Component* MultiDocumentPanel::getContainerComp (Component* c) const
{
    if (mode == FloatingWindows)
    {
        for (auto* child : getChildren())
            if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
                if (dw->getContentComponent() == c)
                    return dw;
    }

    return c;
}

void MultiDocumentPanel::componentNameChanged (Component&)
{
    if (mode == FloatingWindows)
    {
        for (auto* child : getChildren())
            if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
                dw->setName (dw->getContentComponent()->getName());
    }
    else if (tabComponent != nullptr)
    {
        for (int i = tabComponent->getNumTabs(); --i >= 0;)
            tabComponent->setTabName (i, tabComponent->getTabContentComponent (i)->getName());
    }
}

void MultiDocumentPanel::updateOrder()
{
    auto oldList = components;

    if (mode == FloatingWindows)
    {
        components.clear();

        for (auto* child : getChildren())
            if (auto* dw = dynamic_cast<MultiDocumentPanelWindow*> (child))
                components.add (dw->getContentComponent());
    }
    else
    {
        if (tabComponent != nullptr)
        {
            if (auto* current = tabComponent->getCurrentContentComponent())
            {
                components.removeFirstMatchingValue (current);
                components.add (current);
            }
        }
    }

    if (components != oldList)
        activeDocumentChanged();
}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

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


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

/**
    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
*/
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;
    };

    //==============================================================================
    /** */
    struct TrackInfo  final
    {
        /** Creates a track with auto dimension. */
        TrackInfo() noexcept;
        /** */
        TrackInfo (Px sizeInPixels) noexcept;
        /** */
        TrackInfo (Fr fractionOfFreeSpace) noexcept;

        /** */
        TrackInfo (Px sizeInPixels, const juce::String& endLineNameToUse) noexcept;
        /** */
        TrackInfo (Fr fractionOfFreeSpace, const juce::String& endLineNameToUse) noexcept;

        /** */
        TrackInfo (const juce::String& startLineNameToUse, Px sizeInPixels) noexcept;
        /** */
        TrackInfo (const juce::String& startLineNameToUse, Fr fractionOfFreeSpace) noexcept;

        /** */
        TrackInfo (const juce::String& startLineNameToUse, Px sizeInPixels, const juce::String& endLineNameToUse) noexcept;
        /** */
        TrackInfo (const juce::String& startLineNameToUse, Fr fractionOfFreeSpace, const juce::String& endLineNameToUse) noexcept;

    private:
        friend class Grid;
        friend class GridItem;

        float size = 0; // Either a fraction or an absolute size in pixels
        bool isFraction = false;
        bool hasKeyword = false;

        juce::String startLineName, endLineName;
    };

    //==============================================================================
    /** */
    enum class JustifyItems : int   { start = 0, end, center, stretch };
    /** */
    enum class AlignItems : int     { start = 0, end, center, stretch };
    /** */
    enum class JustifyContent       { start, end, center, stretch, spaceAround, spaceBetween, spaceEvenly };
    /** */
    enum class AlignContent         { start, end, center, stretch, spaceAround, spaceBetween, spaceEvenly };
    /** */
    enum class AutoFlow             { row, column, rowDense, columnDense };

    //==============================================================================
    /** */
    Grid() noexcept;

    /** Destructor */
    ~Grid() noexcept;

    //==============================================================================
    /** */
    JustifyItems   justifyItems   = JustifyItems::stretch;
    /** */
    AlignItems     alignItems     = AlignItems::stretch;
    /** */
    JustifyContent justifyContent = JustifyContent::stretch;
    /** */
    AlignContent   alignContent   = AlignContent::stretch;
    /** */
    AutoFlow       autoFlow       = AutoFlow::row;

    //==============================================================================
    /** */
    juce::Array<TrackInfo> templateColumns;

    /** */
    juce::Array<TrackInfo> templateRows;

    /** Template areas */
    juce::StringArray templateAreas;

    /** */
    TrackInfo autoRows;

    /** */
    TrackInfo autoColumns;

    /** */
    Px columnGap { 0 };
    /** */
    Px rowGap { 0 };

    /** */
    void setGap (Px sizeInPixels) noexcept          { rowGap = columnGap = sizeInPixels; }

    //==============================================================================
    /** */
    juce::Array<GridItem> items;

    //==============================================================================
    /** */
    void performLayout (juce::Rectangle<int>);

    //==============================================================================
    /** */
    int getNumberOfColumns() const noexcept         { return templateColumns.size(); }
    /** */
    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


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

/**
    Describes the properties of an item inside a FlexBox container.

    @see FlexBox
*/
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. */
    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, flexStart, flexEnd, center, stretch };

    /** This is the aligh-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


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    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
*/
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


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

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);
    }

    int direction;

private:
    ScrollBar& owner;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScrollbarButton)
};

//==============================================================================
ScrollBar::ScrollBar (const bool shouldBeVertical)
    : totalRange (0.0, 1.0),
      visibleRange (0.0, 0.1),
      singleStepSize (0.1),
      thumbAreaStart (0),
      thumbAreaSize (0),
      thumbStart (0),
      thumbSize (0),
      initialDelayInMillisecs (100),
      repeatDelayInMillisecs (50),
      minimumDelayInMillisecs (10),
      vertical (shouldBeVertical),
      isDraggingThumb (false),
      autohides (true)
{
    setRepaintsOnMouseActivity (true);
    setFocusContainer (true);
}

ScrollBar::~ScrollBar()
{
    upButton = nullptr;
    downButton = nullptr;
}

//==============================================================================
void ScrollBar::setRangeLimits (Range<double> newRangeLimit, NotificationType notification)
{
    if (totalRange != newRangeLimit)
    {
        totalRange = newRangeLimit;
        setCurrentRange (visibleRange, notification);
        updateThumbPosition();
    }
}

void ScrollBar::setRangeLimits (const double newMinimum, const 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, const NotificationType notification)
{
    const Range<double> 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 (const double newStart, const double newSize, NotificationType notification)
{
    setCurrentRange (Range<double> (newStart, newStart + newSize), notification);
}

void ScrollBar::setCurrentRangeStart (const double newStart, NotificationType notification)
{
    setCurrentRange (visibleRange.movedToStartAt (newStart), notification);
}

void ScrollBar::setSingleStepSize (const double newSingleStepSize) noexcept
{
    singleStepSize = newSingleStepSize;
}

bool ScrollBar::moveScrollbarInSteps (const int howManySteps, NotificationType notification)
{
    return setCurrentRange (visibleRange + howManySteps * singleStepSize, notification);
}

bool ScrollBar::moveScrollbarInPages (const 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 (const int newInitialDelay,
                                      const int newRepeatDelay,
                                      const 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* const listener)
{
    listeners.add (listener);
}

void ScrollBar::removeListener (Listener* const listener)
{
    listeners.remove (listener);
}

void ScrollBar::handleAsyncUpdate()
{
    double start = visibleRange.getStart(); // (need to use a temp variable for VC7 compatibility)
    listeners.call (&ScrollBar::Listener::scrollBarMoved, this, start);
}

//==============================================================================
void ScrollBar::updateThumbPosition()
{
    const int 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()));

    setVisible ((! autohides) || (totalRange.getLength() > visibleRange.getLength()
                                    && visibleRange.getLength() > 0.0));

    if (thumbStart != newThumbStart  || thumbSize != newThumbSize)
    {
        const int repaintStart = jmin (thumbStart, newThumbStart) - 4;
        const int 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 (const bool shouldBeVertical)
{
    if (vertical != shouldBeVertical)
    {
        vertical = shouldBeVertical;

        if (upButton != nullptr)
        {
            upButton->direction    = vertical ? 0 : 3;
            downButton->direction  = vertical ? 2 : 1;
        }

        updateThumbPosition();
    }
}

void ScrollBar::setAutoHide (const bool shouldHideWhenFullRange)
{
    autohides = shouldHideWhenFullRange;
    updateThumbPosition();
}

bool ScrollBar::autoHides() const noexcept
{
    return autohides;
}

//==============================================================================
void ScrollBar::paint (Graphics& g)
{
    if (thumbAreaSize > 0)
    {
        LookAndFeel& lf = getLookAndFeel();

        const int 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()
{
    const int length = vertical ? getHeight() : getWidth();

    LookAndFeel& lf = getLookAndFeel();
    const bool buttonsVisible = lf.areScrollbarButtonsVisible();
    int buttonSize = 0;

    if (buttonsVisible)
    {
        if (upButton == nullptr)
        {
            addAndMakeVisible (upButton   = new ScrollbarButton (vertical ? 0 : 3, *this));
            addAndMakeVisible (downButton = new ScrollbarButton (vertical ? 2 : 1, *this));

            setButtonRepeatSpeed (initialDelayInMillisecs, repeatDelayInMillisecs, minimumDelayInMillisecs);
        }

        buttonSize = jmin (lf.getScrollbarButtonSize (*this), length / 2);
    }
    else
    {
        upButton = nullptr;
        downButton = nullptr;
    }

    if (length < 32 + lf.getMinimumScrollbarThumbSize (*this))
    {
        thumbAreaStart = length / 2;
        thumbAreaSize = 0;
    }
    else
    {
        thumbAreaStart = buttonSize;
        thumbAreaSize = length - 2 * buttonSize;
    }

    if (upButton != nullptr)
    {
        Rectangle<int> 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)
{
    const int mousePos = vertical ? e.y : e.x;

    if (isDraggingThumb && lastMousePos != mousePos && thumbAreaSize > thumbSize)
    {
        const int 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;
}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    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
*/
class JUCE_API  ComponentAnimator  : public ChangeBroadcaster,
                                     private Timer
{
public:
    //==============================================================================
    /** Creates a ComponentAnimator. */
    ComponentAnimator();

    /** Destructor. */
    ~ComponentAnimator();

    //==============================================================================
    /** 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 if 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