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 >= (int32)B_SIZE_UNLIMITED - b) 47 return (int32)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