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.

369 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_StretchableLayoutManager.h"
  21. //==============================================================================
  22. StretchableLayoutManager::StretchableLayoutManager()
  23. : totalSize (0)
  24. {
  25. }
  26. StretchableLayoutManager::~StretchableLayoutManager()
  27. {
  28. }
  29. //==============================================================================
  30. void StretchableLayoutManager::clearAllItems()
  31. {
  32. items.clear();
  33. totalSize = 0;
  34. }
  35. void StretchableLayoutManager::setItemLayout (const int itemIndex,
  36. const double minimumSize,
  37. const double maximumSize,
  38. const double preferredSize)
  39. {
  40. ItemLayoutProperties* layout = getInfoFor (itemIndex);
  41. if (layout == 0)
  42. {
  43. layout = new ItemLayoutProperties();
  44. layout->itemIndex = itemIndex;
  45. int i;
  46. for (i = 0; i < items.size(); ++i)
  47. if (items.getUnchecked (i)->itemIndex > itemIndex)
  48. break;
  49. items.insert (i, layout);
  50. }
  51. layout->minSize = minimumSize;
  52. layout->maxSize = maximumSize;
  53. layout->preferredSize = preferredSize;
  54. layout->currentSize = 0;
  55. }
  56. bool StretchableLayoutManager::getItemLayout (const int itemIndex,
  57. double& minimumSize,
  58. double& maximumSize,
  59. double& preferredSize) const
  60. {
  61. const ItemLayoutProperties* const layout = getInfoFor (itemIndex);
  62. if (layout != 0)
  63. {
  64. minimumSize = layout->minSize;
  65. maximumSize = layout->maxSize;
  66. preferredSize = layout->preferredSize;
  67. return true;
  68. }
  69. return false;
  70. }
  71. //==============================================================================
  72. void StretchableLayoutManager::setTotalSize (const int newTotalSize)
  73. {
  74. totalSize = newTotalSize;
  75. fitComponentsIntoSpace (0, items.size(), totalSize, 0);
  76. }
  77. int StretchableLayoutManager::getItemCurrentPosition (const int itemIndex) const
  78. {
  79. int pos = 0;
  80. for (int i = 0; i < itemIndex; ++i)
  81. {
  82. const ItemLayoutProperties* const layout = getInfoFor (i);
  83. if (layout != 0)
  84. pos += layout->currentSize;
  85. }
  86. return pos;
  87. }
  88. int StretchableLayoutManager::getItemCurrentAbsoluteSize (const int itemIndex) const
  89. {
  90. const ItemLayoutProperties* const layout = getInfoFor (itemIndex);
  91. if (layout != 0)
  92. return layout->currentSize;
  93. return 0;
  94. }
  95. double StretchableLayoutManager::getItemCurrentRelativeSize (const int itemIndex) const
  96. {
  97. const ItemLayoutProperties* const layout = getInfoFor (itemIndex);
  98. if (layout != 0)
  99. return -layout->currentSize / (double) totalSize;
  100. return 0;
  101. }
  102. void StretchableLayoutManager::setItemPosition (const int itemIndex,
  103. int newPosition)
  104. {
  105. for (int i = items.size(); --i >= 0;)
  106. {
  107. const ItemLayoutProperties* const layout = items.getUnchecked(i);
  108. if (layout->itemIndex == itemIndex)
  109. {
  110. int realTotalSize = jmax (totalSize, getMinimumSizeOfItems (0, items.size()));
  111. const int minSizeAfterThisComp = getMinimumSizeOfItems (i, items.size());
  112. const int maxSizeAfterThisComp = getMaximumSizeOfItems (i + 1, items.size());
  113. newPosition = jmax (newPosition, totalSize - maxSizeAfterThisComp - layout->currentSize);
  114. newPosition = jmin (newPosition, realTotalSize - minSizeAfterThisComp);
  115. int endPos = fitComponentsIntoSpace (0, i, newPosition, 0);
  116. endPos += layout->currentSize;
  117. fitComponentsIntoSpace (i + 1, items.size(), totalSize - endPos, endPos);
  118. updatePrefSizesToMatchCurrentPositions();
  119. break;
  120. }
  121. }
  122. }
  123. //==============================================================================
  124. void StretchableLayoutManager::layOutComponents (Component** const components,
  125. int numComponents,
  126. int x, int y, int w, int h,
  127. const bool vertically,
  128. const bool resizeOtherDimension)
  129. {
  130. setTotalSize (vertically ? h : w);
  131. int pos = vertically ? y : x;
  132. for (int i = 0; i < numComponents; ++i)
  133. {
  134. const ItemLayoutProperties* const layout = getInfoFor (i);
  135. if (layout != 0)
  136. {
  137. Component* const c = components[i];
  138. if (c != 0)
  139. {
  140. if (i == numComponents - 1)
  141. {
  142. // if it's the last item, crop it to exactly fit the available space..
  143. if (resizeOtherDimension)
  144. {
  145. if (vertically)
  146. c->setBounds (x, pos, w, jmax (layout->currentSize, h - pos));
  147. else
  148. c->setBounds (pos, y, jmax (layout->currentSize, w - pos), h);
  149. }
  150. else
  151. {
  152. if (vertically)
  153. c->setBounds (c->getX(), pos, c->getWidth(), jmax (layout->currentSize, h - pos));
  154. else
  155. c->setBounds (pos, c->getY(), jmax (layout->currentSize, w - pos), c->getHeight());
  156. }
  157. }
  158. else
  159. {
  160. if (resizeOtherDimension)
  161. {
  162. if (vertically)
  163. c->setBounds (x, pos, w, layout->currentSize);
  164. else
  165. c->setBounds (pos, y, layout->currentSize, h);
  166. }
  167. else
  168. {
  169. if (vertically)
  170. c->setBounds (c->getX(), pos, c->getWidth(), layout->currentSize);
  171. else
  172. c->setBounds (pos, c->getY(), layout->currentSize, c->getHeight());
  173. }
  174. }
  175. }
  176. pos += layout->currentSize;
  177. }
  178. }
  179. }
  180. //==============================================================================
  181. StretchableLayoutManager::ItemLayoutProperties* StretchableLayoutManager::getInfoFor (const int itemIndex) const
  182. {
  183. for (int i = items.size(); --i >= 0;)
  184. if (items.getUnchecked(i)->itemIndex == itemIndex)
  185. return items.getUnchecked(i);
  186. return 0;
  187. }
  188. int StretchableLayoutManager::fitComponentsIntoSpace (const int startIndex,
  189. const int endIndex,
  190. const int availableSpace,
  191. int startPos)
  192. {
  193. // calculate the total sizes
  194. int i;
  195. double totalIdealSize = 0.0;
  196. int totalMinimums = 0;
  197. for (i = startIndex; i < endIndex; ++i)
  198. {
  199. ItemLayoutProperties* const layout = items.getUnchecked (i);
  200. layout->currentSize = sizeToRealSize (layout->minSize, totalSize);
  201. totalMinimums += layout->currentSize;
  202. totalIdealSize += sizeToRealSize (layout->preferredSize, availableSpace);
  203. }
  204. if (totalIdealSize <= 0)
  205. totalIdealSize = 1.0;
  206. // now calc the best sizes..
  207. int extraSpace = availableSpace - totalMinimums;
  208. while (extraSpace > 0)
  209. {
  210. int numWantingMoreSpace = 0;
  211. int numHavingTakenExtraSpace = 0;
  212. // first figure out how many comps want a slice of the extra space..
  213. for (i = startIndex; i < endIndex; ++i)
  214. {
  215. ItemLayoutProperties* const layout = items.getUnchecked (i);
  216. double sizeWanted = sizeToRealSize (layout->preferredSize, availableSpace);
  217. const int bestSize = jlimit (layout->currentSize,
  218. jmax (layout->currentSize,
  219. sizeToRealSize (layout->maxSize, totalSize)),
  220. roundToInt (sizeWanted * availableSpace / totalIdealSize));
  221. if (bestSize > layout->currentSize)
  222. ++numWantingMoreSpace;
  223. }
  224. // ..share out the extra space..
  225. for (i = startIndex; i < endIndex; ++i)
  226. {
  227. ItemLayoutProperties* const layout = items.getUnchecked (i);
  228. double sizeWanted = sizeToRealSize (layout->preferredSize, availableSpace);
  229. int bestSize = jlimit (layout->currentSize,
  230. jmax (layout->currentSize, sizeToRealSize (layout->maxSize, totalSize)),
  231. roundToInt (sizeWanted * availableSpace / totalIdealSize));
  232. const int extraWanted = bestSize - layout->currentSize;
  233. if (extraWanted > 0)
  234. {
  235. const int extraAllowed = jmin (extraWanted,
  236. extraSpace / jmax (1, numWantingMoreSpace));
  237. if (extraAllowed > 0)
  238. {
  239. ++numHavingTakenExtraSpace;
  240. --numWantingMoreSpace;
  241. layout->currentSize += extraAllowed;
  242. extraSpace -= extraAllowed;
  243. }
  244. }
  245. }
  246. if (numHavingTakenExtraSpace <= 0)
  247. break;
  248. }
  249. // ..and calculate the end position
  250. for (i = startIndex; i < endIndex; ++i)
  251. {
  252. ItemLayoutProperties* const layout = items.getUnchecked(i);
  253. startPos += layout->currentSize;
  254. }
  255. return startPos;
  256. }
  257. int StretchableLayoutManager::getMinimumSizeOfItems (const int startIndex,
  258. const int endIndex) const
  259. {
  260. int totalMinimums = 0;
  261. for (int i = startIndex; i < endIndex; ++i)
  262. totalMinimums += sizeToRealSize (items.getUnchecked (i)->minSize, totalSize);
  263. return totalMinimums;
  264. }
  265. int StretchableLayoutManager::getMaximumSizeOfItems (const int startIndex, const int endIndex) const
  266. {
  267. int totalMaximums = 0;
  268. for (int i = startIndex; i < endIndex; ++i)
  269. totalMaximums += sizeToRealSize (items.getUnchecked (i)->maxSize, totalSize);
  270. return totalMaximums;
  271. }
  272. void StretchableLayoutManager::updatePrefSizesToMatchCurrentPositions()
  273. {
  274. for (int i = 0; i < items.size(); ++i)
  275. {
  276. ItemLayoutProperties* const layout = items.getUnchecked (i);
  277. layout->preferredSize
  278. = (layout->preferredSize < 0) ? getItemCurrentRelativeSize (i)
  279. : getItemCurrentAbsoluteSize (i);
  280. }
  281. }
  282. int StretchableLayoutManager::sizeToRealSize (double size, int totalSpace)
  283. {
  284. if (size < 0)
  285. size *= -totalSpace;
  286. return roundToInt (size);
  287. }
  288. END_JUCE_NAMESPACE