Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_FlexBox.cpp 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. struct FlexBoxLayoutCalculation
  21. {
  22. using Coord = double;
  23. FlexBoxLayoutCalculation (FlexBox& fb, Coord w, Coord h)
  24. : owner (fb), parentWidth (w), parentHeight (h), numItems (owner.items.size()),
  25. isRowDirection (fb.flexDirection == FlexBox::Direction::row
  26. || fb.flexDirection == FlexBox::Direction::rowReverse),
  27. containerLineLength (isRowDirection ? parentWidth : parentHeight)
  28. {
  29. lineItems.calloc (numItems * numItems);
  30. lineInfo.calloc (numItems);
  31. }
  32. struct ItemWithState
  33. {
  34. ItemWithState (FlexItem& source) noexcept : item (&source) {}
  35. FlexItem* item;
  36. Coord lockedWidth = 0, lockedHeight = 0;
  37. Coord lockedMarginLeft = 0, lockedMarginRight = 0, lockedMarginTop = 0, lockedMarginBottom = 0;
  38. Coord preferredWidth = 0, preferredHeight = 0;
  39. bool locked = false;
  40. void resetItemLockedSize() noexcept
  41. {
  42. lockedWidth = preferredWidth;
  43. lockedHeight = preferredHeight;
  44. lockedMarginLeft = getValueOrZeroIfAuto (item->margin.left);
  45. lockedMarginRight = getValueOrZeroIfAuto (item->margin.right);
  46. lockedMarginTop = getValueOrZeroIfAuto (item->margin.top);
  47. lockedMarginBottom = getValueOrZeroIfAuto (item->margin.bottom);
  48. }
  49. void setWidthChecked (Coord newWidth) noexcept
  50. {
  51. if (isAssigned (item->maxWidth)) newWidth = jmin (newWidth, static_cast<Coord> (item->maxWidth));
  52. if (isAssigned (item->minWidth)) newWidth = jmax (newWidth, static_cast<Coord> (item->minWidth));
  53. lockedWidth = newWidth;
  54. }
  55. void setHeightChecked (Coord newHeight) noexcept
  56. {
  57. if (isAssigned (item->maxHeight)) newHeight = jmin (newHeight, (Coord) item->maxHeight);
  58. if (isAssigned (item->minHeight)) newHeight = jmax (newHeight, (Coord) item->minHeight);
  59. lockedHeight = newHeight;
  60. }
  61. };
  62. struct RowInfo
  63. {
  64. int numItems;
  65. Coord crossSize, lineY, totalLength;
  66. };
  67. FlexBox& owner;
  68. const Coord parentWidth, parentHeight;
  69. const int numItems;
  70. const bool isRowDirection;
  71. const Coord containerLineLength;
  72. int numberOfRows = 1;
  73. Coord containerCrossLength = 0;
  74. HeapBlock<ItemWithState*> lineItems;
  75. HeapBlock<RowInfo> lineInfo;
  76. Array<ItemWithState> itemStates;
  77. ItemWithState& getItem (int x, int y) const noexcept { return *lineItems[y * numItems + x]; }
  78. static bool isAuto (Coord value) noexcept { return value == FlexItem::autoValue; }
  79. static bool isAssigned (Coord value) noexcept { return value != FlexItem::notAssigned; }
  80. static Coord getValueOrZeroIfAuto (Coord value) noexcept { return isAuto (value) ? Coord() : value; }
  81. //==============================================================================
  82. void createStates()
  83. {
  84. itemStates.ensureStorageAllocated (numItems);
  85. for (auto& item : owner.items)
  86. itemStates.add (item);
  87. std::stable_sort (itemStates.begin(), itemStates.end(),
  88. [] (const ItemWithState& i1, const ItemWithState& i2) { return i1.item->order < i2.item->order; });
  89. for (auto& item : itemStates)
  90. {
  91. item.preferredWidth = getPreferredWidth (item);
  92. item.preferredHeight = getPreferredHeight (item);
  93. }
  94. }
  95. void initialiseItems() noexcept
  96. {
  97. if (owner.flexWrap == FlexBox::Wrap::noWrap) // for single-line, all items go in line 1
  98. {
  99. lineInfo[0].numItems = numItems;
  100. int i = 0;
  101. for (auto& item : itemStates)
  102. {
  103. item.resetItemLockedSize();
  104. lineItems[i++] = &item;
  105. }
  106. }
  107. else // if multi-line, group the flexbox items into multiple lines
  108. {
  109. auto currentLength = containerLineLength;
  110. int column = 0, row = 0;
  111. bool firstRow = true;
  112. for (auto& item : itemStates)
  113. {
  114. item.resetItemLockedSize();
  115. const auto flexitemLength = getItemLength (item);
  116. if (flexitemLength > currentLength)
  117. {
  118. if (! firstRow)
  119. row++;
  120. if (row >= numItems)
  121. break;
  122. column = 0;
  123. currentLength = containerLineLength;
  124. numberOfRows = jmax (numberOfRows, row + 1);
  125. }
  126. currentLength -= flexitemLength;
  127. lineItems[row * numItems + column] = &item;
  128. ++column;
  129. lineInfo[row].numItems = jmax (lineInfo[row].numItems, column);
  130. firstRow = false;
  131. }
  132. }
  133. }
  134. void resolveFlexibleLengths() noexcept
  135. {
  136. for (int row = 0; row < numberOfRows; ++row)
  137. {
  138. resetRowItems (row);
  139. for (int maxLoops = numItems; --maxLoops >= 0;)
  140. {
  141. resetUnlockedRowItems (row);
  142. if (layoutRowItems (row))
  143. break;
  144. }
  145. }
  146. }
  147. void resolveAutoMarginsOnMainAxis() noexcept
  148. {
  149. for (int row = 0; row < numberOfRows; ++row)
  150. {
  151. Coord allFlexGrow = 0;
  152. const auto numColumns = lineInfo[row].numItems;
  153. const auto remainingLength = containerLineLength - lineInfo[row].totalLength;
  154. for (int column = 0; column < numColumns; ++column)
  155. {
  156. auto& item = getItem (column, row);
  157. if (isRowDirection)
  158. {
  159. if (isAuto (item.item->margin.left)) ++allFlexGrow;
  160. if (isAuto (item.item->margin.right)) ++allFlexGrow;
  161. }
  162. else
  163. {
  164. if (isAuto (item.item->margin.top)) ++allFlexGrow;
  165. if (isAuto (item.item->margin.bottom)) ++allFlexGrow;
  166. }
  167. }
  168. auto changeUnitWidth = remainingLength / allFlexGrow;
  169. if (changeUnitWidth > 0)
  170. {
  171. for (int column = 0; column < numColumns; ++column)
  172. {
  173. auto& item = getItem (column, row);
  174. if (isRowDirection)
  175. {
  176. if (isAuto (item.item->margin.left)) item.lockedMarginLeft = changeUnitWidth;
  177. if (isAuto (item.item->margin.right)) item.lockedMarginRight = changeUnitWidth;
  178. }
  179. else
  180. {
  181. if (isAuto (item.item->margin.top)) item.lockedMarginTop = changeUnitWidth;
  182. if (isAuto (item.item->margin.bottom)) item.lockedMarginBottom = changeUnitWidth;
  183. }
  184. }
  185. }
  186. }
  187. }
  188. void calculateCrossSizesByLine() noexcept
  189. {
  190. for (int row = 0; row < numberOfRows; ++row)
  191. {
  192. Coord maxSize = 0;
  193. const auto numColumns = lineInfo[row].numItems;
  194. for (int column = 0; column < numColumns; ++column)
  195. {
  196. auto& item = getItem (column, row);
  197. maxSize = jmax (maxSize, isRowDirection ? item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom
  198. : item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight);
  199. }
  200. lineInfo[row].crossSize = maxSize;
  201. }
  202. }
  203. void calculateCrossSizeOfAllItems() noexcept
  204. {
  205. for (int row = 0; row < numberOfRows; ++row)
  206. {
  207. const auto numColumns = lineInfo[row].numItems;
  208. for (int column = 0; column < numColumns; ++column)
  209. {
  210. auto& item = getItem (column, row);
  211. if (isAssigned (item.item->maxHeight) && item.lockedHeight > item.item->maxHeight)
  212. item.lockedHeight = item.item->maxHeight;
  213. if (isAssigned (item.item->maxWidth) && item.lockedWidth > item.item->maxWidth)
  214. item.lockedWidth = item.item->maxWidth;
  215. }
  216. }
  217. }
  218. void alignLinesPerAlignContent() noexcept
  219. {
  220. containerCrossLength = isRowDirection ? parentHeight : parentWidth;
  221. if (owner.alignContent == FlexBox::AlignContent::flexStart)
  222. {
  223. for (int row = 0; row < numberOfRows; ++row)
  224. for (int row2 = row; row2 < numberOfRows; ++row2)
  225. lineInfo[row].lineY = row == 0 ? 0 : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
  226. }
  227. else if (owner.alignContent == FlexBox::AlignContent::flexEnd)
  228. {
  229. for (int row = 0; row < numberOfRows; ++row)
  230. {
  231. Coord crossHeights = 0;
  232. for (int row2 = row; row2 < numberOfRows; ++row2)
  233. crossHeights += lineInfo[row2].crossSize;
  234. lineInfo[row].lineY = containerCrossLength - crossHeights;
  235. }
  236. }
  237. else
  238. {
  239. Coord totalHeight = 0;
  240. for (int row = 0; row < numberOfRows; ++row)
  241. totalHeight += lineInfo[row].crossSize;
  242. if (owner.alignContent == FlexBox::AlignContent::stretch)
  243. {
  244. const auto difference = jmax (Coord(), (containerCrossLength - totalHeight) / numberOfRows);
  245. for (int row = 0; row < numberOfRows; ++row)
  246. {
  247. lineInfo[row].crossSize += difference;
  248. lineInfo[row].lineY = row == 0 ? 0 : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
  249. }
  250. }
  251. else if (owner.alignContent == FlexBox::AlignContent::center)
  252. {
  253. const auto additionalength = (containerCrossLength - totalHeight) / 2;
  254. for (int row = 0; row < numberOfRows; ++row)
  255. lineInfo[row].lineY = row == 0 ? additionalength : lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
  256. }
  257. else if (owner.alignContent == FlexBox::AlignContent::spaceBetween)
  258. {
  259. const auto additionalength = numberOfRows <= 1 ? Coord() : jmax (Coord(), (containerCrossLength - totalHeight)
  260. / static_cast<Coord> (numberOfRows - 1));
  261. lineInfo[0].lineY = 0;
  262. for (int row = 1; row < numberOfRows; ++row)
  263. lineInfo[row].lineY += additionalength + lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
  264. }
  265. else if (owner.alignContent == FlexBox::AlignContent::spaceAround)
  266. {
  267. const auto additionalength = numberOfRows <= 1 ? Coord() : jmax (Coord(), (containerCrossLength - totalHeight)
  268. / static_cast<Coord> (2 + (2 * (numberOfRows - 1))));
  269. lineInfo[0].lineY = additionalength;
  270. for (int row = 1; row < numberOfRows; ++row)
  271. lineInfo[row].lineY += (2 * additionalength) + lineInfo[row - 1].lineY + lineInfo[row - 1].crossSize;
  272. }
  273. }
  274. }
  275. void resolveAutoMarginsOnCrossAxis() noexcept
  276. {
  277. for (int row = 0; row < numberOfRows; ++row)
  278. {
  279. const auto numColumns = lineInfo[row].numItems;
  280. const auto crossSizeForLine = lineInfo[row].crossSize;
  281. for (int column = 0; column < numColumns; ++column)
  282. {
  283. auto& item = getItem (column, row);
  284. if (isRowDirection)
  285. {
  286. if (isAuto (item.item->margin.top) && isAuto (item.item->margin.bottom))
  287. item.lockedMarginTop = (crossSizeForLine - item.lockedHeight) / 2;
  288. else if (isAuto (item.item->margin.top))
  289. item.lockedMarginTop = crossSizeForLine - item.lockedHeight - item.item->margin.bottom;
  290. }
  291. else if (isAuto (item.item->margin.left) && isAuto (item.item->margin.right))
  292. {
  293. item.lockedMarginLeft = jmax (Coord(), (crossSizeForLine - item.lockedWidth) / 2);
  294. }
  295. else if (isAuto (item.item->margin.top))
  296. {
  297. item.lockedMarginLeft = jmax (Coord(), crossSizeForLine - item.lockedHeight - item.item->margin.bottom);
  298. }
  299. }
  300. }
  301. }
  302. void alignItemsInCrossAxisInLinesPerAlignItems() noexcept
  303. {
  304. for (int row = 0; row < numberOfRows; ++row)
  305. {
  306. const auto numColumns = lineInfo[row].numItems;
  307. const auto lineSize = lineInfo[row].crossSize;
  308. for (int column = 0; column < numColumns; ++column)
  309. {
  310. auto& item = getItem (column, row);
  311. if (item.item->alignSelf == FlexItem::AlignSelf::autoAlign)
  312. {
  313. if (owner.alignItems == FlexBox::AlignItems::stretch)
  314. {
  315. item.lockedMarginTop = item.item->margin.top;
  316. if (isRowDirection)
  317. item.setHeightChecked (lineSize - item.item->margin.top - item.item->margin.bottom);
  318. else
  319. item.setWidthChecked (lineSize - item.item->margin.left - item.item->margin.right);
  320. }
  321. else if (owner.alignItems == FlexBox::AlignItems::flexStart)
  322. {
  323. item.lockedMarginTop = item.item->margin.top;
  324. }
  325. else if (owner.alignItems == FlexBox::AlignItems::flexEnd)
  326. {
  327. if (isRowDirection)
  328. item.lockedMarginTop = lineSize - item.lockedHeight - item.item->margin.bottom;
  329. else
  330. item.lockedMarginLeft = lineSize - item.lockedWidth - item.item->margin.right;
  331. }
  332. else if (owner.alignItems == FlexBox::AlignItems::center)
  333. {
  334. if (isRowDirection)
  335. item.lockedMarginTop = (lineSize - item.lockedHeight - item.item->margin.top - item.item->margin.bottom) / 2;
  336. else
  337. item.lockedMarginLeft = (lineSize - item.lockedWidth - item.item->margin.left - item.item->margin.right) / 2;
  338. }
  339. }
  340. }
  341. }
  342. }
  343. void alignLinesPerAlignSelf() noexcept
  344. {
  345. for (int row = 0; row < numberOfRows; ++row)
  346. {
  347. const auto numColumns = lineInfo[row].numItems;
  348. const auto lineSize = lineInfo[row].crossSize;
  349. for (int column = 0; column < numColumns; ++column)
  350. {
  351. auto& item = getItem (column, row);
  352. if (! isAuto (item.item->margin.top))
  353. {
  354. if (item.item->alignSelf == FlexItem::AlignSelf::flexStart)
  355. {
  356. if (isRowDirection)
  357. item.lockedMarginTop = item.item->margin.top;
  358. else
  359. item.lockedMarginLeft = item.item->margin.left;
  360. }
  361. else if (item.item->alignSelf == FlexItem::AlignSelf::flexEnd)
  362. {
  363. if (isRowDirection)
  364. item.lockedMarginTop = lineSize - item.lockedHeight - item.item->margin.bottom;
  365. else
  366. item.lockedMarginLeft = lineSize - item.lockedWidth - item.item->margin.right;
  367. }
  368. else if (item.item->alignSelf == FlexItem::AlignSelf::center)
  369. {
  370. if (isRowDirection)
  371. item.lockedMarginTop = item.item->margin.top + (lineSize - item.lockedHeight - item.item->margin.top - item.item->margin.bottom) / 2;
  372. else
  373. item.lockedMarginLeft = item.item->margin.left + (lineSize - item.lockedWidth - item.item->margin.left - item.item->margin.right) / 2;
  374. }
  375. else if (item.item->alignSelf == FlexItem::AlignSelf::stretch)
  376. {
  377. item.lockedMarginTop = item.item->margin.top;
  378. item.lockedMarginLeft = item.item->margin.left;
  379. if (isRowDirection)
  380. item.setHeightChecked (isAssigned (item.item->height) ? getPreferredHeight (item)
  381. : lineSize - item.item->margin.top - item.item->margin.bottom);
  382. else
  383. item.setWidthChecked (isAssigned (item.item->width) ? getPreferredWidth (item)
  384. : lineSize - item.item->margin.left - item.item->margin.right);
  385. }
  386. }
  387. }
  388. }
  389. }
  390. void alignItemsByJustifyContent() noexcept
  391. {
  392. Coord additionalMarginRight = 0, additionalMarginLeft = 0;
  393. recalculateTotalItemLengthPerLineArray();
  394. for (int row = 0; row < numberOfRows; ++row)
  395. {
  396. const auto numColumns = lineInfo[row].numItems;
  397. Coord x = 0;
  398. if (owner.justifyContent == FlexBox::JustifyContent::flexEnd)
  399. {
  400. x = containerLineLength - lineInfo[row].totalLength;
  401. }
  402. else if (owner.justifyContent == FlexBox::JustifyContent::center)
  403. {
  404. x = (containerLineLength - lineInfo[row].totalLength) / 2;
  405. }
  406. else if (owner.justifyContent == FlexBox::JustifyContent::spaceBetween)
  407. {
  408. additionalMarginRight
  409. = jmax (Coord(), (containerLineLength - lineInfo[row].totalLength) / jmax (1, numColumns - 1));
  410. }
  411. else if (owner.justifyContent == FlexBox::JustifyContent::spaceAround)
  412. {
  413. additionalMarginLeft = additionalMarginRight
  414. = jmax (Coord(), (containerLineLength - lineInfo[row].totalLength) / jmax (1, 2 * numColumns));
  415. }
  416. for (int column = 0; column < numColumns; ++column)
  417. {
  418. auto& item = getItem (column, row);
  419. if (isRowDirection)
  420. {
  421. item.lockedMarginLeft += additionalMarginLeft;
  422. item.lockedMarginRight += additionalMarginRight;
  423. item.item->currentBounds.setPosition ((float) (x + item.lockedMarginLeft), (float) item.lockedMarginTop);
  424. x += item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight;
  425. }
  426. else
  427. {
  428. item.lockedMarginTop += additionalMarginLeft;
  429. item.lockedMarginBottom += additionalMarginRight;
  430. item.item->currentBounds.setPosition ((float) item.lockedMarginLeft, (float) (x + item.lockedMarginTop));
  431. x += item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
  432. }
  433. }
  434. }
  435. }
  436. void layoutAllItems() noexcept
  437. {
  438. for (int row = 0; row < numberOfRows; ++row)
  439. {
  440. const auto lineY = lineInfo[row].lineY;
  441. const auto numColumns = lineInfo[row].numItems;
  442. for (int column = 0; column < numColumns; ++column)
  443. {
  444. auto& item = getItem (column, row);
  445. if (isRowDirection)
  446. item.item->currentBounds.setY ((float) (lineY + item.lockedMarginTop));
  447. else
  448. item.item->currentBounds.setX ((float) (lineY + item.lockedMarginLeft));
  449. item.item->currentBounds.setSize ((float) item.lockedWidth,
  450. (float) item.lockedHeight);
  451. }
  452. }
  453. reverseLocations();
  454. reverseWrap();
  455. }
  456. private:
  457. void resetRowItems (const int row) noexcept
  458. {
  459. const auto numColumns = lineInfo[row].numItems;
  460. for (int column = 0; column < numColumns; ++column)
  461. resetItem (getItem (column, row));
  462. }
  463. void resetUnlockedRowItems (const int row) noexcept
  464. {
  465. const auto numColumns = lineInfo[row].numItems;
  466. for (int column = 0; column < numColumns; ++column)
  467. {
  468. auto& item = getItem (column, row);
  469. if (! item.locked)
  470. resetItem (item);
  471. }
  472. }
  473. void resetItem (ItemWithState& item) noexcept
  474. {
  475. item.locked = false;
  476. item.lockedWidth = getPreferredWidth (item);
  477. item.lockedHeight = getPreferredHeight (item);
  478. }
  479. bool layoutRowItems (const int row) noexcept
  480. {
  481. const auto numColumns = lineInfo[row].numItems;
  482. auto flexContainerLength = containerLineLength;
  483. Coord totalItemsLength = 0, totalFlexGrow = 0, totalFlexShrink = 0;
  484. for (int column = 0; column < numColumns; ++column)
  485. {
  486. const auto& item = getItem (column, row);
  487. if (item.locked)
  488. {
  489. flexContainerLength -= getItemLength (item);
  490. }
  491. else
  492. {
  493. totalItemsLength += getItemLength (item);
  494. totalFlexGrow += item.item->flexGrow;
  495. totalFlexShrink += item.item->flexShrink;
  496. }
  497. }
  498. Coord changeUnit = 0;
  499. const auto difference = flexContainerLength - totalItemsLength;
  500. const bool positiveFlexibility = difference > 0;
  501. if (positiveFlexibility)
  502. {
  503. if (totalFlexGrow != 0.0)
  504. changeUnit = difference / totalFlexGrow;
  505. }
  506. else
  507. {
  508. if (totalFlexShrink != 0.0)
  509. changeUnit = difference / totalFlexShrink;
  510. }
  511. bool ok = true;
  512. for (int column = 0; column < numColumns; ++column)
  513. {
  514. auto& item = getItem (column, row);
  515. if (! item.locked)
  516. if (! addToItemLength (item, (positiveFlexibility ? item.item->flexGrow
  517. : item.item->flexShrink) * changeUnit, row))
  518. ok = false;
  519. }
  520. return ok;
  521. }
  522. void recalculateTotalItemLengthPerLineArray() noexcept
  523. {
  524. for (int row = 0; row < numberOfRows; ++row)
  525. {
  526. lineInfo[row].totalLength = 0;
  527. const auto numColumns = lineInfo[row].numItems;
  528. for (int column = 0; column < numColumns; ++column)
  529. {
  530. const auto& item = getItem (column, row);
  531. lineInfo[row].totalLength += isRowDirection ? item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight
  532. : item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
  533. }
  534. }
  535. }
  536. void reverseLocations() noexcept
  537. {
  538. if (owner.flexDirection == FlexBox::Direction::rowReverse)
  539. {
  540. for (auto& item : owner.items)
  541. item.currentBounds.setX ((float) (containerLineLength - item.currentBounds.getRight()));
  542. }
  543. else if (owner.flexDirection == FlexBox::Direction::columnReverse)
  544. {
  545. for (auto& item : owner.items)
  546. item.currentBounds.setY ((float) (containerLineLength - item.currentBounds.getBottom()));
  547. }
  548. }
  549. void reverseWrap() noexcept
  550. {
  551. if (owner.flexWrap == FlexBox::Wrap::wrapReverse)
  552. {
  553. if (isRowDirection)
  554. {
  555. for (auto& item : owner.items)
  556. item.currentBounds.setY ((float) (containerCrossLength - item.currentBounds.getBottom()));
  557. }
  558. else
  559. {
  560. for (auto& item : owner.items)
  561. item.currentBounds.setX ((float) (containerCrossLength - item.currentBounds.getRight()));
  562. }
  563. }
  564. }
  565. Coord getItemLength (const ItemWithState& item) const noexcept
  566. {
  567. return isRowDirection ? item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight
  568. : item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
  569. }
  570. Coord getItemCrossSize (const ItemWithState& item) const noexcept
  571. {
  572. return isRowDirection ? item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom
  573. : item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight;
  574. }
  575. bool addToItemLength (ItemWithState& item, const Coord length, int row) const noexcept
  576. {
  577. bool ok = false;
  578. if (isRowDirection)
  579. {
  580. const auto prefWidth = getPreferredWidth (item);
  581. if (isAssigned (item.item->maxWidth) && item.item->maxWidth < prefWidth + length)
  582. {
  583. item.lockedWidth = item.item->maxWidth;
  584. item.locked = true;
  585. }
  586. else if (isAssigned (prefWidth) && item.item->minWidth > prefWidth + length)
  587. {
  588. item.lockedWidth = item.item->minWidth;
  589. item.locked = true;
  590. }
  591. else
  592. {
  593. ok = true;
  594. item.lockedWidth = prefWidth + length;
  595. }
  596. lineInfo[row].totalLength += item.lockedWidth + item.lockedMarginLeft + item.lockedMarginRight;
  597. }
  598. else
  599. {
  600. const auto prefHeight = getPreferredHeight (item);
  601. if (isAssigned (item.item->maxHeight) && item.item->maxHeight < prefHeight + length)
  602. {
  603. item.lockedHeight = item.item->maxHeight;
  604. item.locked = true;
  605. }
  606. else if (isAssigned (prefHeight) && item.item->minHeight > prefHeight + length)
  607. {
  608. item.lockedHeight = item.item->minHeight;
  609. item.locked = true;
  610. }
  611. else
  612. {
  613. ok = true;
  614. item.lockedHeight = prefHeight + length;
  615. }
  616. lineInfo[row].totalLength += item.lockedHeight + item.lockedMarginTop + item.lockedMarginBottom;
  617. }
  618. return ok;
  619. }
  620. Coord getPreferredWidth (const ItemWithState& itemWithState) const noexcept
  621. {
  622. const auto& item = *itemWithState.item;
  623. auto preferredWidth = (item.flexBasis > 0 && isRowDirection)
  624. ? item.flexBasis
  625. : (isAssigned (item.width) ? item.width : item.minWidth);
  626. if (isAssigned (item.minWidth) && preferredWidth < item.minWidth) return item.minWidth;
  627. if (isAssigned (item.maxWidth) && preferredWidth > item.maxWidth) return item.maxWidth;
  628. return preferredWidth;
  629. }
  630. Coord getPreferredHeight (const ItemWithState& itemWithState) const noexcept
  631. {
  632. const auto& item = *itemWithState.item;
  633. auto preferredHeight = (item.flexBasis > 0 && ! isRowDirection)
  634. ? item.flexBasis
  635. : (isAssigned (item.height) ? item.height : item.minHeight);
  636. if (isAssigned (item.minHeight) && preferredHeight < item.minHeight) return item.minHeight;
  637. if (isAssigned (item.maxHeight) && preferredHeight > item.maxHeight) return item.maxHeight;
  638. return preferredHeight;
  639. }
  640. };
  641. //==============================================================================
  642. FlexBox::FlexBox() noexcept {}
  643. FlexBox::~FlexBox() noexcept {}
  644. FlexBox::FlexBox (JustifyContent jc) noexcept : justifyContent (jc) {}
  645. FlexBox::FlexBox (Direction d, Wrap w, AlignContent ac, AlignItems ai, JustifyContent jc) noexcept
  646. : flexDirection (d), flexWrap (w), alignContent (ac), alignItems (ai), justifyContent (jc)
  647. {
  648. }
  649. void FlexBox::performLayout (Rectangle<float> targetArea)
  650. {
  651. if (! items.isEmpty())
  652. {
  653. FlexBoxLayoutCalculation layout (*this, targetArea.getWidth(), targetArea.getHeight());
  654. layout.createStates();
  655. layout.initialiseItems();
  656. layout.resolveFlexibleLengths();
  657. layout.resolveAutoMarginsOnMainAxis();
  658. layout.calculateCrossSizesByLine();
  659. layout.calculateCrossSizeOfAllItems();
  660. layout.alignLinesPerAlignContent();
  661. layout.resolveAutoMarginsOnCrossAxis();
  662. layout.alignItemsInCrossAxisInLinesPerAlignItems();
  663. layout.alignLinesPerAlignSelf();
  664. layout.alignItemsByJustifyContent();
  665. layout.layoutAllItems();
  666. for (auto& item : items)
  667. {
  668. item.currentBounds += targetArea.getPosition();
  669. if (auto* comp = item.associatedComponent)
  670. comp->setBounds (Rectangle<int>::leftTopRightBottom ((int) item.currentBounds.getX(),
  671. (int) item.currentBounds.getY(),
  672. (int) item.currentBounds.getRight(),
  673. (int) item.currentBounds.getBottom()));
  674. if (auto* box = item.associatedFlexBox)
  675. box->performLayout (item.currentBounds);
  676. }
  677. }
  678. }
  679. void FlexBox::performLayout (Rectangle<int> targetArea)
  680. {
  681. performLayout (targetArea.toFloat());
  682. }
  683. //==============================================================================
  684. FlexItem::FlexItem() noexcept {}
  685. FlexItem::FlexItem (float w, float h) noexcept : currentBounds (w, h), minWidth (w), minHeight (h) {}
  686. FlexItem::FlexItem (float w, float h, Component& c) noexcept : FlexItem (w, h) { associatedComponent = &c; }
  687. FlexItem::FlexItem (float w, float h, FlexBox& fb) noexcept : FlexItem (w, h) { associatedFlexBox = &fb; }
  688. FlexItem::FlexItem (Component& c) noexcept : associatedComponent (&c) {}
  689. FlexItem::FlexItem (FlexBox& fb) noexcept : associatedFlexBox (&fb) {}
  690. FlexItem::Margin::Margin() noexcept : left(), right(), top(), bottom() {}
  691. FlexItem::Margin::Margin (float v) noexcept : left (v), right (v), top (v), bottom (v) {}
  692. FlexItem::Margin::Margin (float t, float r, float b, float l) noexcept : left (l), right (r), top (t), bottom (b) {}
  693. //==============================================================================
  694. FlexItem FlexItem::withFlex (float newFlexGrow) const noexcept
  695. {
  696. auto fi = *this;
  697. fi.flexGrow = newFlexGrow;
  698. return fi;
  699. }
  700. FlexItem FlexItem::withFlex (float newFlexGrow, float newFlexShrink) const noexcept
  701. {
  702. auto fi = withFlex (newFlexGrow);
  703. fi.flexShrink = newFlexShrink;
  704. return fi;
  705. }
  706. FlexItem FlexItem::withFlex (float newFlexGrow, float newFlexShrink, float newFlexBasis) const noexcept
  707. {
  708. auto fi = withFlex (newFlexGrow, newFlexShrink);
  709. fi.flexBasis = newFlexBasis;
  710. return fi;
  711. }
  712. FlexItem FlexItem::withWidth (float newWidth) const noexcept { auto fi = *this; fi.width = newWidth; return fi; }
  713. FlexItem FlexItem::withMinWidth (float newMinWidth) const noexcept { auto fi = *this; fi.minWidth = newMinWidth; return fi; }
  714. FlexItem FlexItem::withMaxWidth (float newMaxWidth) const noexcept { auto fi = *this; fi.maxWidth = newMaxWidth; return fi; }
  715. FlexItem FlexItem::withMinHeight (float newMinHeight) const noexcept { auto fi = *this; fi.minHeight = newMinHeight; return fi; }
  716. FlexItem FlexItem::withMaxHeight (float newMaxHeight) const noexcept { auto fi = *this; fi.maxHeight = newMaxHeight; return fi; }
  717. FlexItem FlexItem::withHeight (float newHeight) const noexcept { auto fi = *this; fi.height = newHeight; return fi; }
  718. FlexItem FlexItem::withMargin (Margin m) const noexcept { auto fi = *this; fi.margin = m; return fi; }
  719. FlexItem FlexItem::withOrder (int newOrder) const noexcept { auto fi = *this; fi.order = newOrder; return fi; }
  720. FlexItem FlexItem::withAlignSelf (AlignSelf a) const noexcept { auto fi = *this; fi.alignSelf = a; return fi; }
  721. } // namespace juce