2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
29 StretchableLayoutManager::StretchableLayoutManager() {}
30 StretchableLayoutManager::~StretchableLayoutManager() {}
32 //==============================================================================
33 void StretchableLayoutManager::clearAllItems()
39 void StretchableLayoutManager::setItemLayout (const int itemIndex
,
40 const double minimumSize
,
41 const double maximumSize
,
42 const double preferredSize
)
44 auto* layout
= getInfoFor (itemIndex
);
46 if (layout
== nullptr)
48 layout
= new ItemLayoutProperties();
49 layout
->itemIndex
= itemIndex
;
52 for (i
= 0; i
< items
.size(); ++i
)
53 if (items
.getUnchecked (i
)->itemIndex
> itemIndex
)
56 items
.insert (i
, layout
);
59 layout
->minSize
= minimumSize
;
60 layout
->maxSize
= maximumSize
;
61 layout
->preferredSize
= preferredSize
;
62 layout
->currentSize
= 0;
65 bool StretchableLayoutManager::getItemLayout (const int itemIndex
,
68 double& preferredSize
) const
70 if (auto* layout
= getInfoFor (itemIndex
))
72 minimumSize
= layout
->minSize
;
73 maximumSize
= layout
->maxSize
;
74 preferredSize
= layout
->preferredSize
;
81 //==============================================================================
82 void StretchableLayoutManager::setTotalSize (const int newTotalSize
)
84 totalSize
= newTotalSize
;
86 fitComponentsIntoSpace (0, items
.size(), totalSize
, 0);
89 int StretchableLayoutManager::getItemCurrentPosition (const int itemIndex
) const
93 for (int i
= 0; i
< itemIndex
; ++i
)
94 if (auto* layout
= getInfoFor (i
))
95 pos
+= layout
->currentSize
;
100 int StretchableLayoutManager::getItemCurrentAbsoluteSize (const int itemIndex
) const
102 if (auto* layout
= getInfoFor (itemIndex
))
103 return layout
->currentSize
;
108 double StretchableLayoutManager::getItemCurrentRelativeSize (const int itemIndex
) const
110 if (auto* layout
= getInfoFor (itemIndex
))
111 return -layout
->currentSize
/ (double) totalSize
;
116 void StretchableLayoutManager::setItemPosition (const int itemIndex
,
119 for (int i
= items
.size(); --i
>= 0;)
121 auto* layout
= items
.getUnchecked(i
);
123 if (layout
->itemIndex
== itemIndex
)
125 auto realTotalSize
= jmax (totalSize
, getMinimumSizeOfItems (0, items
.size()));
126 auto minSizeAfterThisComp
= getMinimumSizeOfItems (i
, items
.size());
127 auto maxSizeAfterThisComp
= getMaximumSizeOfItems (i
+ 1, items
.size());
129 newPosition
= jmax (newPosition
, totalSize
- maxSizeAfterThisComp
- layout
->currentSize
);
130 newPosition
= jmin (newPosition
, realTotalSize
- minSizeAfterThisComp
);
132 auto endPos
= fitComponentsIntoSpace (0, i
, newPosition
, 0);
134 endPos
+= layout
->currentSize
;
136 fitComponentsIntoSpace (i
+ 1, items
.size(), totalSize
- endPos
, endPos
);
137 updatePrefSizesToMatchCurrentPositions();
143 //==============================================================================
144 void StretchableLayoutManager::layOutComponents (Component
** const components
,
146 int x
, int y
, int w
, int h
,
147 const bool vertically
,
148 const bool resizeOtherDimension
)
150 setTotalSize (vertically
? h
: w
);
151 int pos
= vertically
? y
: x
;
153 for (int i
= 0; i
< numComponents
; ++i
)
155 if (auto* layout
= getInfoFor (i
))
157 if (auto* c
= components
[i
])
159 if (i
== numComponents
- 1)
161 // if it's the last item, crop it to exactly fit the available space..
162 if (resizeOtherDimension
)
165 c
->setBounds (x
, pos
, w
, jmax (layout
->currentSize
, h
- pos
));
167 c
->setBounds (pos
, y
, jmax (layout
->currentSize
, w
- pos
), h
);
172 c
->setBounds (c
->getX(), pos
, c
->getWidth(), jmax (layout
->currentSize
, h
- pos
));
174 c
->setBounds (pos
, c
->getY(), jmax (layout
->currentSize
, w
- pos
), c
->getHeight());
179 if (resizeOtherDimension
)
182 c
->setBounds (x
, pos
, w
, layout
->currentSize
);
184 c
->setBounds (pos
, y
, layout
->currentSize
, h
);
189 c
->setBounds (c
->getX(), pos
, c
->getWidth(), layout
->currentSize
);
191 c
->setBounds (pos
, c
->getY(), layout
->currentSize
, c
->getHeight());
196 pos
+= layout
->currentSize
;
202 //==============================================================================
203 StretchableLayoutManager::ItemLayoutProperties
* StretchableLayoutManager::getInfoFor (const int itemIndex
) const
205 for (auto* i
: items
)
206 if (i
->itemIndex
== itemIndex
)
212 int StretchableLayoutManager::fitComponentsIntoSpace (const int startIndex
,
214 const int availableSpace
,
217 // calculate the total sizes
218 double totalIdealSize
= 0.0;
219 int totalMinimums
= 0;
221 for (int i
= startIndex
; i
< endIndex
; ++i
)
223 auto* layout
= items
.getUnchecked (i
);
225 layout
->currentSize
= sizeToRealSize (layout
->minSize
, totalSize
);
227 totalMinimums
+= layout
->currentSize
;
228 totalIdealSize
+= sizeToRealSize (layout
->preferredSize
, totalSize
);
231 if (totalIdealSize
<= 0)
232 totalIdealSize
= 1.0;
234 // now calc the best sizes..
235 int extraSpace
= availableSpace
- totalMinimums
;
237 while (extraSpace
> 0)
239 int numWantingMoreSpace
= 0;
240 int numHavingTakenExtraSpace
= 0;
242 // first figure out how many comps want a slice of the extra space..
243 for (int i
= startIndex
; i
< endIndex
; ++i
)
245 auto* layout
= items
.getUnchecked (i
);
247 auto sizeWanted
= sizeToRealSize (layout
->preferredSize
, totalSize
);
249 auto bestSize
= jlimit (layout
->currentSize
,
250 jmax (layout
->currentSize
,
251 sizeToRealSize (layout
->maxSize
, totalSize
)),
252 roundToInt (sizeWanted
* availableSpace
/ totalIdealSize
));
254 if (bestSize
> layout
->currentSize
)
255 ++numWantingMoreSpace
;
258 // ..share out the extra space..
259 for (int i
= startIndex
; i
< endIndex
; ++i
)
261 auto* layout
= items
.getUnchecked (i
);
263 auto sizeWanted
= sizeToRealSize (layout
->preferredSize
, totalSize
);
265 auto bestSize
= jlimit (layout
->currentSize
,
266 jmax (layout
->currentSize
, sizeToRealSize (layout
->maxSize
, totalSize
)),
267 roundToInt (sizeWanted
* availableSpace
/ totalIdealSize
));
269 auto extraWanted
= bestSize
- layout
->currentSize
;
273 auto extraAllowed
= jmin (extraWanted
,
274 extraSpace
/ jmax (1, numWantingMoreSpace
));
276 if (extraAllowed
> 0)
278 ++numHavingTakenExtraSpace
;
279 --numWantingMoreSpace
;
281 layout
->currentSize
+= extraAllowed
;
282 extraSpace
-= extraAllowed
;
287 if (numHavingTakenExtraSpace
<= 0)
291 // ..and calculate the end position
292 for (int i
= startIndex
; i
< endIndex
; ++i
)
294 auto* layout
= items
.getUnchecked(i
);
295 startPos
+= layout
->currentSize
;
301 int StretchableLayoutManager::getMinimumSizeOfItems (const int startIndex
,
302 const int endIndex
) const
304 int totalMinimums
= 0;
306 for (int i
= startIndex
; i
< endIndex
; ++i
)
307 totalMinimums
+= sizeToRealSize (items
.getUnchecked (i
)->minSize
, totalSize
);
309 return totalMinimums
;
312 int StretchableLayoutManager::getMaximumSizeOfItems (const int startIndex
, const int endIndex
) const
314 int totalMaximums
= 0;
316 for (int i
= startIndex
; i
< endIndex
; ++i
)
317 totalMaximums
+= sizeToRealSize (items
.getUnchecked (i
)->maxSize
, totalSize
);
319 return totalMaximums
;
322 void StretchableLayoutManager::updatePrefSizesToMatchCurrentPositions()
324 for (int i
= 0; i
< items
.size(); ++i
)
326 auto* layout
= items
.getUnchecked (i
);
328 layout
->preferredSize
329 = (layout
->preferredSize
< 0) ? getItemCurrentRelativeSize (i
)
330 : getItemCurrentAbsoluteSize (i
);
334 int StretchableLayoutManager::sizeToRealSize (double size
, int totalSpace
)
339 return roundToInt (size
);