19ecf9d1cSIngo Weinhold /* 24a8da960SIngo Weinhold * Copyright 2006-2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3*e439b003SJohn Scipione * Copyright 2014 Haiku, Inc. All rights reserved. 4*e439b003SJohn Scipione * 539c1cc12SIngo Weinhold * Distributed under the terms of the MIT License. 6*e439b003SJohn Scipione * 7*e439b003SJohn Scipione * Authors: 8*e439b003SJohn Scipione * John Scipione, jscipione@gmail.com 9*e439b003SJohn 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; 309ecf9d1cSIngo Weinhold // 319ecf9d1cSIngo Weinhold // return sum; 329ecf9d1cSIngo Weinhold // } 339ecf9d1cSIngo 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 Weinhold int32 449ecf9d1cSIngo Weinhold BLayoutUtils::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 Weinhold int32 549ecf9d1cSIngo Weinhold BLayoutUtils::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 Weinhold float 629ecf9d1cSIngo Weinhold BLayoutUtils::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; 679ecf9d1cSIngo Weinhold 689ecf9d1cSIngo Weinhold return sum; 699ecf9d1cSIngo Weinhold } 709ecf9d1cSIngo Weinhold 71dd5ac13bSIngo Weinhold 729ecf9d1cSIngo Weinhold // AddDistances 739ecf9d1cSIngo Weinhold float 749ecf9d1cSIngo Weinhold BLayoutUtils::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 Weinhold int32 929ecf9d1cSIngo Weinhold BLayoutUtils::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 Weinhold float 1029ecf9d1cSIngo Weinhold BLayoutUtils::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 Weinhold void 112dd5ac13bSIngo Weinhold BLayoutUtils::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 Weinhold void 125dd5ac13bSIngo Weinhold BLayoutUtils::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 1329ecf9d1cSIngo Weinhold // ComposeSize 1339ecf9d1cSIngo Weinhold BSize 1349ecf9d1cSIngo Weinhold BLayoutUtils::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 Weinhold BAlignment 1479ecf9d1cSIngo Weinhold BLayoutUtils::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 159*e439b003SJohn Scipione // This method restricts the dimensions of the resulting rectangle according 160*e439b003SJohn Scipione // to the available size specified by maxSize. 1619ecf9d1cSIngo Weinhold BRect 1629ecf9d1cSIngo Weinhold BLayoutUtils::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 Weinhold void 1849ecf9d1cSIngo Weinhold BLayoutUtils::AlignInFrame(BView* view, BRect frame) 1859ecf9d1cSIngo Weinhold { 186dd5ac13bSIngo Weinhold BSize maxSize = view->MaxSize(); 18752e06f98SStephan Aßmus BAlignment alignment = view->LayoutAlignment(); 188dd5ac13bSIngo Weinhold if (view->HasHeightForWidth()) { 189dd5ac13bSIngo Weinhold // The view has height for width, so we do the horizontal alignment 190dd5ac13bSIngo Weinhold // ourselves and restrict the height max constraint respectively. 191dd5ac13bSIngo Weinhold if (maxSize.width < frame.Width() 192dd5ac13bSIngo Weinhold && alignment.horizontal != B_ALIGN_USE_FULL_WIDTH) { 193f0fdd7b9SJohn Scipione frame.OffsetBy(floorf((frame.Width() - maxSize.width) 194dd5ac13bSIngo Weinhold * alignment.RelativeHorizontal()), 0); 195dd5ac13bSIngo Weinhold frame.right = frame.left + maxSize.width; 196dd5ac13bSIngo Weinhold } 197dd5ac13bSIngo Weinhold alignment.horizontal = B_ALIGN_USE_FULL_WIDTH; 198dd5ac13bSIngo Weinhold float minHeight; 199dd5ac13bSIngo Weinhold float maxHeight; 200dd5ac13bSIngo Weinhold float preferredHeight; 201dd5ac13bSIngo Weinhold view->GetHeightForWidth(frame.Width(), &minHeight, &maxHeight, 202dd5ac13bSIngo Weinhold &preferredHeight); 2036ecc270fSJohn Scipione frame.bottom = frame.top + std::max(frame.Height(), minHeight); 204dd5ac13bSIngo Weinhold maxSize.height = minHeight; 205dd5ac13bSIngo Weinhold } 206dd5ac13bSIngo Weinhold frame = AlignInFrame(frame, maxSize, alignment); 207dd5ac13bSIngo Weinhold view->MoveTo(frame.LeftTop()); 208dd5ac13bSIngo Weinhold view->ResizeTo(frame.Size()); 2099ecf9d1cSIngo Weinhold } 2109ecf9d1cSIngo Weinhold 21139c1cc12SIngo Weinhold 212*e439b003SJohn Scipione // AlignOnRect 213*e439b003SJohn Scipione // This method, unlike AlignInFrame(), provides the possibility to return 214*e439b003SJohn Scipione // a rectangle with dimensions greater than the available size. 215*e439b003SJohn Scipione BRect 216*e439b003SJohn Scipione BLayoutUtils::AlignOnRect(BRect rect, BSize size, BAlignment alignment) 217*e439b003SJohn Scipione { 218*e439b003SJohn Scipione rect.left += (int)((rect.Width() - size.width) 219*e439b003SJohn Scipione * alignment.RelativeHorizontal()); 220*e439b003SJohn Scipione rect.top += (int)(((rect.Height() - size.height)) 221*e439b003SJohn Scipione * alignment.RelativeVertical()); 222*e439b003SJohn Scipione rect.right = rect.left + size.width; 223*e439b003SJohn Scipione rect.bottom = rect.top + size.height; 224*e439b003SJohn Scipione 225*e439b003SJohn Scipione return rect; 226*e439b003SJohn Scipione } 227*e439b003SJohn Scipione 228*e439b003SJohn 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 Weinhold BLayoutUtils::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 Weinhold BLayoutUtils::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 Weinhold BLayoutUtils::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 Weinhold BLayoutUtils::_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 Weinhold BLayoutUtils::_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