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.

853 lines
33KB

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