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.

juce_ComponentBoundsConstrainer.cpp 10KB

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