19ecf9d1cSIngo Weinhold/*
24a8da960SIngo Weinhold * Copyright 2006-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3e439b003SJohn Scipione * Copyright 2014 Haiku, Inc. All rights reserved.
4e439b003SJohn Scipione *
539c1cc12SIngo Weinhold * Distributed under the terms of the MIT License.
6e439b003SJohn Scipione *
7e439b003SJohn Scipione * Authors:
8e439b003SJohn Scipione *		John Scipione, jscipione@gmail.com
9e439b003SJohn Scipione *		Ingo Weinhold, ingo_weinhold@gmx.de
109ecf9d1cSIngo Weinhold */
119ecf9d1cSIngo Weinhold
129ecf9d1cSIngo Weinhold#include <LayoutUtils.h>
139ecf9d1cSIngo Weinhold
146ecc270fSJohn Scipione#include <algorithm>
154a8da960SIngo Weinhold#include <typeinfo>
164a8da960SIngo Weinhold
174a8da960SIngo Weinhold#include <Layout.h>
18dd5ac13bSIngo Weinhold#include <View.h>
19dd5ac13bSIngo Weinhold
204a8da960SIngo Weinhold#include "ViewLayoutItem.h"
214a8da960SIngo Weinhold
229ecf9d1cSIngo Weinhold
239ecf9d1cSIngo Weinhold// // AddSizesFloat
249ecf9d1cSIngo Weinhold// float
259ecf9d1cSIngo Weinhold// BLayoutUtils::AddSizesFloat(float a, float b)
269ecf9d1cSIngo Weinhold// {
279ecf9d1cSIngo Weinhold// 	float sum = a + b + 1;
289ecf9d1cSIngo Weinhold// 	if (sum >= B_SIZE_UNLIMITED)
299ecf9d1cSIngo Weinhold// 		return B_SIZE_UNLIMITED;
3039c1cc12SIngo Weinhold//
319ecf9d1cSIngo Weinhold// 	return sum;
329ecf9d1cSIngo Weinhold// }
3339c1cc12SIngo Weinhold//
349ecf9d1cSIngo Weinhold// // AddSizesFloat
359ecf9d1cSIngo Weinhold// float
369ecf9d1cSIngo Weinhold// BLayoutUtils::AddSizesFloat(float a, float b, float c)
379ecf9d1cSIngo Weinhold// {
389ecf9d1cSIngo Weinhold// 	return AddSizesFloat(AddSizesFloat(a, b), c);
399ecf9d1cSIngo Weinhold// }
409ecf9d1cSIngo Weinhold
41dd5ac13bSIngo Weinhold
429ecf9d1cSIngo Weinhold// AddSizesInt32
439ecf9d1cSIngo Weinholdint32
449ecf9d1cSIngo WeinholdBLayoutUtils::AddSizesInt32(int32 a, int32 b)
459ecf9d1cSIngo Weinhold{
469ecf9d1cSIngo Weinhold	if (a >= B_SIZE_UNLIMITED - b)
479ecf9d1cSIngo Weinhold		return B_SIZE_UNLIMITED;
489ecf9d1cSIngo Weinhold	return a + b;
499ecf9d1cSIngo Weinhold}
509ecf9d1cSIngo Weinhold
51dd5ac13bSIngo Weinhold
529ecf9d1cSIngo Weinhold// AddSizesInt32
539ecf9d1cSIngo Weinholdint32
549ecf9d1cSIngo WeinholdBLayoutUtils::AddSizesInt32(int32 a, int32 b, int32 c)
559ecf9d1cSIngo Weinhold{
569ecf9d1cSIngo Weinhold	return AddSizesInt32(AddSizesInt32(a, b), c);
579ecf9d1cSIngo Weinhold}
589ecf9d1cSIngo Weinhold
59dd5ac13bSIngo Weinhold
609ecf9d1cSIngo Weinhold// AddDistances
619ecf9d1cSIngo Weinholdfloat
629ecf9d1cSIngo WeinholdBLayoutUtils::AddDistances(float a, float b)
639ecf9d1cSIngo Weinhold{
649ecf9d1cSIngo Weinhold	float sum = a + b + 1;
659ecf9d1cSIngo Weinhold	if (sum >= B_SIZE_UNLIMITED)
669ecf9d1cSIngo Weinhold		return B_SIZE_UNLIMITED;
6739c1cc12SIngo Weinhold
689ecf9d1cSIngo Weinhold	return sum;
699ecf9d1cSIngo Weinhold}
709ecf9d1cSIngo Weinhold
71dd5ac13bSIngo Weinhold
729ecf9d1cSIngo Weinhold// AddDistances
739ecf9d1cSIngo Weinholdfloat
749ecf9d1cSIngo WeinholdBLayoutUtils::AddDistances(float a, float b, float c)
759ecf9d1cSIngo Weinhold{
769ecf9d1cSIngo Weinhold	return AddDistances(AddDistances(a, b), c);
779ecf9d1cSIngo Weinhold}
789ecf9d1cSIngo Weinhold
79dd5ac13bSIngo Weinhold
809ecf9d1cSIngo Weinhold// // SubtractSizesFloat
819ecf9d1cSIngo Weinhold// float
829ecf9d1cSIngo Weinhold// BLayoutUtils::SubtractSizesFloat(float a, float b)
839ecf9d1cSIngo Weinhold// {
849ecf9d1cSIngo Weinhold// 	if (a < b)
859ecf9d1cSIngo Weinhold// 		return -1;
869ecf9d1cSIngo Weinhold// 	return a - b - 1;
879ecf9d1cSIngo Weinhold// }
889ecf9d1cSIngo Weinhold
89dd5ac13bSIngo Weinhold
909ecf9d1cSIngo Weinhold// SubtractSizesInt32
919ecf9d1cSIngo Weinholdint32
929ecf9d1cSIngo WeinholdBLayoutUtils::SubtractSizesInt32(int32 a, int32 b)
939ecf9d1cSIngo Weinhold{
949ecf9d1cSIngo Weinhold	if (a < b)
959ecf9d1cSIngo Weinhold		return 0;
969ecf9d1cSIngo Weinhold	return a - b;
979ecf9d1cSIngo Weinhold}
989ecf9d1cSIngo Weinhold
99dd5ac13bSIngo Weinhold
1009ecf9d1cSIngo Weinhold// SubtractDistances
1019ecf9d1cSIngo Weinholdfloat
1029ecf9d1cSIngo WeinholdBLayoutUtils::SubtractDistances(float a, float b)
1039ecf9d1cSIngo Weinhold{
1049ecf9d1cSIngo Weinhold	if (a < b)
1059ecf9d1cSIngo Weinhold		return -1;
1069ecf9d1cSIngo Weinhold	return a - b - 1;
1079ecf9d1cSIngo Weinhold}
1089ecf9d1cSIngo Weinhold
109dd5ac13bSIngo Weinhold
110dd5ac13bSIngo Weinhold// FixSizeConstraints
111dd5ac13bSIngo Weinholdvoid
112dd5ac13bSIngo WeinholdBLayoutUtils::FixSizeConstraints(float& min, float& max, float& preferred)
113dd5ac13bSIngo Weinhold{
114dd5ac13bSIngo Weinhold	if (max < min)
115dd5ac13bSIngo Weinhold		max = min;
116dd5ac13bSIngo Weinhold	if (preferred < min)
117dd5ac13bSIngo Weinhold		preferred = min;
118dd5ac13bSIngo Weinhold	else if (preferred > max)
119dd5ac13bSIngo Weinhold		preferred = max;
120dd5ac13bSIngo Weinhold}
121dd5ac13bSIngo Weinhold
122dd5ac13bSIngo Weinhold
123dd5ac13bSIngo Weinhold// FixSizeConstraints
124dd5ac13bSIngo Weinholdvoid
125dd5ac13bSIngo WeinholdBLayoutUtils::FixSizeConstraints(BSize& min, BSize& max, BSize& preferred)
126dd5ac13bSIngo Weinhold{
127dd5ac13bSIngo Weinhold	FixSizeConstraints(min.width, max.width, preferred.width);
128dd5ac13bSIngo Weinhold	FixSizeConstraints(min.height, max.height, preferred.height);
129dd5ac13bSIngo Weinhold}
130dd5ac13bSIngo Weinhold
131dd5ac13bSIngo Weinhold
13239c1cc12SIngo Weinhold// ComposeSize
1339ecf9d1cSIngo WeinholdBSize
1349ecf9d1cSIngo WeinholdBLayoutUtils::ComposeSize(BSize size, BSize layoutSize)
1359ecf9d1cSIngo Weinhold{
1369ecf9d1cSIngo Weinhold	if (!size.IsWidthSet())
1379ecf9d1cSIngo Weinhold		size.width = layoutSize.width;
1389ecf9d1cSIngo Weinhold	if (!size.IsHeightSet())
1399ecf9d1cSIngo Weinhold		size.height = layoutSize.height;
1409ecf9d1cSIngo Weinhold
1419ecf9d1cSIngo Weinhold	return size;
1429ecf9d1cSIngo Weinhold}
1439ecf9d1cSIngo Weinhold
144dd5ac13bSIngo Weinhold
1459ecf9d1cSIngo Weinhold// ComposeAlignment
1469ecf9d1cSIngo WeinholdBAlignment
1479ecf9d1cSIngo WeinholdBLayoutUtils::ComposeAlignment(BAlignment alignment, BAlignment layoutAlignment)
1489ecf9d1cSIngo Weinhold{
1499ecf9d1cSIngo Weinhold	if (!alignment.IsHorizontalSet())
1509ecf9d1cSIngo Weinhold		alignment.horizontal = layoutAlignment.horizontal;
1519ecf9d1cSIngo Weinhold	if (!alignment.IsVerticalSet())
1529ecf9d1cSIngo Weinhold		alignment.vertical = layoutAlignment.vertical;
1539ecf9d1cSIngo Weinhold
1549ecf9d1cSIngo Weinhold	return alignment;
1559ecf9d1cSIngo Weinhold}
1569ecf9d1cSIngo Weinhold
157dd5ac13bSIngo Weinhold
1589ecf9d1cSIngo Weinhold// AlignInFrame
159e439b003SJohn Scipione// This method restricts the dimensions of the resulting rectangle according
160e439b003SJohn Scipione// to the available size specified by maxSize.
1619ecf9d1cSIngo WeinholdBRect
1629ecf9d1cSIngo WeinholdBLayoutUtils::AlignInFrame(BRect frame, BSize maxSize, BAlignment alignment)
1639ecf9d1cSIngo Weinhold{
1649ecf9d1cSIngo Weinhold	// align according to the given alignment
1659ecf9d1cSIngo Weinhold	if (maxSize.width < frame.Width()
1669ecf9d1cSIngo Weinhold		&& alignment.horizontal != B_ALIGN_USE_FULL_WIDTH) {
1679ecf9d1cSIngo Weinhold		frame.left += (int)((frame.Width() - maxSize.width)
1689ecf9d1cSIngo Weinhold			* alignment.RelativeHorizontal());
1699ecf9d1cSIngo Weinhold		frame.right = frame.left + maxSize.width;
1709ecf9d1cSIngo Weinhold	}
1719ecf9d1cSIngo Weinhold	if (maxSize.height < frame.Height()
1729ecf9d1cSIngo Weinhold		&& alignment.vertical != B_ALIGN_USE_FULL_HEIGHT) {
1739ecf9d1cSIngo Weinhold		frame.top += (int)((frame.Height() - maxSize.height)
1749ecf9d1cSIngo Weinhold			* alignment.RelativeVertical());
1759ecf9d1cSIngo Weinhold		frame.bottom = frame.top + maxSize.height;
1769ecf9d1cSIngo Weinhold	}
1779ecf9d1cSIngo Weinhold
1789ecf9d1cSIngo Weinhold	return frame;
1799ecf9d1cSIngo Weinhold}
1809ecf9d1cSIngo Weinhold
181dd5ac13bSIngo Weinhold
1829ecf9d1cSIngo Weinhold// AlignInFrame
1839ecf9d1cSIngo Weinholdvoid
1849ecf9d1cSIngo WeinholdBLayoutUtils::AlignInFrame(BView* view, BRect frame)
1859ecf9d1cSIngo Weinhold{
18604b48a29SJohn Scipione	BSize maxSize = view->MaxSize();
18704b48a29SJohn Scipione	BAlignment alignment = view->LayoutAlignment();
18804b48a29SJohn Scipione	if (view->HasHeightForWidth()) {
18904b48a29SJohn Scipione		// The view has height for width, so we do the horizontal alignment
19004b48a29SJohn Scipione		// ourselves and restrict the height max constraint respectively.
19104b48a29SJohn Scipione		if (maxSize.width < frame.Width()
19204b48a29SJohn Scipione			&& alignment.horizontal != B_ALIGN_USE_FULL_WIDTH) {
193f0fdd7b9SJohn Scipione			frame.OffsetBy(floorf((frame.Width() - maxSize.width)
19404b48a29SJohn Scipione				* alignment.RelativeHorizontal()), 0);
19504b48a29SJohn Scipione			frame.right = frame.left + maxSize.width;
19604b48a29SJohn Scipione		}
19704b48a29SJohn Scipione		alignment.horizontal = B_ALIGN_USE_FULL_WIDTH;
19804b48a29SJohn Scipione		float minHeight;
19904b48a29SJohn Scipione		float maxHeight;
20004b48a29SJohn Scipione		float preferredHeight;
20104b48a29SJohn Scipione		view->GetHeightForWidth(frame.Width(), &minHeight, &maxHeight,
20204b48a29SJohn Scipione			&preferredHeight);
2036ecc270fSJohn Scipione		frame.bottom = frame.top + std::max(frame.Height(), minHeight);
20404b48a29SJohn Scipione		maxSize.height = minHeight;
20504b48a29SJohn Scipione	}
20604b48a29SJohn Scipione	frame = AlignInFrame(frame, maxSize, alignment);
20704b48a29SJohn Scipione	view->MoveTo(frame.LeftTop());
20804b48a29SJohn Scipione	view->ResizeTo(frame.Size());
2099ecf9d1cSIngo Weinhold}
2109ecf9d1cSIngo Weinhold
21139c1cc12SIngo Weinhold
212e439b003SJohn Scipione// AlignOnRect
213e439b003SJohn Scipione// This method, unlike AlignInFrame(), provides the possibility to return
214e439b003SJohn Scipione// a rectangle with dimensions greater than the available size.
215e439b003SJohn ScipioneBRect
216e439b003SJohn ScipioneBLayoutUtils::AlignOnRect(BRect rect, BSize size, BAlignment alignment)
217e439b003SJohn Scipione{
218e439b003SJohn Scipione	rect.left += (int)((rect.Width() - size.width)
219e439b003SJohn Scipione		* alignment.RelativeHorizontal());
220e439b003SJohn Scipione	rect.top += (int)(((rect.Height() - size.height))
221e439b003SJohn Scipione		* alignment.RelativeVertical());
222e439b003SJohn Scipione	rect.right = rect.left + size.width;
223e439b003SJohn Scipione	rect.bottom = rect.top + size.height;
224e439b003SJohn Scipione
225e439b003SJohn Scipione	return rect;
226e439b003SJohn Scipione}
227e439b003SJohn Scipione
228e439b003SJohn Scipione
22939c1cc12SIngo Weinhold/*!	Offsets a rectangle's location so that it lies fully in a given rectangular
23039c1cc12SIngo Weinhold	frame.
23139c1cc12SIngo Weinhold
23239c1cc12SIngo Weinhold	If the rectangle is too wide/high to fully fit in the frame, its left/top
23339c1cc12SIngo Weinhold	edge is offset to 0. The rect's size always remains unchanged.
23439c1cc12SIngo Weinhold
23539c1cc12SIngo Weinhold	\param rect The rectangle to be moved.
23639c1cc12SIngo Weinhold	\param frameSize The size of the frame the rect shall be moved into. The
23739c1cc12SIngo Weinhold		frame's left-top is (0, 0).
23839c1cc12SIngo Weinhold	\return The modified rect.
23939c1cc12SIngo Weinhold*/
24039c1cc12SIngo Weinhold/*static*/ BRect
24139c1cc12SIngo WeinholdBLayoutUtils::MoveIntoFrame(BRect rect, BSize frameSize)
24239c1cc12SIngo Weinhold{
24339c1cc12SIngo Weinhold	BPoint leftTop(rect.LeftTop());
24439c1cc12SIngo Weinhold
24539c1cc12SIngo Weinhold	// enforce horizontal limits; favor left edge
24639c1cc12SIngo Weinhold	if (rect.right > frameSize.width)
24739c1cc12SIngo Weinhold		leftTop.x -= rect.right - frameSize.width;
248df2b4726SIngo Weinhold	if (leftTop.x < 0)
24939c1cc12SIngo Weinhold		leftTop.x = 0;
25039c1cc12SIngo Weinhold
25139c1cc12SIngo Weinhold	// enforce vertical limits; favor top edge
25239c1cc12SIngo Weinhold	if (rect.bottom > frameSize.height)
25339c1cc12SIngo Weinhold		leftTop.y -= rect.bottom - frameSize.height;
254df2b4726SIngo Weinhold	if (leftTop.y < 0)
25539c1cc12SIngo Weinhold		leftTop.y = 0;
25639c1cc12SIngo Weinhold
25739c1cc12SIngo Weinhold	return rect.OffsetToSelf(leftTop);
25839c1cc12SIngo Weinhold}
2594a8da960SIngo Weinhold
2604a8da960SIngo Weinhold
2614a8da960SIngo Weinhold/*static*/ BString
2624a8da960SIngo WeinholdBLayoutUtils::GetLayoutTreeDump(BView* view)
2634a8da960SIngo Weinhold{
2644a8da960SIngo Weinhold	BString result;
2654a8da960SIngo Weinhold	_GetLayoutTreeDump(view, 0, result);
2664a8da960SIngo Weinhold	return result;
2674a8da960SIngo Weinhold}
2684a8da960SIngo Weinhold
2694a8da960SIngo Weinhold
2704a8da960SIngo Weinhold/*static*/ BString
2714a8da960SIngo WeinholdBLayoutUtils::GetLayoutTreeDump(BLayoutItem* item)
2724a8da960SIngo Weinhold{
2734a8da960SIngo Weinhold	BString result;
2744a8da960SIngo Weinhold	_GetLayoutTreeDump(item, 0, false, result);
2754a8da960SIngo Weinhold	return result;
2764a8da960SIngo Weinhold}
2774a8da960SIngo Weinhold
2784a8da960SIngo Weinhold
2794a8da960SIngo Weinhold/*static*/ void
2804a8da960SIngo WeinholdBLayoutUtils::_GetLayoutTreeDump(BView* view, int level, BString& _output)
2814a8da960SIngo Weinhold{
2824a8da960SIngo Weinhold	BString indent;
2834a8da960SIngo Weinhold	indent.SetTo(' ', level * 2);
2844a8da960SIngo Weinhold
2854a8da960SIngo Weinhold	if (view == NULL) {
2864a8da960SIngo Weinhold		_output << indent << "<null view>\n";
2874a8da960SIngo Weinhold		return;
2884a8da960SIngo Weinhold	}
2894a8da960SIngo Weinhold
2904a8da960SIngo Weinhold	BRect frame = view->Frame();
2914a8da960SIngo Weinhold	BSize min = view->MinSize();
2924a8da960SIngo Weinhold	BSize max = view->MinSize();
2934a8da960SIngo Weinhold	BSize preferred = view->PreferredSize();
2944a8da960SIngo Weinhold	_output << BString().SetToFormat(
2954a8da960SIngo Weinhold		"%sview %p (%s):\n"
2964a8da960SIngo Weinhold		"%s  frame: (%f, %f, %f, %f)\n"
2974a8da960SIngo Weinhold		"%s  min:   (%f, %f)\n"
2984a8da960SIngo Weinhold		"%s  max:   (%f, %f)\n"
2994a8da960SIngo Weinhold		"%s  pref:  (%f, %f)\n",
3004a8da960SIngo Weinhold		indent.String(), view, typeid(*view).name(),
3014a8da960SIngo Weinhold		indent.String(), frame.left, frame.top, frame.right, frame.bottom,
3024a8da960SIngo Weinhold		indent.String(), min.width, min.height,
3034a8da960SIngo Weinhold		indent.String(), max.width, max.height,
3044a8da960SIngo Weinhold		indent.String(), preferred.width, preferred.height);
3054a8da960SIngo Weinhold
3064a8da960SIngo Weinhold	if (BLayout* layout = view->GetLayout()) {
3074a8da960SIngo Weinhold		_GetLayoutTreeDump(layout, level, true, _output);
3084a8da960SIngo Weinhold		return;
3094a8da960SIngo Weinhold	}
3104a8da960SIngo Weinhold
3114a8da960SIngo Weinhold	int32 count = view->CountChildren();
3124a8da960SIngo Weinhold	for (int32 i = 0; i < count; i++) {
3134a8da960SIngo Weinhold		_output << indent << "  ---\n";
3144a8da960SIngo Weinhold		_GetLayoutTreeDump(view->ChildAt(i), level + 1, _output);
3154a8da960SIngo Weinhold	}
3164a8da960SIngo Weinhold}
3174a8da960SIngo Weinhold
3184a8da960SIngo Weinhold
3194a8da960SIngo Weinhold/*static*/ void
3204a8da960SIngo WeinholdBLayoutUtils::_GetLayoutTreeDump(BLayoutItem* item, int level,
3214a8da960SIngo Weinhold	bool isViewLayout, BString& _output)
3224a8da960SIngo Weinhold{
3234a8da960SIngo Weinhold	if (BViewLayoutItem* viewItem = dynamic_cast<BViewLayoutItem*>(item)) {
3244a8da960SIngo Weinhold		_GetLayoutTreeDump(viewItem->View(), level, _output);
3254a8da960SIngo Weinhold		return;
3264a8da960SIngo Weinhold	}
3274a8da960SIngo Weinhold
3284a8da960SIngo Weinhold	BString indent;
3294a8da960SIngo Weinhold	indent.SetTo(' ', level * 2);
3304a8da960SIngo Weinhold
3314a8da960SIngo Weinhold	if (item == NULL) {
3324a8da960SIngo Weinhold		_output << indent << "<null item>\n";
3334a8da960SIngo Weinhold		return;
3344a8da960SIngo Weinhold	}
3354a8da960SIngo Weinhold
3364a8da960SIngo Weinhold	BLayout* layout = dynamic_cast<BLayout*>(item);
3374a8da960SIngo Weinhold	BRect frame = item->Frame();
3384a8da960SIngo Weinhold	BSize min = item->MinSize();
3394a8da960SIngo Weinhold	BSize max = item->MinSize();
3404a8da960SIngo Weinhold	BSize preferred = item->PreferredSize();
3414a8da960SIngo Weinhold	if (isViewLayout) {
3424a8da960SIngo Weinhold		_output << indent << BString().SetToFormat("  [layout %p (%s)]\n",
3434a8da960SIngo Weinhold			layout, typeid(*layout).name());
3444a8da960SIngo Weinhold	} else {
3454a8da960SIngo Weinhold		_output << indent << BString().SetToFormat("item %p (%s):\n",
3464a8da960SIngo Weinhold			item, typeid(*item).name());
3474a8da960SIngo Weinhold	}
3484a8da960SIngo Weinhold	_output << BString().SetToFormat(
3494a8da960SIngo Weinhold		"%s  frame: (%f, %f, %f, %f)\n"
3504a8da960SIngo Weinhold		"%s  min:   (%f, %f)\n"
3514a8da960SIngo Weinhold		"%s  max:   (%f, %f)\n"
3524a8da960SIngo Weinhold		"%s  pref:  (%f, %f)\n",
3534a8da960SIngo Weinhold		indent.String(), frame.left, frame.top, frame.right, frame.bottom,
3544a8da960SIngo Weinhold		indent.String(), min.width, min.height,
3554a8da960SIngo Weinhold		indent.String(), max.width, max.height,
3564a8da960SIngo Weinhold		indent.String(), preferred.width, preferred.height);
3574a8da960SIngo Weinhold
3584a8da960SIngo Weinhold	if (layout == NULL)
3594a8da960SIngo Weinhold		return;
3604a8da960SIngo Weinhold
3614a8da960SIngo Weinhold	int32 count = layout->CountItems();
3624a8da960SIngo Weinhold	for (int32 i = 0; i < count; i++) {
3634a8da960SIngo Weinhold		_output << indent << "  ---\n";
3644a8da960SIngo Weinhold		_GetLayoutTreeDump(layout->ItemAt(i), level + 1, false, _output);
3654a8da960SIngo Weinhold	}
3664a8da960SIngo Weinhold}
367