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.

846 lines
33KB

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