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.

297 lines
10KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - 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 the technical preview this file cannot be licensed commercially.
  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. ComponentBoundsConstrainer::ComponentBoundsConstrainer() noexcept {}
  16. ComponentBoundsConstrainer::~ComponentBoundsConstrainer() {}
  17. //==============================================================================
  18. void ComponentBoundsConstrainer::setMinimumWidth (int minimumWidth) noexcept { minW = minimumWidth; }
  19. void ComponentBoundsConstrainer::setMaximumWidth (int maximumWidth) noexcept { maxW = maximumWidth; }
  20. void ComponentBoundsConstrainer::setMinimumHeight (int minimumHeight) noexcept { minH = minimumHeight; }
  21. void ComponentBoundsConstrainer::setMaximumHeight (int maximumHeight) noexcept { maxH = maximumHeight; }
  22. void ComponentBoundsConstrainer::setMinimumSize (int minimumWidth, int minimumHeight) noexcept
  23. {
  24. jassert (maxW >= minimumWidth);
  25. jassert (maxH >= minimumHeight);
  26. jassert (minimumWidth > 0 && minimumHeight > 0);
  27. minW = minimumWidth;
  28. minH = minimumHeight;
  29. if (minW > maxW) maxW = minW;
  30. if (minH > maxH) maxH = minH;
  31. }
  32. void ComponentBoundsConstrainer::setMaximumSize (int maximumWidth, int maximumHeight) noexcept
  33. {
  34. jassert (maximumWidth >= minW);
  35. jassert (maximumHeight >= minH);
  36. jassert (maximumWidth > 0 && maximumHeight > 0);
  37. maxW = jmax (minW, maximumWidth);
  38. maxH = jmax (minH, maximumHeight);
  39. }
  40. void ComponentBoundsConstrainer::setSizeLimits (int minimumWidth,
  41. int minimumHeight,
  42. int maximumWidth,
  43. int maximumHeight) noexcept
  44. {
  45. jassert (maximumWidth >= minimumWidth);
  46. jassert (maximumHeight >= minimumHeight);
  47. jassert (maximumWidth > 0 && maximumHeight > 0);
  48. jassert (minimumWidth > 0 && minimumHeight > 0);
  49. minW = jmax (0, minimumWidth);
  50. minH = jmax (0, minimumHeight);
  51. maxW = jmax (minW, maximumWidth);
  52. maxH = jmax (minH, maximumHeight);
  53. }
  54. void ComponentBoundsConstrainer::setMinimumOnscreenAmounts (int minimumWhenOffTheTop,
  55. int minimumWhenOffTheLeft,
  56. int minimumWhenOffTheBottom,
  57. int minimumWhenOffTheRight) noexcept
  58. {
  59. minOffTop = minimumWhenOffTheTop;
  60. minOffLeft = minimumWhenOffTheLeft;
  61. minOffBottom = minimumWhenOffTheBottom;
  62. minOffRight = minimumWhenOffTheRight;
  63. }
  64. void ComponentBoundsConstrainer::setFixedAspectRatio (double widthOverHeight) noexcept
  65. {
  66. aspectRatio = jmax (0.0, widthOverHeight);
  67. }
  68. double ComponentBoundsConstrainer::getFixedAspectRatio() const noexcept
  69. {
  70. return aspectRatio;
  71. }
  72. void ComponentBoundsConstrainer::setBoundsForComponent (Component* component,
  73. Rectangle<int> targetBounds,
  74. bool isStretchingTop,
  75. bool isStretchingLeft,
  76. bool isStretchingBottom,
  77. bool isStretchingRight)
  78. {
  79. jassert (component != nullptr);
  80. auto bounds = targetBounds;
  81. auto limits = [&]() -> Rectangle<int>
  82. {
  83. if (auto* parent = component->getParentComponent())
  84. return { parent->getWidth(), parent->getHeight() };
  85. if (auto* display = Desktop::getInstance().getDisplays().getDisplayForPoint (targetBounds.getCentre()))
  86. return component->getLocalArea (nullptr, display->userArea) + component->getPosition();
  87. const auto max = std::numeric_limits<int>::max();
  88. return { max, max };
  89. }();
  90. auto border = [&]() -> BorderSize<int>
  91. {
  92. if (component->getParentComponent() == nullptr)
  93. if (auto* peer = component->getPeer())
  94. if (const auto frameSize = peer->getFrameSizeIfPresent())
  95. return *frameSize;
  96. return {};
  97. }();
  98. border.addTo (bounds);
  99. checkBounds (bounds,
  100. border.addedTo (component->getBounds()), limits,
  101. isStretchingTop, isStretchingLeft,
  102. isStretchingBottom, isStretchingRight);
  103. border.subtractFrom (bounds);
  104. applyBoundsToComponent (*component, bounds);
  105. }
  106. void ComponentBoundsConstrainer::checkComponentBounds (Component* component)
  107. {
  108. setBoundsForComponent (component, component->getBounds(),
  109. false, false, false, false);
  110. }
  111. void ComponentBoundsConstrainer::applyBoundsToComponent (Component& component, Rectangle<int> bounds)
  112. {
  113. if (auto* positioner = component.getPositioner())
  114. positioner->applyNewBounds (bounds);
  115. else
  116. component.setBounds (bounds);
  117. }
  118. //==============================================================================
  119. void ComponentBoundsConstrainer::resizeStart()
  120. {
  121. }
  122. void ComponentBoundsConstrainer::resizeEnd()
  123. {
  124. }
  125. //==============================================================================
  126. void ComponentBoundsConstrainer::checkBounds (Rectangle<int>& bounds,
  127. const Rectangle<int>& old,
  128. const Rectangle<int>& limits,
  129. bool isStretchingTop,
  130. bool isStretchingLeft,
  131. bool isStretchingBottom,
  132. bool isStretchingRight)
  133. {
  134. if (isStretchingLeft)
  135. bounds.setLeft (jlimit (old.getRight() - maxW, old.getRight() - minW, bounds.getX()));
  136. else
  137. bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
  138. if (isStretchingTop)
  139. bounds.setTop (jlimit (old.getBottom() - maxH, old.getBottom() - minH, bounds.getY()));
  140. else
  141. bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
  142. if (bounds.isEmpty())
  143. return;
  144. if (minOffTop > 0)
  145. {
  146. const int limit = limits.getY() + jmin (minOffTop - bounds.getHeight(), 0);
  147. if (bounds.getY() < limit)
  148. {
  149. if (isStretchingTop)
  150. bounds.setTop (limits.getY());
  151. else
  152. bounds.setY (limit);
  153. }
  154. }
  155. if (minOffLeft > 0)
  156. {
  157. const int limit = limits.getX() + jmin (minOffLeft - bounds.getWidth(), 0);
  158. if (bounds.getX() < limit)
  159. {
  160. if (isStretchingLeft)
  161. bounds.setLeft (limits.getX());
  162. else
  163. bounds.setX (limit);
  164. }
  165. }
  166. if (minOffBottom > 0)
  167. {
  168. const int limit = limits.getBottom() - jmin (minOffBottom, bounds.getHeight());
  169. if (bounds.getY() > limit)
  170. {
  171. if (isStretchingBottom)
  172. bounds.setBottom (limits.getBottom());
  173. else
  174. bounds.setY (limit);
  175. }
  176. }
  177. if (minOffRight > 0)
  178. {
  179. const int limit = limits.getRight() - jmin (minOffRight, bounds.getWidth());
  180. if (bounds.getX() > limit)
  181. {
  182. if (isStretchingRight)
  183. bounds.setRight (limits.getRight());
  184. else
  185. bounds.setX (limit);
  186. }
  187. }
  188. // constrain the aspect ratio if one has been specified..
  189. if (aspectRatio > 0.0)
  190. {
  191. bool adjustWidth;
  192. if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
  193. {
  194. adjustWidth = true;
  195. }
  196. else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
  197. {
  198. adjustWidth = false;
  199. }
  200. else
  201. {
  202. const double oldRatio = (old.getHeight() > 0) ? std::abs (old.getWidth() / (double) old.getHeight()) : 0.0;
  203. const double newRatio = std::abs (bounds.getWidth() / (double) bounds.getHeight());
  204. adjustWidth = (oldRatio > newRatio);
  205. }
  206. if (adjustWidth)
  207. {
  208. bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
  209. if (bounds.getWidth() > maxW || bounds.getWidth() < minW)
  210. {
  211. bounds.setWidth (jlimit (minW, maxW, bounds.getWidth()));
  212. bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
  213. }
  214. }
  215. else
  216. {
  217. bounds.setHeight (roundToInt (bounds.getWidth() / aspectRatio));
  218. if (bounds.getHeight() > maxH || bounds.getHeight() < minH)
  219. {
  220. bounds.setHeight (jlimit (minH, maxH, bounds.getHeight()));
  221. bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio));
  222. }
  223. }
  224. if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight))
  225. {
  226. bounds.setX (old.getX() + (old.getWidth() - bounds.getWidth()) / 2);
  227. }
  228. else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom))
  229. {
  230. bounds.setY (old.getY() + (old.getHeight() - bounds.getHeight()) / 2);
  231. }
  232. else
  233. {
  234. if (isStretchingLeft)
  235. bounds.setX (old.getRight() - bounds.getWidth());
  236. if (isStretchingTop)
  237. bounds.setY (old.getBottom() - bounds.getHeight());
  238. }
  239. }
  240. jassert (! bounds.isEmpty());
  241. }
  242. } // namespace juce