1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 //==============================================================================
30 /**
31     For laying out a set of components, where the components have preferred sizes
32     and size limits, but where they are allowed to stretch to fill the available
33     space.
34 
35     For example, if you have a component containing several other components, and
36     each one should be given a share of the total size, you could use one of these
37     to resize the child components when the parent component is resized. Then
38     you could add a StretchableLayoutResizerBar to easily let the user rescale them.
39 
40     A StretchableLayoutManager operates only in one dimension, so if you have a set
41     of components stacked vertically on top of each other, you'd use one to manage their
42     heights. To build up complex arrangements of components, e.g. for applications
43     with multiple nested panels, you would use more than one StretchableLayoutManager.
44     E.g. by using two (one vertical, one horizontal), you could create a resizable
45     spreadsheet-style table.
46 
47     E.g.
48     @code
49     class MyComp  : public Component
50     {
51         StretchableLayoutManager myLayout;
52 
53         MyComp()
54         {
55             myLayout.setItemLayout (0,          // for item 0
56                                     50, 100,    // must be between 50 and 100 pixels in size
57                                     -0.6);      // and its preferred size is 60% of the total available space
58 
59             myLayout.setItemLayout (1,          // for item 1
60                                     -0.2, -0.6, // size must be between 20% and 60% of the available space
61                                     50);        // and its preferred size is 50 pixels
62         }
63 
64         void resized()
65         {
66             // make a list of two of our child components that we want to reposition
67             Component* comps[] = { myComp1, myComp2 };
68 
69             // this will position the 2 components, one above the other, to fit
70             // vertically into the rectangle provided.
71             myLayout.layOutComponents (comps, 2,
72                                        0, 0, getWidth(), getHeight(),
73                                        true);
74         }
75     };
76     @endcode
77 
78     @see StretchableLayoutResizerBar
79 
80     @tags{GUI}
81 */
82 class JUCE_API  StretchableLayoutManager
83 {
84 public:
85     //==============================================================================
86     /** Creates an empty layout.
87 
88         You'll need to add some item properties to the layout before it can be used
89         to resize things - see setItemLayout().
90     */
91     StretchableLayoutManager();
92 
93     /** Destructor. */
94     ~StretchableLayoutManager();
95 
96     //==============================================================================
97     /** For a numbered item, this sets its size limits and preferred size.
98 
99         @param itemIndex        the index of the item to change.
100         @param minimumSize      the minimum size that this item is allowed to be - a positive number
101                                 indicates an absolute size in pixels. A negative number indicates a
102                                 proportion of the available space (e.g -0.5 is 50%)
103         @param maximumSize      the maximum size that this item is allowed to be - a positive number
104                                 indicates an absolute size in pixels. A negative number indicates a
105                                 proportion of the available space
106         @param preferredSize    the size that this item would like to be, if there's enough room. A
107                                 positive number indicates an absolute size in pixels. A negative number
108                                 indicates a proportion of the available space
109         @see getItemLayout
110     */
111     void setItemLayout (int itemIndex,
112                         double minimumSize,
113                         double maximumSize,
114                         double preferredSize);
115 
116     /** For a numbered item, this returns its size limits and preferred size.
117 
118         @param itemIndex        the index of the item.
119         @param minimumSize      the minimum size that this item is allowed to be - a positive number
120                                 indicates an absolute size in pixels. A negative number indicates a
121                                 proportion of the available space (e.g -0.5 is 50%)
122         @param maximumSize      the maximum size that this item is allowed to be - a positive number
123                                 indicates an absolute size in pixels. A negative number indicates a
124                                 proportion of the available space
125         @param preferredSize    the size that this item would like to be, if there's enough room. A
126                                 positive number indicates an absolute size in pixels. A negative number
127                                 indicates a proportion of the available space
128         @returns false if the item's properties hadn't been set
129         @see setItemLayout
130     */
131     bool getItemLayout (int itemIndex,
132                         double& minimumSize,
133                         double& maximumSize,
134                         double& preferredSize) const;
135 
136     /** Clears all the properties that have been set with setItemLayout() and resets
137         this object to its initial state.
138     */
139     void clearAllItems();
140 
141     //==============================================================================
142     /** Takes a set of components that correspond to the layout's items, and positions
143         them to fill a space.
144 
145         This will try to give each item its preferred size, whether that's a relative size
146         or an absolute one.
147 
148         @param components       an array of components that correspond to each of the
149                                 numbered items that the StretchableLayoutManager object
150                                 has been told about with setItemLayout()
151         @param numComponents    the number of components in the array that is passed-in. This
152                                 should be the same as the number of items this object has been
153                                 told about.
154         @param x                the left of the rectangle in which the components should
155                                 be laid out
156         @param y                the top of the rectangle in which the components should
157                                 be laid out
158         @param width            the width of the rectangle in which the components should
159                                 be laid out
160         @param height           the height of the rectangle in which the components should
161                                 be laid out
162         @param vertically       if true, the components will be positioned in a vertical stack,
163                                 so that they fill the height of the rectangle. If false, they
164                                 will be placed side-by-side in a horizontal line, filling the
165                                 available width
166         @param resizeOtherDimension     if true, this means that the components will have their
167                                 other dimension resized to fit the space - i.e. if the 'vertically'
168                                 parameter is true, their x-positions and widths are adjusted to fit
169                                 the x and width parameters; if 'vertically' is false, their y-positions
170                                 and heights are adjusted to fit the y and height parameters.
171     */
172     void layOutComponents (Component** components,
173                            int numComponents,
174                            int x, int y, int width, int height,
175                            bool vertically,
176                            bool resizeOtherDimension);
177 
178     //==============================================================================
179     /** Returns the current position of one of the items.
180 
181         This is only a valid call after layOutComponents() has been called, as it
182         returns the last position that this item was placed at. If the layout was
183         vertical, the value returned will be the y position of the top of the item,
184         relative to the top of the rectangle in which the items were placed (so for
185         example, item 0 will always have position of 0, even in the rectangle passed
186         in to layOutComponents() wasn't at y = 0). If the layout was done horizontally,
187         the position returned is the item's left-hand position, again relative to the
188         x position of the rectangle used.
189 
190         @see getItemCurrentSize, setItemPosition
191     */
192     int getItemCurrentPosition (int itemIndex) const;
193 
194     /** Returns the current size of one of the items.
195 
196         This is only meaningful after layOutComponents() has been called, as it
197         returns the last size that this item was given. If the layout was done
198         vertically, it'll return the item's height in pixels; if it was horizontal,
199         it'll return its width.
200 
201         @see getItemCurrentRelativeSize
202     */
203     int getItemCurrentAbsoluteSize (int itemIndex) const;
204 
205     /** Returns the current size of one of the items.
206 
207         This is only meaningful after layOutComponents() has been called, as it
208         returns the last size that this item was given. If the layout was done
209         vertically, it'll return a negative value representing the item's height relative
210         to the last size used for laying the components out; if the layout was done
211         horizontally it'll be the proportion of its width.
212 
213         @see getItemCurrentAbsoluteSize
214     */
215     double getItemCurrentRelativeSize (int itemIndex) const;
216 
217     //==============================================================================
218     /** Moves one of the items, shifting along any other items as necessary in
219         order to get it to the desired position.
220 
221         Calling this method will also update the preferred sizes of the items it
222         shuffles along, so that they reflect their new positions.
223 
224         (This is the method that a StretchableLayoutResizerBar uses to shift the items
225         about when it's dragged).
226 
227         @param itemIndex        the item to move
228         @param newPosition      the absolute position that you'd like this item to move
229                                 to. The item might not be able to always reach exactly this position,
230                                 because other items may have minimum sizes that constrain how
231                                 far it can go
232     */
233     void setItemPosition (int itemIndex,
234                           int newPosition);
235 
236 
237 private:
238     //==============================================================================
239     struct ItemLayoutProperties
240     {
241         int itemIndex;
242         int currentSize;
243         double minSize, maxSize, preferredSize;
244     };
245 
246     OwnedArray<ItemLayoutProperties> items;
247     int totalSize = 0;
248 
249     //==============================================================================
250     static int sizeToRealSize (double size, int totalSpace);
251     ItemLayoutProperties* getInfoFor (int itemIndex) const;
252     void setTotalSize (int newTotalSize);
253     int fitComponentsIntoSpace (int startIndex, int endIndex, int availableSpace, int startPos);
254     int getMinimumSizeOfItems (int startIndex, int endIndex) const;
255     int getMaximumSizeOfItems (int startIndex, int endIndex) const;
256     void updatePrefSizesToMatchCurrentPositions();
257 
258     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StretchableLayoutManager)
259 };
260 
261 } // namespace juce
262