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.

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