JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_FlexBox.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - 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 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-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
26namespace juce
27{
28
30{
31 using Coord = double;
32
33 enum class Axis { main, cross };
34
35 FlexBoxLayoutCalculation (FlexBox& fb, Coord w, Coord h)
36 : owner (fb), parentWidth (w), parentHeight (h), numItems (owner.items.size()),
37 isRowDirection (fb.flexDirection == FlexBox::Direction::row
38 || fb.flexDirection == FlexBox::Direction::rowReverse),
39 containerLineLength (getContainerSize (Axis::main))
40 {
41 lineItems.calloc (numItems * numItems);
42 lineInfo.calloc (numItems);
43 }
44
46 {
47 ItemWithState (FlexItem& source) noexcept : item (&source) {}
48
49 FlexItem* item;
50 Coord lockedWidth = 0, lockedHeight = 0;
51 Coord lockedMarginLeft = 0, lockedMarginRight = 0, lockedMarginTop = 0, lockedMarginBottom = 0;
52 Coord preferredWidth = 0, preferredHeight = 0;
53 bool locked = false;
54
55 void resetItemLockedSize() noexcept
56 {
57 lockedWidth = preferredWidth;
58 lockedHeight = preferredHeight;
59 lockedMarginLeft = getValueOrZeroIfAuto (item->margin.left);
60 lockedMarginRight = getValueOrZeroIfAuto (item->margin.right);
61 lockedMarginTop = getValueOrZeroIfAuto (item->margin.top);
62 lockedMarginBottom = getValueOrZeroIfAuto (item->margin.bottom);
63 }
64 };
65
66 struct RowInfo
67 {
68 int numItems;
69 Coord crossSize, lineY, totalLength;
70 };
71
72 FlexBox& owner;
73 const Coord parentWidth, parentHeight;
74 const int numItems;
75 const bool isRowDirection;
76 const Coord containerLineLength;
77
78 int numberOfRows = 1;
79 Coord containerCrossLength = 0;
80
82 HeapBlock<RowInfo> lineInfo;
83 Array<ItemWithState> itemStates;
84
85 ItemWithState& getItem (int x, int y) const noexcept { return *lineItems[y * numItems + x]; }
86
87 static bool isAuto (Coord value) noexcept
88 {
89 return exactlyEqual (value, static_cast<Coord> (FlexItem::autoValue));
90 }
91
92 static bool isAssigned (Coord value) noexcept
93 {
94 return ! exactlyEqual (value, static_cast<Coord> (FlexItem::notAssigned));
95 }
96
97 static Coord getValueOrZeroIfAuto (Coord value) noexcept { return isAuto (value) ? Coord() : value; }
98
99 //==============================================================================
100 bool isSingleLine() const { return owner.flexWrap == FlexBox::Wrap::noWrap; }
101
102 template <typename Value>
103 Value& pickForAxis (Axis axis, Value& x, Value& y) const
104 {
105 return (isRowDirection ? axis == Axis::main : axis == Axis::cross) ? x : y;
106 }
107
108 auto& getStartMargin (Axis axis, ItemWithState& item) const
109 {
110 return pickForAxis (axis, item.item->margin.left, item.item->margin.top);
111 }
112
113 auto& getEndMargin (Axis axis, ItemWithState& item) const
114 {
115 return pickForAxis (axis, item.item->margin.right, item.item->margin.bottom);
116 }
117
118 auto& getStartLockedMargin (Axis axis, ItemWithState& item) const
119 {
120 return pickForAxis (axis, item.lockedMarginLeft, item.lockedMarginTop);
121 }
122
123 auto& getEndLockedMargin (Axis axis, ItemWithState& item) const
124 {
125 return pickForAxis (axis, item.lockedMarginRight, item.lockedMarginBottom);
126 }
127
128 auto& getLockedSize (Axis axis, ItemWithState& item) const
129 {
130 return pickForAxis (axis, item.lockedWidth, item.lockedHeight);
131 }
132
133 auto& getPreferredSize (Axis axis, ItemWithState& item) const
134 {
135 return pickForAxis (axis, item.preferredWidth, item.preferredHeight);
136 }
137
138 Coord getContainerSize (Axis axis) const
139 {
140 return pickForAxis (axis, parentWidth, parentHeight);
141 }
142
143 auto& getItemSize (Axis axis, ItemWithState& item) const
144 {
145 return pickForAxis (axis, item.item->width, item.item->height);
146 }
147
148 auto& getMinSize (Axis axis, ItemWithState& item) const
149 {
150 return pickForAxis (axis, item.item->minWidth, item.item->minHeight);
151 }
152
153 auto& getMaxSize (Axis axis, ItemWithState& item) const
154 {
155 return pickForAxis (axis, item.item->maxWidth, item.item->maxHeight);
156 }
157
158 //==============================================================================
159 void createStates()
160 {
161 itemStates.ensureStorageAllocated (numItems);
162
163 for (auto& item : owner.items)
164 itemStates.add (item);
165
166 std::stable_sort (itemStates.begin(), itemStates.end(),
167 [] (const ItemWithState& i1, const ItemWithState& i2) { return i1.item->order < i2.item->order; });
168
169 for (auto& item : itemStates)
170 {
171 for (auto& axis : { Axis::main, Axis::cross })
172 getPreferredSize (axis, item) = computePreferredSize (axis, item);
173 }
174 }
175
176 void initialiseItems() noexcept
177 {
178 if (isSingleLine()) // for single-line, all items go in line 1
179 {
180 lineInfo[0].numItems = numItems;
181 int i = 0;
182
183 for (auto& item : itemStates)
184 {
185 item.resetItemLockedSize();
186 lineItems[i++] = &item;
187 }
188 }
189 else // if multi-line, group the flexbox items into multiple lines
190 {
191 auto currentLength = containerLineLength;
192 int column = 0, row = 0;
193 bool firstRow = true;
194
195 for (auto& item : itemStates)
196 {
197 item.resetItemLockedSize();
198
199 const auto flexitemLength = getItemMainSize (item);
200
202 {
203 if (! firstRow)
204 row++;
205
206 if (row >= numItems)
207 break;
208
209 column = 0;
210 currentLength = containerLineLength;
211 numberOfRows = jmax (numberOfRows, row + 1);
212 }
213
215 lineItems[row * numItems + column] = &item;
216 ++column;
217 lineInfo[row].numItems = jmax (lineInfo[row].numItems, column);
218 firstRow = false;
219 }
220 }
221 }
222
223 void resolveFlexibleLengths() noexcept
224 {
225 for (int row = 0; row < numberOfRows; ++row)
226 {
227 resetRowItems (row);
228
229 for (int maxLoops = numItems; --maxLoops >= 0;)
230 {
231 resetUnlockedRowItems (row);
232
233 if (layoutRowItems (row))
234 break;
235 }
236 }
237 }
238
239 void resolveAutoMarginsOnMainAxis() noexcept
240 {
241 for (int row = 0; row < numberOfRows; ++row)
242 {
243 Coord allFlexGrow = 0;
244 const auto numColumns = lineInfo[row].numItems;
245 const auto remainingLength = containerLineLength - lineInfo[row].totalLength;
246
247 for (int column = 0; column < numColumns; ++column)
248 {
249 auto& item = getItem (column, row);
250
251 if (isAuto (getStartMargin (Axis::main, item))) ++allFlexGrow;
252 if (isAuto (getEndMargin (Axis::main, item))) ++allFlexGrow;
253 }
254
256
257 if (changeUnitWidth > 0)
258 {
259 for (int column = 0; column < numColumns; ++column)
260 {
261 auto& item = getItem (column, row);
262
263 if (isAuto (getStartMargin (Axis::main, item)))
264 getStartLockedMargin (Axis::main, item) = changeUnitWidth;
265
266 if (isAuto (getEndMargin (Axis::main, item)))
267 getEndLockedMargin (Axis::main, item) = changeUnitWidth;
268 }
269 }
270 }
271 }
272
273 void calculateCrossSizesByLine() noexcept
274 {
275 // https://www.w3.org/TR/css-flexbox-1/#algo-cross-line
276 // If the flex container is single-line and has a definite cross size, the cross size of the
277 // flex line is the flex container’s inner cross size.
278 if (isSingleLine())
279 {
280 lineInfo[0].crossSize = getContainerSize (Axis::cross);
281 }
282 else
283 {
284 for (int row = 0; row < numberOfRows; ++row)
285 {
286 Coord maxSize = 0;
287 const auto numColumns = lineInfo[row].numItems;
288
289 for (int column = 0; column < numColumns; ++column)
290 maxSize = jmax (maxSize, getItemCrossSize (getItem (column, row)));
291
292 lineInfo[row].crossSize = maxSize;
293 }
294 }
295 }
296
297 void calculateCrossSizeOfAllItems() noexcept
298 {
299 for (int row = 0; row < numberOfRows; ++row)
300 {
301 const auto numColumns = lineInfo[row].numItems;
302
303 for (int column = 0; column < numColumns; ++column)
304 {
305 auto& item = getItem (column, row);
306
307 if (isAssigned (item.item->maxHeight) && item.lockedHeight > item.item->maxHeight)
308 item.lockedHeight = item.item->maxHeight;
309
310 if (isAssigned (item.item->maxWidth) && item.lockedWidth > item.item->maxWidth)
311 item.lockedWidth = item.item->maxWidth;
312 }
313 }
314 }
315
316 void alignLinesPerAlignContent() noexcept
317 {
318 containerCrossLength = getContainerSize (Axis::cross);
319
321 {
322 for (int row = 0; row < numberOfRows; ++row)
323 for (int row2 = row; row2 < numberOfRows; ++row2)
324 lineInfo[row].lineY = row == 0 ? 0 : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
325 }
327 {
328 for (int row = 0; row < numberOfRows; ++row)
329 {
330 Coord crossHeights = 0;
331
332 for (int row2 = row; row2 < numberOfRows; ++row2)
333 crossHeights += lineInfo[row2].crossSize;
334
335 lineInfo[row].lineY = containerCrossLength - crossHeights;
336 }
337 }
338 else
339 {
340 Coord totalHeight = 0;
341
342 for (int row = 0; row < numberOfRows; ++row)
343 totalHeight += lineInfo[row].crossSize;
344
346 {
347 const auto difference = jmax (Coord(), (containerCrossLength - totalHeight) / numberOfRows);
348
349 for (int row = 0; row < numberOfRows; ++row)
350 {
351 lineInfo[row].crossSize += difference;
352 lineInfo[row].lineY = row == 0 ? 0 : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
353 }
354 }
356 {
357 const auto additionalength = (containerCrossLength - totalHeight) / 2;
358
359 for (int row = 0; row < numberOfRows; ++row)
360 lineInfo[row].lineY = row == 0 ? additionalength : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
361 }
363 {
364 const auto additionalength = numberOfRows <= 1 ? Coord() : jmax (Coord(), (containerCrossLength - totalHeight)
365 / static_cast<Coord> (numberOfRows - 1));
366 lineInfo[0].lineY = 0;
367
368 for (int row = 1; row < numberOfRows; ++row)
369 lineInfo[row].lineY += additionalength + lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
370 }
372 {
373 const auto additionalength = numberOfRows <= 1 ? Coord() : jmax (Coord(), (containerCrossLength - totalHeight)
374 / static_cast<Coord> (2 + (2 * (numberOfRows - 1))));
375
376 lineInfo[0].lineY = additionalength;
377
378 for (int row = 1; row < numberOfRows; ++row)
379 lineInfo[row].lineY += (2 * additionalength) + lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
380 }
381 }
382 }
383
384 void resolveAutoMarginsOnCrossAxis() noexcept
385 {
386 for (int row = 0; row < numberOfRows; ++row)
387 {
388 const auto numColumns = lineInfo[row].numItems;
389 const auto crossSizeForLine = lineInfo[row].crossSize;
390
391 for (int column = 0; column < numColumns; ++column)
392 {
393 auto& item = getItem (column, row);
394
395 getStartLockedMargin (Axis::cross, item) = [&]
396 {
397 if (isAuto (getStartMargin (Axis::cross, item)) && isAuto (getEndMargin (Axis::cross, item)))
398 return (crossSizeForLine - getLockedSize (Axis::cross, item)) / 2;
399
400 if (isAuto (getStartMargin (Axis::cross, item)))
401 return crossSizeForLine - getLockedSize (Axis::cross, item) - getEndMargin (Axis::cross, item);
402
403 return getStartLockedMargin (Axis::cross, item);
404 }();
405 }
406 }
407 }
408
409 // Align all flex items along the cross-axis per align-self, if neither of the item’s cross-axis margins are auto.
410 void alignItemsInCrossAxisInLinesPerAlignSelf() noexcept
411 {
412 for (int row = 0; row < numberOfRows; ++row)
413 {
414 const auto numColumns = lineInfo[row].numItems;
415 const auto lineSize = lineInfo[row].crossSize;
416
417 for (int column = 0; column < numColumns; ++column)
418 {
419 auto& item = getItem (column, row);
420
421 if (isAuto (getStartMargin (Axis::cross, item)) || isAuto (getEndMargin (Axis::cross, item)))
422 continue;
423
424 const auto alignment = [&]
425 {
426 switch (item.item->alignSelf)
427 {
433 }
434
435 return owner.alignItems;
436 }();
437
438 getStartLockedMargin (Axis::cross, item) = [&]
439 {
440 switch (alignment)
441 {
442 // https://www.w3.org/TR/css-flexbox-1/#valdef-align-items-flex-start
443 // The cross-start margin edge of the flex item is placed flush with the
444 // cross-start edge of the line.
446 return (Coord) getStartMargin (Axis::cross, item);
447
448 // https://www.w3.org/TR/css-flexbox-1/#valdef-align-items-flex-end
449 // The cross-end margin edge of the flex item is placed flush with the cross-end
450 // edge of the line.
452 return lineSize - getLockedSize (Axis::cross, item) - getEndMargin (Axis::cross, item);
453
454 // https://www.w3.org/TR/css-flexbox-1/#valdef-align-items-center
455 // The flex item’s margin box is centered in the cross axis within the line.
457 return getStartMargin (Axis::cross, item) + (lineSize - getLockedSize (Axis::cross, item) - getStartMargin (Axis::cross, item) - getEndMargin (Axis::cross, item)) / 2;
458
459 // https://www.w3.org/TR/css-flexbox-1/#valdef-align-items-stretch
461 return (Coord) getStartMargin (Axis::cross, item);
462 }
463
465 return 0.0;
466 }();
467
469 {
470 auto newSize = isAssigned (getItemSize (Axis::cross, item)) ? computePreferredSize (Axis::cross, item)
471 : lineSize - getStartMargin (Axis::cross, item) - getEndMargin (Axis::cross, item);
472
473 if (isAssigned (getMaxSize (Axis::cross, item)))
474 newSize = jmin (newSize, (Coord) getMaxSize (Axis::cross, item));
475
476 if (isAssigned (getMinSize (Axis::cross, item)))
477 newSize = jmax (newSize, (Coord) getMinSize (Axis::cross, item));
478
479 getLockedSize (Axis::cross, item) = newSize;
480 }
481 }
482 }
483 }
484
485 void alignItemsByJustifyContent() noexcept
486 {
488
489 recalculateTotalItemLengthPerLineArray();
490
491 for (int row = 0; row < numberOfRows; ++row)
492 {
493 const auto numColumns = lineInfo[row].numItems;
494 Coord x = 0;
495
497 {
498 x = containerLineLength - lineInfo[row].totalLength;
499 }
501 {
502 x = (containerLineLength - lineInfo[row].totalLength) / 2;
503 }
505 {
507 = jmax (Coord(), (containerLineLength - lineInfo[row].totalLength) / jmax (1, numColumns - 1));
508 }
510 {
512 = jmax (Coord(), (containerLineLength - lineInfo[row].totalLength) / jmax (1, 2 * numColumns));
513 }
514
515 for (int column = 0; column < numColumns; ++column)
516 {
517 auto& item = getItem (column, row);
518
519 getStartLockedMargin (Axis::main, item) += additionalMarginLeft;
520 getEndLockedMargin (Axis::main, item) += additionalMarginRight;
521
522 item.item->currentBounds.setPosition (isRowDirection ? (float) (x + item.lockedMarginLeft)
523 : (float) item.lockedMarginLeft,
524 isRowDirection ? (float) item.lockedMarginTop
525 : (float) (x + item.lockedMarginTop));
526
527 x += getItemMainSize (item);
528 }
529 }
530 }
531
532 void layoutAllItems() noexcept
533 {
534 for (int row = 0; row < numberOfRows; ++row)
535 {
536 const auto lineY = lineInfo[row].lineY;
537 const auto numColumns = lineInfo[row].numItems;
538
539 for (int column = 0; column < numColumns; ++column)
540 {
541 auto& item = getItem (column, row);
542
543 if (isRowDirection)
544 item.item->currentBounds.setY ((float) (lineY + item.lockedMarginTop));
545 else
546 item.item->currentBounds.setX ((float) (lineY + item.lockedMarginLeft));
547
548 item.item->currentBounds.setSize ((float) item.lockedWidth,
549 (float) item.lockedHeight);
550 }
551 }
552
553 reverseLocations();
554 reverseWrap();
555 }
556
557private:
558 void resetRowItems (const int row) noexcept
559 {
560 const auto numColumns = lineInfo[row].numItems;
561
562 for (int column = 0; column < numColumns; ++column)
563 resetItem (getItem (column, row));
564 }
565
566 void resetUnlockedRowItems (const int row) noexcept
567 {
568 const auto numColumns = lineInfo[row].numItems;
569
570 for (int column = 0; column < numColumns; ++column)
571 {
572 auto& item = getItem (column, row);
573
574 if (! item.locked)
575 resetItem (item);
576 }
577 }
578
579 void resetItem (ItemWithState& item) noexcept
580 {
581 item.locked = false;
582
583 for (auto& axis : { Axis::main, Axis::cross })
584 getLockedSize (axis, item) = computePreferredSize (axis, item);
585 }
586
587 bool layoutRowItems (const int row) noexcept
588 {
589 const auto numColumns = lineInfo[row].numItems;
590 auto flexContainerLength = containerLineLength;
592
593 for (int column = 0; column < numColumns; ++column)
594 {
595 const auto& item = getItem (column, row);
596
597 if (item.locked)
598 {
599 flexContainerLength -= getItemMainSize (item);
600 }
601 else
602 {
603 totalItemsLength += getItemMainSize (item);
604 totalFlexGrow += item.item->flexGrow;
605 totalFlexShrink += item.item->flexShrink;
606 }
607 }
608
609 Coord changeUnit = 0;
611 const bool positiveFlexibility = difference > 0;
612
614 {
617 }
618 else
619 {
622 }
623
624 bool ok = true;
625
626 for (int column = 0; column < numColumns; ++column)
627 {
628 auto& item = getItem (column, row);
629
630 if (! item.locked)
631 if (! addToItemLength (item, (positiveFlexibility ? item.item->flexGrow
632 : item.item->flexShrink) * changeUnit, row))
633 ok = false;
634 }
635
636 return ok;
637 }
638
639 void recalculateTotalItemLengthPerLineArray() noexcept
640 {
641 for (int row = 0; row < numberOfRows; ++row)
642 {
643 lineInfo[row].totalLength = 0;
644 const auto numColumns = lineInfo[row].numItems;
645
646 for (int column = 0; column < numColumns; ++column)
647 lineInfo[row].totalLength += getItemMainSize (getItem (column, row));
648 }
649 }
650
651 void reverseLocations() noexcept
652 {
654 {
655 for (auto& item : owner.items)
656 item.currentBounds.setX ((float) (containerLineLength - item.currentBounds.getRight()));
657 }
659 {
660 for (auto& item : owner.items)
661 item.currentBounds.setY ((float) (containerLineLength - item.currentBounds.getBottom()));
662 }
663 }
664
665 void reverseWrap() noexcept
666 {
668 {
669 if (isRowDirection)
670 {
671 for (auto& item : owner.items)
672 item.currentBounds.setY ((float) (containerCrossLength - item.currentBounds.getBottom()));
673 }
674 else
675 {
676 for (auto& item : owner.items)
677 item.currentBounds.setX ((float) (containerCrossLength - item.currentBounds.getRight()));
678 }
679 }
680 }
681
682 Coord getItemMainSize (const ItemWithState& item) const noexcept
683 {
684 return isRowDirection ? item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight
685 : item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
686 }
687
688 Coord getItemCrossSize (const ItemWithState& item) const noexcept
689 {
690 return isRowDirection ? item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom
691 : item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight;
692 }
693
694 bool addToItemLength (ItemWithState& item, const Coord length, int row) const noexcept
695 {
696 bool ok = false;
697
698 const auto prefSize = computePreferredSize (Axis::main, item);
699
700 const auto pickForMainAxis = [this] (auto& a, auto& b) -> auto& { return pickForAxis (Axis::main, a, b); };
701
702 if (isAssigned (pickForMainAxis (item.item->maxWidth, item.item->maxHeight))
703 && pickForMainAxis (item.item->maxWidth, item.item->maxHeight) < prefSize + length)
704 {
705 pickForMainAxis (item.lockedWidth, item.lockedHeight) = pickForMainAxis (item.item->maxWidth, item.item->maxHeight);
706 item.locked = true;
707 }
708 else if (isAssigned (prefSize) && pickForMainAxis (item.item->minWidth, item.item->minHeight) > prefSize + length)
709 {
710 pickForMainAxis (item.lockedWidth, item.lockedHeight) = pickForMainAxis (item.item->minWidth, item.item->minHeight);
711 item.locked = true;
712 }
713 else
714 {
715 ok = true;
716 pickForMainAxis (item.lockedWidth, item.lockedHeight) = prefSize + length;
717 }
718
719 lineInfo[row].totalLength += pickForMainAxis (item.lockedWidth, item.lockedHeight)
720 + pickForMainAxis (item.lockedMarginLeft, item.lockedMarginTop)
721 + pickForMainAxis (item.lockedMarginRight, item.lockedMarginBottom);
722
723 return ok;
724 }
725
726 Coord computePreferredSize (Axis axis, ItemWithState& itemWithState) const noexcept
727 {
728 const auto& item = *itemWithState.item;
729
730 auto preferredSize = (item.flexBasis > 0 && axis == Axis::main) ? item.flexBasis
731 : (isAssigned (getItemSize (axis, itemWithState)) ? getItemSize (axis, itemWithState)
732 : getMinSize (axis, itemWithState));
733
734 const auto minSize = getMinSize (axis, itemWithState);
735
736 if (isAssigned (minSize) && preferredSize < minSize)
737 return minSize;
738
739 const auto maxSize = getMaxSize (axis, itemWithState);
740
741 if (isAssigned (maxSize) && maxSize < preferredSize)
742 return maxSize;
743
744 return preferredSize;
745 }
746};
747
748//==============================================================================
749FlexBox::FlexBox (JustifyContent jc) noexcept : justifyContent (jc) {}
750
752 : flexDirection (d), flexWrap (w), alignContent (ac), alignItems (ai), justifyContent (jc)
753{
754}
755
757{
758 if (! items.isEmpty())
759 {
760 FlexBoxLayoutCalculation layout (*this, targetArea.getWidth(), targetArea.getHeight());
761
762 layout.createStates();
763 layout.initialiseItems();
764 layout.resolveFlexibleLengths();
765 layout.resolveAutoMarginsOnMainAxis();
766 layout.calculateCrossSizesByLine();
767 layout.calculateCrossSizeOfAllItems();
768 layout.alignLinesPerAlignContent();
769 layout.resolveAutoMarginsOnCrossAxis();
770 layout.alignItemsInCrossAxisInLinesPerAlignSelf();
771 layout.alignItemsByJustifyContent();
772 layout.layoutAllItems();
773
774 for (auto& item : items)
775 {
776 item.currentBounds += targetArea.getPosition();
777
778 if (auto* comp = item.associatedComponent)
779 comp->setBounds (Rectangle<int>::leftTopRightBottom ((int) item.currentBounds.getX(),
780 (int) item.currentBounds.getY(),
781 (int) item.currentBounds.getRight(),
782 (int) item.currentBounds.getBottom()));
783
784 if (auto* box = item.associatedFlexBox)
785 box->performLayout (item.currentBounds);
786 }
787 }
788}
789
791{
792 performLayout (targetArea.toFloat());
793}
794
795//==============================================================================
797FlexItem::FlexItem (float w, float h) noexcept : currentBounds (w, h), minWidth (w), minHeight (h) {}
798FlexItem::FlexItem (float w, float h, Component& c) noexcept : FlexItem (w, h) { associatedComponent = &c; }
799FlexItem::FlexItem (float w, float h, FlexBox& fb) noexcept : FlexItem (w, h) { associatedFlexBox = &fb; }
800FlexItem::FlexItem (Component& c) noexcept : associatedComponent (&c) {}
801FlexItem::FlexItem (FlexBox& fb) noexcept : associatedFlexBox (&fb) {}
802
803FlexItem::Margin::Margin() noexcept : left(), right(), top(), bottom() {}
804FlexItem::Margin::Margin (float v) noexcept : left (v), right (v), top (v), bottom (v) {}
805FlexItem::Margin::Margin (float t, float r, float b, float l) noexcept : left (l), right (r), top (t), bottom (b) {}
806
807//==============================================================================
809{
810 auto fi = *this;
812 return fi;
813}
814
816{
817 auto fi = withFlex (newFlexGrow);
818 fi.flexShrink = newFlexShrink;
819 return fi;
820}
821
823{
825 fi.flexBasis = newFlexBasis;
826 return fi;
827}
828
829FlexItem FlexItem::withWidth (float newWidth) const noexcept { auto fi = *this; fi.width = newWidth; return fi; }
830FlexItem FlexItem::withMinWidth (float newMinWidth) const noexcept { auto fi = *this; fi.minWidth = newMinWidth; return fi; }
831FlexItem FlexItem::withMaxWidth (float newMaxWidth) const noexcept { auto fi = *this; fi.maxWidth = newMaxWidth; return fi; }
832
833FlexItem FlexItem::withMinHeight (float newMinHeight) const noexcept { auto fi = *this; fi.minHeight = newMinHeight; return fi; }
834FlexItem FlexItem::withMaxHeight (float newMaxHeight) const noexcept { auto fi = *this; fi.maxHeight = newMaxHeight; return fi; }
835FlexItem FlexItem::withHeight (float newHeight) const noexcept { auto fi = *this; fi.height = newHeight; return fi; }
836
837FlexItem FlexItem::withMargin (Margin m) const noexcept { auto fi = *this; fi.margin = m; return fi; }
838FlexItem FlexItem::withOrder (int newOrder) const noexcept { auto fi = *this; fi.order = newOrder; return fi; }
839FlexItem FlexItem::withAlignSelf (AlignSelf a) const noexcept { auto fi = *this; fi.alignSelf = a; return fi; }
840
841//==============================================================================
842//==============================================================================
843#if JUCE_UNIT_TESTS
844
845class FlexBoxTests final : public UnitTest
846{
847public:
848 FlexBoxTests() : UnitTest ("FlexBox", UnitTestCategories::gui) {}
849
850 void runTest() override
851 {
852 using AlignSelf = FlexItem::AlignSelf;
853 using Direction = FlexBox::Direction;
854
855 const Rectangle<float> rect (10.0f, 20.0f, 300.0f, 200.0f);
856 const auto doLayout = [&rect] (Direction direction, Array<FlexItem> items)
857 {
858 juce::FlexBox flex;
859 flex.flexDirection = direction;
860 flex.items = std::move (items);
861 flex.performLayout (rect);
862 return flex;
863 };
864
865 beginTest ("flex item with mostly auto properties");
866 {
867 const auto test = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
868 {
869 const auto flex = doLayout (direction, { juce::FlexItem{}.withAlignSelf (alignment) });
870 expect (flex.items.getFirst().currentBounds == expectedBounds);
871 };
872
873 test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() });
874 test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() });
875 test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f });
876 test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom(), 0.0f, 0.0f });
877 test (Direction::row, AlignSelf::center, { rect.getX(), rect.getCentreY(), 0.0f, 0.0f });
878
879 test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f });
880 test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f });
881 test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f });
882 test (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getY(), 0.0f, 0.0f });
883 test (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getY(), 0.0f, 0.0f });
884 }
885
886 beginTest ("flex item with specified width and height");
887 {
888 constexpr auto w = 50.0f;
889 constexpr auto h = 60.0f;
890 const auto test = [&] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
891 {
892 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
893 .withWidth (w)
894 .withHeight (h) });
895 expect (flex.items.getFirst().currentBounds == expectedBounds);
896 };
897
898 test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), w, h });
899 test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), w, h });
900 test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), w, h });
901 test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom() - h, w, h });
902 test (Direction::row, AlignSelf::center, { rect.getX(), rect.getY() + (rect.getHeight() - h) * 0.5f, w, h });
903
904 test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), w, h });
905 test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), w, h });
906 test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), w, h });
907 test (Direction::column, AlignSelf::flexEnd, { rect.getRight() - w, rect.getY(), w, h });
908 test (Direction::column, AlignSelf::center, { rect.getX() + (rect.getWidth() - w) * 0.5f, rect.getY(), w, h });
909 }
910
911 beginTest ("flex item with oversized width and height");
912 {
913 const auto w = rect.getWidth() * 2;
914 const auto h = rect.getHeight() * 2;
915 const auto test = [this, &doLayout, &w, &h] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
916 {
917 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
918 .withWidth (w)
919 .withHeight (h) });
920 expect (flex.items.getFirst().currentBounds == expectedBounds);
921 };
922
923 const Rectangle<float> baseRow (rect.getX(), rect.getY(), rect.getWidth(), h);
924 test (Direction::row, AlignSelf::autoAlign, baseRow);
925 test (Direction::row, AlignSelf::stretch, baseRow);
926 test (Direction::row, AlignSelf::flexStart, baseRow);
927 test (Direction::row, AlignSelf::flexEnd, baseRow.withBottomY (rect.getBottom()));
928 test (Direction::row, AlignSelf::center, baseRow.withCentre (rect.getCentre()));
929
930 const Rectangle<float> baseColumn (rect.getX(), rect.getY(), w, rect.getHeight());
931 test (Direction::column, AlignSelf::autoAlign, baseColumn);
932 test (Direction::column, AlignSelf::stretch, baseColumn);
933 test (Direction::column, AlignSelf::flexStart, baseColumn);
934 test (Direction::column, AlignSelf::flexEnd, baseColumn.withRightX (rect.getRight()));
935 test (Direction::column, AlignSelf::center, baseColumn.withCentre (rect.getCentre()));
936 }
937
938 beginTest ("flex item with minimum width and height");
939 {
940 constexpr auto w = 50.0f;
941 constexpr auto h = 60.0f;
942 const auto test = [&] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
943 {
944 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
945 .withMinWidth (w)
946 .withMinHeight (h) });
947 expect (flex.items.getFirst().currentBounds == expectedBounds);
948 };
949
950 test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), w, rect.getHeight() });
951 test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), w, rect.getHeight() });
952 test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), w, h });
953 test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom() - h, w, h });
954 test (Direction::row, AlignSelf::center, { rect.getX(), rect.getY() + (rect.getHeight() - h) * 0.5f, w, h });
955
956 test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), h });
957 test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), h });
958 test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), w, h });
959 test (Direction::column, AlignSelf::flexEnd, { rect.getRight() - w, rect.getY(), w, h });
960 test (Direction::column, AlignSelf::center, { rect.getX() + (rect.getWidth() - w) * 0.5f, rect.getY(), w, h });
961 }
962
963 beginTest ("flex item with maximum width and height");
964 {
965 constexpr auto w = 50.0f;
966 constexpr auto h = 60.0f;
967 const auto test = [&] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
968 {
969 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
970 .withMaxWidth (w)
971 .withMaxHeight (h) });
972 expect (flex.items.getFirst().currentBounds == expectedBounds);
973 };
974
975 test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), 0.0f, h });
976 test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), 0.0f, h });
977 test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f });
978 test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom(), 0.0f, 0.0f });
979 test (Direction::row, AlignSelf::center, { rect.getX(), rect.getCentreY(), 0.0f, 0.0f });
980
981 test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), w, 0.0f });
982 test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), w, 0.0f });
983 test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f });
984 test (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getY(), 0.0f, 0.0f });
985 test (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getY(), 0.0f, 0.0f });
986 }
987
988 beginTest ("flex item with specified flex");
989 {
990 const auto test = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
991 {
992 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment).withFlex (1.0f) });
993 expect (flex.items.getFirst().currentBounds == expectedBounds);
994 };
995
996 test (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() });
997 test (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() });
998 test (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f });
999 test (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom(), rect.getWidth(), 0.0f });
1000 test (Direction::row, AlignSelf::center, { rect.getX(), rect.getCentreY(), rect.getWidth(), 0.0f });
1001
1002 test (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() });
1003 test (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() });
1004 test (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() });
1005 test (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getY(), 0.0f, rect.getHeight() });
1006 test (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getY(), 0.0f, rect.getHeight() });
1007 }
1008
1009 beginTest ("flex item with margin");
1010 {
1011 const FlexItem::Margin margin (10.0f, 20.0f, 30.0f, 40.0f);
1012
1013 const auto test = [this, &doLayout, &margin] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
1014 {
1015 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment).withMargin (margin) });
1016 expect (flex.items.getFirst().currentBounds == expectedBounds);
1017 };
1018
1019 const auto remainingHeight = rect.getHeight() - margin.top - margin.bottom;
1020 const auto remainingWidth = rect.getWidth() - margin.left - margin.right;
1021
1022 test (Direction::row, AlignSelf::autoAlign, { rect.getX() + margin.left, rect.getY() + margin.top, 0.0f, remainingHeight });
1023 test (Direction::row, AlignSelf::stretch, { rect.getX() + margin.left, rect.getY() + margin.top, 0.0f, remainingHeight });
1024 test (Direction::row, AlignSelf::flexStart, { rect.getX() + margin.left, rect.getY() + margin.top, 0.0f, 0.0f });
1025 test (Direction::row, AlignSelf::flexEnd, { rect.getX() + margin.left, rect.getBottom() - margin.bottom, 0.0f, 0.0f });
1026 test (Direction::row, AlignSelf::center, { rect.getX() + margin.left, rect.getY() + margin.top + remainingHeight * 0.5f, 0.0f, 0.0f });
1027
1028 test (Direction::column, AlignSelf::autoAlign, { rect.getX() + margin.left, rect.getY() + margin.top, remainingWidth, 0.0f });
1029 test (Direction::column, AlignSelf::stretch, { rect.getX() + margin.left, rect.getY() + margin.top, remainingWidth, 0.0f });
1030 test (Direction::column, AlignSelf::flexStart, { rect.getX() + margin.left, rect.getY() + margin.top, 0.0f, 0.0f });
1031 test (Direction::column, AlignSelf::flexEnd, { rect.getRight() - margin.right, rect.getY() + margin.top, 0.0f, 0.0f });
1032 test (Direction::column, AlignSelf::center, { rect.getX() + margin.left + remainingWidth * 0.5f, rect.getY() + margin.top, 0.0f, 0.0f });
1033 }
1034
1035 const AlignSelf alignments[] { AlignSelf::autoAlign,
1036 AlignSelf::stretch,
1037 AlignSelf::flexStart,
1038 AlignSelf::flexEnd,
1039 AlignSelf::center };
1040
1041 beginTest ("flex item with auto margin");
1042 {
1043 for (const auto& alignment : alignments)
1044 {
1045 for (const auto& direction : { Direction::row, Direction::column })
1046 {
1047 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
1048 .withMargin ((float) FlexItem::autoValue) });
1049 expect (flex.items.getFirst().currentBounds == Rectangle<float> (rect.getCentre(), rect.getCentre()));
1050 }
1051 }
1052
1053 const auto testTop = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
1054 {
1055 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
1056 .withMargin ({ (float) FlexItem::autoValue, 0.0f, 0.0f, 0.0f }) });
1057 expect (flex.items.getFirst().currentBounds == expectedBounds);
1058 };
1059
1060 for (const auto& alignment : alignments)
1061 testTop (Direction::row, alignment, { rect.getX(), rect.getBottom(), 0.0f, 0.0f });
1062
1063 testTop (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getBottom(), rect.getWidth(), 0.0f });
1064 testTop (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getBottom(), rect.getWidth(), 0.0f });
1065 testTop (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getBottom(), 0.0f, 0.0f });
1066 testTop (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getBottom(), 0.0f, 0.0f });
1067 testTop (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getBottom(), 0.0f, 0.0f });
1068
1069 const auto testBottom = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
1070 {
1071 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
1072 .withMargin ({ 0.0f, 0.0f, (float) FlexItem::autoValue, 0.0f }) });
1073 expect (flex.items.getFirst().currentBounds == expectedBounds);
1074 };
1075
1076 for (const auto& alignment : alignments)
1077 testBottom (Direction::row, alignment, { rect.getX(), rect.getY(), 0.0f, 0.0f });
1078
1079 testBottom (Direction::column, AlignSelf::autoAlign, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f });
1080 testBottom (Direction::column, AlignSelf::stretch, { rect.getX(), rect.getY(), rect.getWidth(), 0.0f });
1081 testBottom (Direction::column, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f });
1082 testBottom (Direction::column, AlignSelf::flexEnd, { rect.getRight(), rect.getY(), 0.0f, 0.0f });
1083 testBottom (Direction::column, AlignSelf::center, { rect.getCentreX(), rect.getY(), 0.0f, 0.0f });
1084
1085 const auto testLeft = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
1086 {
1087 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
1088 .withMargin ({ 0.0f, 0.0f, 0.0f, (float) FlexItem::autoValue }) });
1089 expect (flex.items.getFirst().currentBounds == expectedBounds);
1090 };
1091
1092 testLeft (Direction::row, AlignSelf::autoAlign, { rect.getRight(), rect.getY(), 0.0f, rect.getHeight() });
1093 testLeft (Direction::row, AlignSelf::stretch, { rect.getRight(), rect.getY(), 0.0f, rect.getHeight() });
1094 testLeft (Direction::row, AlignSelf::flexStart, { rect.getRight(), rect.getY(), 0.0f, 0.0f });
1095 testLeft (Direction::row, AlignSelf::flexEnd, { rect.getRight(), rect.getBottom(), 0.0f, 0.0f });
1096 testLeft (Direction::row, AlignSelf::center, { rect.getRight(), rect.getCentreY(), 0.0f, 0.0f });
1097
1098 for (const auto& alignment : alignments)
1099 testLeft (Direction::column, alignment, { rect.getRight(), rect.getY(), 0.0f, 0.0f });
1100
1101 const auto testRight = [this, &doLayout] (Direction direction, AlignSelf alignment, Rectangle<float> expectedBounds)
1102 {
1103 const auto flex = doLayout (direction, { juce::FlexItem().withAlignSelf (alignment)
1104 .withMargin ({ 0.0f, (float) FlexItem::autoValue, 0.0f, 0.0f }) });
1105 expect (flex.items.getFirst().currentBounds == expectedBounds);
1106 };
1107
1108 testRight (Direction::row, AlignSelf::autoAlign, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() });
1109 testRight (Direction::row, AlignSelf::stretch, { rect.getX(), rect.getY(), 0.0f, rect.getHeight() });
1110 testRight (Direction::row, AlignSelf::flexStart, { rect.getX(), rect.getY(), 0.0f, 0.0f });
1111 testRight (Direction::row, AlignSelf::flexEnd, { rect.getX(), rect.getBottom(), 0.0f, 0.0f });
1112 testRight (Direction::row, AlignSelf::center, { rect.getX(), rect.getCentreY(), 0.0f, 0.0f });
1113
1114 for (const auto& alignment : alignments)
1115 testRight (Direction::column, alignment, { rect.getX(), rect.getY(), 0.0f, 0.0f });
1116 }
1117
1118 beginTest ("in a multiline layout, items too large to fit on the main axis are given a line to themselves");
1119 {
1120 const auto spacer = 10.0f;
1121
1122 for (const auto alignment : alignments)
1123 {
1124 juce::FlexBox flex;
1125 flex.flexWrap = FlexBox::Wrap::wrap;
1126 flex.items = { FlexItem().withAlignSelf (alignment)
1127 .withWidth (spacer)
1128 .withHeight (spacer),
1129 FlexItem().withAlignSelf (alignment)
1130 .withWidth (rect.getWidth() * 2)
1131 .withHeight (rect.getHeight()),
1132 FlexItem().withAlignSelf (alignment)
1133 .withWidth (spacer)
1134 .withHeight (spacer) };
1135 flex.performLayout (rect);
1136
1137 expect (flex.items[0].currentBounds == Rectangle<float> (rect.getX(), rect.getY(), spacer, spacer));
1138 expect (flex.items[1].currentBounds == Rectangle<float> (rect.getX(), rect.getY() + spacer, rect.getWidth(), rect.getHeight()));
1139 expect (flex.items[2].currentBounds == Rectangle<float> (rect.getX(), rect.getBottom() + spacer, 10.0f, 10.0f));
1140 }
1141 }
1142 }
1143};
1144
1146
1147#endif
1148
1149} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
void ensureStorageAllocated(int minNumElements)
Increases the array's internal storage to hold a minimum number of elements.
ElementType * begin() noexcept
Returns a pointer to the first element in the array.
Definition juce_Array.h:328
ElementType * end() noexcept
Returns a pointer to the element which follows the last element in the array.
Definition juce_Array.h:344
The base class for all JUCE user-interface objects.
Represents a FlexBox container, which contains and manages the layout of a set of FlexItem objects.
JustifyContent justifyContent
Defines how the container distributes space between and around items along the main-axis.
AlignContent
Possible values for the alignContent property.
@ stretch
Lines of items are stretched from start to end of the cross axis.
@ center
Lines of items are aligned towards the center of the cross axis.
@ spaceBetween
Lines of items are evenly spaced along the cross axis with spaces between them.
@ flexEnd
Lines of items are aligned towards the end of the cross axis.
@ spaceAround
Lines of items are evenly spaced along the cross axis with spaces around them.
@ flexStart
Lines of items are aligned towards the start of the cross axis.
Wrap
Possible values for the flexWrap property.
@ noWrap
Items are forced into a single line.
@ wrapReverse
Items are wrapped onto multiple lines from bottom to top.
Direction
Possible values for the flexDirection property.
@ rowReverse
Set the main axis direction from right to left.
@ row
Set the main axis direction from left to right.
@ columnReverse
Set the main axis direction from bottom to top.
JustifyContent
Possible values for the justifyContent property.
@ center
Items are justified towards the center of the main axis.
@ spaceBetween
Items are evenly spaced along the main axis with spaces between them.
@ flexEnd
Items are justified towards the end of the main axis.
@ spaceAround
Items are evenly spaced along the main axis with spaces around them.
FlexBox() noexcept=default
Creates an empty FlexBox container with default parameters.
Wrap flexWrap
Specifies whether items are forced into a single line or can be wrapped onto multiple lines.
void performLayout(Rectangle< float > targetArea)
Lays-out the box's items within the given rectangle.
AlignItems
Possible values for the alignItems property.
@ stretch
Items are stretched from start to end of the cross axis.
@ center
Items are aligned towards the center of the cross axis.
@ flexEnd
Items are aligned towards the end of the cross axis.
@ flexStart
Items are aligned towards the start of the cross axis.
Array< FlexItem > items
The set of items to lay-out.
AlignItems alignItems
Specifies the alignment of flex items along the cross-axis of each line.
Direction flexDirection
Specifies how flex items are placed in the flex container, and defines the direction of the main axis...
AlignContent alignContent
Specifies how a flex container's lines are placed within the flex container when there is extra space...
Describes the properties of an item inside a FlexBox container.
float height
The item's height.
AlignSelf
Possible value for the alignSelf property.
@ stretch
Item is stretched from start to end of the cross axis.
@ center
Item is aligned towards the center of the cross axis.
@ flexEnd
Item is aligned towards the end of the cross axis.
@ flexStart
Item is aligned towards the start of the cross axis.
@ autoAlign
Follows the FlexBox container's alignItems property.
AlignSelf alignSelf
This is the align-self property of the item.
FlexItem() noexcept
Creates an item with default parameters, and zero size.
FlexItem withHeight(float newHeight) const noexcept
Returns a copy of this object with a new height.
FlexItem withWidth(float newWidth) const noexcept
Returns a copy of this object with a new width.
float minWidth
The item's minimum width.
float maxHeight
The item's maximum height.
FlexItem withOrder(int newOrder) const noexcept
Returns a copy of this object with a new order.
int order
Determines the order used to lay out items in their flex container.
static const int autoValue
This constant can be used for sizes to indicate that 'auto' mode should be used.
FlexItem withAlignSelf(AlignSelf newAlignSelf) const noexcept
Returns a copy of this object with a new alignSelf value.
FlexItem withMinWidth(float newMinWidth) const noexcept
Returns a copy of this object with a new minimum width.
FlexItem withMinHeight(float newMinHeight) const noexcept
Returns a copy of this object with a new minimum height.
float minHeight
The item's minimum height.
FlexItem withMargin(Margin) const noexcept
Returns a copy of this object with a new margin.
FlexItem withMaxHeight(float newMaxHeight) const noexcept
Returns a copy of this object with a new maximum height.
float width
The item's width.
float flexGrow
Specifies the flex grow factor of this item.
float maxWidth
The item's maximum width.
FlexItem withMaxWidth(float newMaxWidth) const noexcept
Returns a copy of this object with a new maximum width.
FlexItem withFlex(float newFlexGrow) const noexcept
Returns a copy of this object with a new flex-grow value.
Margin margin
The margin to leave around this item.
static const int notAssigned
This constant can be used for sizes to indicate that no value has been set.
Very simple container class to hold a pointer to some data on the heap.
Manages a rectangle and allows geometric operations to be performed on it.
Rectangle< float > toFloat() const noexcept
Casts this rectangle to a Rectangle<float>.
Point< ValueType > getPosition() const noexcept
Returns the rectangle's top-left position as a Point.
ValueType getWidth() const noexcept
Returns the width of the rectangle.
ValueType getHeight() const noexcept
Returns the height of the rectangle.
This is a base class for classes that perform a unit test.
#define jassertfalse
This will always cause an assertion failure.
typedef double
JUCE Namespace.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr bool exactlyEqual(Type a, Type b)
Equivalent to operator==, but suppresses float-equality warnings.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
T stable_sort(T... args)
Represents a margin.
Margin() noexcept
Creates a margin of size zero.
float right
Right margin size.
float left
Left margin size.
float bottom
Bottom margin size.
float top
Top margin size.