1 /*
2 * Copyright 2006-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2014 Haiku, Inc. All rights reserved.
4 *
5 * Distributed under the terms of the MIT License.
6 *
7 * Authors:
8 * John Scipione, jscipione@gmail.com
9 * Ingo Weinhold, ingo_weinhold@gmx.de
10 */
11
12 #include <LayoutUtils.h>
13
14 #include <algorithm>
15
16 #include <ClassInfo.h>
17 #include <Layout.h>
18 #include <View.h>
19
20 #include "ViewLayoutItem.h"
21
22
23 // // AddSizesFloat
24 // float
25 // BLayoutUtils::AddSizesFloat(float a, float b)
26 // {
27 // float sum = a + b + 1;
28 // if (sum >= B_SIZE_UNLIMITED)
29 // return B_SIZE_UNLIMITED;
30 //
31 // return sum;
32 // }
33 //
34 // // AddSizesFloat
35 // float
36 // BLayoutUtils::AddSizesFloat(float a, float b, float c)
37 // {
38 // return AddSizesFloat(AddSizesFloat(a, b), c);
39 // }
40
41
42 // AddSizesInt32
43 int32
AddSizesInt32(int32 a,int32 b)44 BLayoutUtils::AddSizesInt32(int32 a, int32 b)
45 {
46 if (a >= (int32)B_SIZE_UNLIMITED - b)
47 return (int32)B_SIZE_UNLIMITED;
48 return a + b;
49 }
50
51
52 // AddSizesInt32
53 int32
AddSizesInt32(int32 a,int32 b,int32 c)54 BLayoutUtils::AddSizesInt32(int32 a, int32 b, int32 c)
55 {
56 return AddSizesInt32(AddSizesInt32(a, b), c);
57 }
58
59
60 // AddDistances
61 float
AddDistances(float a,float b)62 BLayoutUtils::AddDistances(float a, float b)
63 {
64 float sum = a + b + 1;
65 if (sum >= B_SIZE_UNLIMITED)
66 return B_SIZE_UNLIMITED;
67
68 return sum;
69 }
70
71
72 // AddDistances
73 float
AddDistances(float a,float b,float c)74 BLayoutUtils::AddDistances(float a, float b, float c)
75 {
76 return AddDistances(AddDistances(a, b), c);
77 }
78
79
80 // // SubtractSizesFloat
81 // float
82 // BLayoutUtils::SubtractSizesFloat(float a, float b)
83 // {
84 // if (a < b)
85 // return -1;
86 // return a - b - 1;
87 // }
88
89
90 // SubtractSizesInt32
91 int32
SubtractSizesInt32(int32 a,int32 b)92 BLayoutUtils::SubtractSizesInt32(int32 a, int32 b)
93 {
94 if (a < b)
95 return 0;
96 return a - b;
97 }
98
99
100 // SubtractDistances
101 float
SubtractDistances(float a,float b)102 BLayoutUtils::SubtractDistances(float a, float b)
103 {
104 if (a < b)
105 return -1;
106 return a - b - 1;
107 }
108
109
110 // FixSizeConstraints
111 void
FixSizeConstraints(float & min,float & max,float & preferred)112 BLayoutUtils::FixSizeConstraints(float& min, float& max, float& preferred)
113 {
114 if (max < min)
115 max = min;
116 if (preferred < min)
117 preferred = min;
118 else if (preferred > max)
119 preferred = max;
120 }
121
122
123 // FixSizeConstraints
124 void
FixSizeConstraints(BSize & min,BSize & max,BSize & preferred)125 BLayoutUtils::FixSizeConstraints(BSize& min, BSize& max, BSize& preferred)
126 {
127 FixSizeConstraints(min.width, max.width, preferred.width);
128 FixSizeConstraints(min.height, max.height, preferred.height);
129 }
130
131
132 // ComposeSize
133 BSize
ComposeSize(BSize size,BSize layoutSize)134 BLayoutUtils::ComposeSize(BSize size, BSize layoutSize)
135 {
136 if (!size.IsWidthSet())
137 size.width = layoutSize.width;
138 if (!size.IsHeightSet())
139 size.height = layoutSize.height;
140
141 return size;
142 }
143
144
145 // ComposeAlignment
146 BAlignment
ComposeAlignment(BAlignment alignment,BAlignment layoutAlignment)147 BLayoutUtils::ComposeAlignment(BAlignment alignment, BAlignment layoutAlignment)
148 {
149 if (!alignment.IsHorizontalSet())
150 alignment.horizontal = layoutAlignment.horizontal;
151 if (!alignment.IsVerticalSet())
152 alignment.vertical = layoutAlignment.vertical;
153
154 return alignment;
155 }
156
157
158 // AlignInFrame
159 // This method restricts the dimensions of the resulting rectangle according
160 // to the available size specified by maxSize.
161 BRect
AlignInFrame(BRect frame,BSize maxSize,BAlignment alignment)162 BLayoutUtils::AlignInFrame(BRect frame, BSize maxSize, BAlignment alignment)
163 {
164 // align according to the given alignment
165 if (maxSize.width < frame.Width()
166 && alignment.horizontal != B_ALIGN_USE_FULL_WIDTH) {
167 frame.left += (int)((frame.Width() - maxSize.width)
168 * alignment.RelativeHorizontal());
169 frame.right = frame.left + maxSize.width;
170 }
171 if (maxSize.height < frame.Height()
172 && alignment.vertical != B_ALIGN_USE_FULL_HEIGHT) {
173 frame.top += (int)((frame.Height() - maxSize.height)
174 * alignment.RelativeVertical());
175 frame.bottom = frame.top + maxSize.height;
176 }
177
178 return frame;
179 }
180
181
182 // AlignInFrame
183 void
AlignInFrame(BView * view,BRect frame)184 BLayoutUtils::AlignInFrame(BView* view, BRect frame)
185 {
186 BSize maxSize = view->MaxSize();
187 BAlignment alignment = view->LayoutAlignment();
188 if (view->HasHeightForWidth()) {
189 // The view has height for width, so we do the horizontal alignment
190 // ourselves and restrict the height max constraint respectively.
191 if (maxSize.width < frame.Width()
192 && alignment.horizontal != B_ALIGN_USE_FULL_WIDTH) {
193 frame.OffsetBy(floorf((frame.Width() - maxSize.width)
194 * alignment.RelativeHorizontal()), 0);
195 frame.right = frame.left + maxSize.width;
196 }
197 alignment.horizontal = B_ALIGN_USE_FULL_WIDTH;
198 float minHeight;
199 float maxHeight;
200 float preferredHeight;
201 view->GetHeightForWidth(frame.Width(), &minHeight, &maxHeight,
202 &preferredHeight);
203 frame.bottom = frame.top + std::max(frame.Height(), minHeight);
204 maxSize.height = minHeight;
205 }
206 frame = AlignInFrame(frame, maxSize, alignment);
207 view->MoveTo(frame.LeftTop());
208 view->ResizeTo(frame.Size());
209 }
210
211
212 // AlignOnRect
213 // This method, unlike AlignInFrame(), provides the possibility to return
214 // a rectangle with dimensions greater than the available size.
215 BRect
AlignOnRect(BRect rect,BSize size,BAlignment alignment)216 BLayoutUtils::AlignOnRect(BRect rect, BSize size, BAlignment alignment)
217 {
218 rect.left += (int)((rect.Width() - size.width)
219 * alignment.RelativeHorizontal());
220 rect.top += (int)(((rect.Height() - size.height))
221 * alignment.RelativeVertical());
222 rect.right = rect.left + size.width;
223 rect.bottom = rect.top + size.height;
224
225 return rect;
226 }
227
228
229 /*! Offsets a rectangle's location so that it lies fully in a given rectangular
230 frame.
231
232 If the rectangle is too wide/high to fully fit in the frame, its left/top
233 edge is offset to 0. The rect's size always remains unchanged.
234
235 \param rect The rectangle to be moved.
236 \param frameSize The size of the frame the rect shall be moved into. The
237 frame's left-top is (0, 0).
238 \return The modified rect.
239 */
240 /*static*/ BRect
MoveIntoFrame(BRect rect,BSize frameSize)241 BLayoutUtils::MoveIntoFrame(BRect rect, BSize frameSize)
242 {
243 BPoint leftTop(rect.LeftTop());
244
245 // enforce horizontal limits; favor left edge
246 if (rect.right > frameSize.width)
247 leftTop.x -= rect.right - frameSize.width;
248 if (leftTop.x < 0)
249 leftTop.x = 0;
250
251 // enforce vertical limits; favor top edge
252 if (rect.bottom > frameSize.height)
253 leftTop.y -= rect.bottom - frameSize.height;
254 if (leftTop.y < 0)
255 leftTop.y = 0;
256
257 return rect.OffsetToSelf(leftTop);
258 }
259
260
261 /*static*/ BString
GetLayoutTreeDump(BView * view)262 BLayoutUtils::GetLayoutTreeDump(BView* view)
263 {
264 BString result;
265 _GetLayoutTreeDump(view, 0, result);
266 return result;
267 }
268
269
270 /*static*/ BString
GetLayoutTreeDump(BLayoutItem * item)271 BLayoutUtils::GetLayoutTreeDump(BLayoutItem* item)
272 {
273 BString result;
274 _GetLayoutTreeDump(item, 0, false, result);
275 return result;
276 }
277
278
279 /*static*/ void
_GetLayoutTreeDump(BView * view,int level,BString & _output)280 BLayoutUtils::_GetLayoutTreeDump(BView* view, int level, BString& _output)
281 {
282 BString indent;
283 indent.SetTo(' ', level * 4);
284
285 if (view == NULL) {
286 _output << indent << "<null view>\n";
287 return;
288 }
289
290 BRect frame = view->Frame();
291 BSize min = view->MinSize();
292 BSize max = view->MaxSize();
293 BSize preferred = view->PreferredSize();
294 _output << BString().SetToFormat(
295 "%sview %p (%s %s):\n"
296 "%s frame: (%f, %f, %f, %f)\n"
297 "%s min: (%f, %f)\n"
298 "%s max: (%f, %f)\n"
299 "%s pref: (%f, %f)\n",
300 indent.String(), view, class_name(view), view->Name(),
301 indent.String(), frame.left, frame.top, frame.right, frame.bottom,
302 indent.String(), min.width, min.height,
303 indent.String(), max.width, max.height,
304 indent.String(), preferred.width, preferred.height);
305
306 if (BLayout* layout = view->GetLayout()) {
307 _GetLayoutTreeDump(layout, level, true, _output);
308 return;
309 }
310
311 int32 count = view->CountChildren();
312 for (int32 i = 0; i < count; i++) {
313 _output << indent << " ---\n";
314 _GetLayoutTreeDump(view->ChildAt(i), level + 1, _output);
315 }
316 }
317
318
319 /*static*/ void
_GetLayoutTreeDump(BLayoutItem * item,int level,bool isViewLayout,BString & _output)320 BLayoutUtils::_GetLayoutTreeDump(BLayoutItem* item, int level,
321 bool isViewLayout, BString& _output)
322 {
323 if (BViewLayoutItem* viewItem = dynamic_cast<BViewLayoutItem*>(item)) {
324 _GetLayoutTreeDump(viewItem->View(), level, _output);
325 return;
326 }
327
328 BString indent;
329 indent.SetTo(' ', level * 4);
330
331 if (item == NULL) {
332 _output << indent << "<null item>\n";
333 return;
334 }
335
336 BLayout* layout = dynamic_cast<BLayout*>(item);
337 BRect frame = item->Frame();
338 BSize min = item->MinSize();
339 BSize max = item->MaxSize();
340 BSize preferred = item->PreferredSize();
341 if (isViewLayout) {
342 _output << indent << BString().SetToFormat(" [layout %p (%s)]\n",
343 layout, class_name(layout));
344 } else {
345 _output << indent << BString().SetToFormat("item %p (%s):\n",
346 item, class_name(item));
347 }
348 _output << BString().SetToFormat(
349 "%s frame: (%f, %f, %f, %f)\n"
350 "%s min: (%f, %f)\n"
351 "%s max: (%f, %f)\n"
352 "%s pref: (%f, %f)\n",
353 indent.String(), frame.left, frame.top, frame.right, frame.bottom,
354 indent.String(), min.width, min.height,
355 indent.String(), max.width, max.height,
356 indent.String(), preferred.width, preferred.height);
357
358 if (layout == NULL)
359 return;
360
361 int32 count = layout->CountItems();
362 for (int32 i = 0; i < count; i++) {
363 _output << indent << " ---\n";
364 _GetLayoutTreeDump(layout->ItemAt(i), level + 1, false, _output);
365 }
366 }
367