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