The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

848 lines
33KB

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