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