xref: /haiku/src/kits/interface/LayoutUtils.cpp (revision 610f99c838cb661ff85377789ffd3ad4ff672a08)
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
44 BLayoutUtils::AddSizesInt32(int32 a, int32 b)
45 {
46 	if (a >= B_SIZE_UNLIMITED - b)
47 		return B_SIZE_UNLIMITED;
48 	return a + b;
49 }
50 
51 
52 // AddSizesInt32
53 int32
54 BLayoutUtils::AddSizesInt32(int32 a, int32 b, int32 c)
55 {
56 	return AddSizesInt32(AddSizesInt32(a, b), c);
57 }
58 
59 
60 // AddDistances
61 float
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
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
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
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
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
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
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
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
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
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
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
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
262 BLayoutUtils::GetLayoutTreeDump(BView* view)
263 {
264 	BString result;
265 	_GetLayoutTreeDump(view, 0, result);
266 	return result;
267 }
268 
269 
270 /*static*/ BString
271 BLayoutUtils::GetLayoutTreeDump(BLayoutItem* item)
272 {
273 	BString result;
274 	_GetLayoutTreeDump(item, 0, false, result);
275 	return result;
276 }
277 
278 
279 /*static*/ void
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
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