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