1 /* 2 * Copyright 2007-2008, Christof Lutteroth, lutteroth@cs.auckland.ac.nz 3 * Copyright 2007-2008, James Kim, jkim202@ec.auckland.ac.nz 4 * Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de> 5 * Distributed under the terms of the MIT License. 6 */ 7 #include "ALMLayout.h" 8 9 10 #include <vector> 11 12 #include <ControlLook.h> 13 14 #include "ALMGroup.h" 15 #include "RowColumnManager.h" 16 #include "ViewLayoutItem.h" 17 18 19 using namespace LinearProgramming; 20 21 22 const BSize kUnsetSize(B_SIZE_UNSET, B_SIZE_UNSET); 23 24 25 /*! 26 * Constructor. 27 * Creates new layout engine. 28 * 29 * If friendLayout is not NULL the solver of the friend layout is used. 30 */ 31 BALMLayout::BALMLayout(float hSpacing, float vSpacing, BALMLayout* friendLayout) 32 : 33 fLeftInset(0), 34 fRightInset(0), 35 fTopInset(0), 36 fBottomInset(0), 37 fHSpacing(hSpacing), 38 fVSpacing(vSpacing) 39 { 40 fSolver = friendLayout ? friendLayout->Solver() : &fOwnSolver; 41 fRowColumnManager = new RowColumnManager(fSolver); 42 43 fLeft = AddXTab(); 44 fRight = AddXTab(); 45 fTop = AddYTab(); 46 fBottom = AddYTab(); 47 48 // the Left tab is always at x-position 0, and the Top tab is always at 49 // y-position 0 50 fLeft->SetRange(0, 0); 51 fTop->SetRange(0, 0); 52 53 // cached layout values 54 // need to be invalidated whenever the layout specification is changed 55 fMinSize = kUnsetSize; 56 fMaxSize = kUnsetSize; 57 fPreferredSize = kUnsetSize; 58 } 59 60 61 BALMLayout::~BALMLayout() 62 { 63 delete fRowColumnManager; 64 } 65 66 67 /** 68 * Adds a new x-tab to the specification. 69 * 70 * @return the new x-tab 71 */ 72 BReference<XTab> 73 BALMLayout::AddXTab() 74 { 75 BReference<XTab> tab(new(std::nothrow) XTab(this), true); 76 if (!tab) 77 return NULL; 78 if (!fSolver->AddVariable(tab)) 79 return NULL; 80 81 fXTabList.AddItem(tab); 82 return tab; 83 } 84 85 86 void 87 BALMLayout::AddXTabs(BReference<XTab>* tabs, uint32 count) 88 { 89 for (uint32 i = 0; i < count; i++) 90 tabs[i] = AddXTab(); 91 } 92 93 94 void 95 BALMLayout::AddYTabs(BReference<YTab>* tabs, uint32 count) 96 { 97 for (uint32 i = 0; i < count; i++) 98 tabs[i] = AddYTab(); 99 } 100 101 102 /** 103 * Adds a new y-tab to the specification. 104 * 105 * @return the new y-tab 106 */ 107 BReference<YTab> 108 BALMLayout::AddYTab() 109 { 110 BReference<YTab> tab(new(std::nothrow) YTab(this), true); 111 if (tab.Get() == NULL) 112 return NULL; 113 if (!fSolver->AddVariable(tab)) 114 return NULL; 115 116 fYTabList.AddItem(tab); 117 return tab; 118 } 119 120 121 int32 122 BALMLayout::CountXTabs() const 123 { 124 return fXTabList.CountItems(); 125 } 126 127 128 int32 129 BALMLayout::CountYTabs() const 130 { 131 return fYTabList.CountItems(); 132 } 133 134 135 XTab* 136 BALMLayout::XTabAt(int32 index) const 137 { 138 return fXTabList.ItemAt(index); 139 } 140 141 142 YTab* 143 BALMLayout::YTabAt(int32 index) const 144 { 145 return fYTabList.ItemAt(index); 146 } 147 148 149 static int 150 compare_x_tab_func(const XTab* tab1, const XTab* tab2) 151 { 152 if (tab1->Value() < tab2->Value()) 153 return -1; 154 else if (tab1->Value() == tab2->Value()) 155 return 0; 156 return 1; 157 } 158 159 160 static int 161 compare_y_tab_func(const YTab* tab1, const YTab* tab2) 162 { 163 if (tab1->Value() < tab2->Value()) 164 return -1; 165 else if (tab1->Value() == tab2->Value()) 166 return 0; 167 return 1; 168 } 169 170 171 const XTabList& 172 BALMLayout::OrderedXTabs() 173 { 174 fXTabList.SortItems(compare_x_tab_func); 175 return fXTabList; 176 } 177 178 179 const YTabList& 180 BALMLayout::OrderedYTabs() 181 { 182 fYTabList.SortItems(compare_y_tab_func); 183 return fYTabList; 184 } 185 186 187 /** 188 * Adds a new row to the specification that is glued to the given y-tabs. 189 * 190 * @param top 191 * @param bottom 192 * @return the new row 193 */ 194 Row* 195 BALMLayout::AddRow(YTab* _top, YTab* _bottom) 196 { 197 BReference<YTab> top = _top; 198 BReference<YTab> bottom = _bottom; 199 if (_top == NULL) 200 top = AddYTab(); 201 if (_bottom == NULL) 202 bottom = AddYTab(); 203 return new(std::nothrow) Row(fSolver, top, bottom); 204 } 205 206 207 /** 208 * Adds a new column to the specification that is glued to the given x-tabs. 209 * 210 * @param left 211 * @param right 212 * @return the new column 213 */ 214 Column* 215 BALMLayout::AddColumn(XTab* _left, XTab* _right) 216 { 217 BReference<XTab> left = _left; 218 BReference<XTab> right = _right; 219 if (_left == NULL) 220 left = AddXTab(); 221 if (_right == NULL) 222 right = AddXTab(); 223 return new(std::nothrow) Column(fSolver, left, right); 224 } 225 226 227 Area* 228 BALMLayout::AreaFor(int32 id) const 229 { 230 int32 areaCount = CountAreas(); 231 for (int32 i = 0; i < areaCount; i++) { 232 Area* area = AreaAt(i); 233 if (area->ID() == id) 234 return area; 235 } 236 return NULL; 237 } 238 239 240 /** 241 * Finds the area that contains the given control. 242 * 243 * @param control the control to look for 244 * @return the area that contains the control 245 */ 246 Area* 247 BALMLayout::AreaFor(const BView* control) const 248 { 249 return AreaFor(ItemAt(IndexOfView(const_cast<BView*>(control)))); 250 } 251 252 253 Area* 254 BALMLayout::AreaFor(const BLayoutItem* item) const 255 { 256 if (!item) 257 return NULL; 258 return static_cast<Area*>(item->LayoutData()); 259 } 260 261 262 int32 263 BALMLayout::CountAreas() const 264 { 265 return CountItems(); 266 } 267 268 269 Area* 270 BALMLayout::AreaAt(int32 index) const 271 { 272 return AreaFor(ItemAt(index)); 273 } 274 275 276 XTab* 277 BALMLayout::LeftOf(const BView* view) const 278 { 279 Area* area = AreaFor(view); 280 if (!area) 281 return NULL; 282 return area->Left(); 283 } 284 285 286 XTab* 287 BALMLayout::LeftOf(const BLayoutItem* item) const 288 { 289 Area* area = AreaFor(item); 290 if (!area) 291 return NULL; 292 return area->Left(); 293 } 294 295 296 XTab* 297 BALMLayout::RightOf(const BView* view) const 298 { 299 Area* area = AreaFor(view); 300 if (!area) 301 return NULL; 302 return area->Right(); 303 } 304 305 306 XTab* 307 BALMLayout::RightOf(const BLayoutItem* item) const 308 { 309 Area* area = AreaFor(item); 310 if (!area) 311 return NULL; 312 return area->Right(); 313 } 314 315 316 YTab* 317 BALMLayout::TopOf(const BView* view) const 318 { 319 Area* area = AreaFor(view); 320 if (!area) 321 return NULL; 322 return area->Top(); 323 } 324 325 326 YTab* 327 BALMLayout::TopOf(const BLayoutItem* item) const 328 { 329 Area* area = AreaFor(item); 330 if (!area) 331 return NULL; 332 return area->Top(); 333 } 334 335 336 YTab* 337 BALMLayout::BottomOf(const BView* view) const 338 { 339 Area* area = AreaFor(view); 340 if (!area) 341 return NULL; 342 return area->Bottom(); 343 } 344 345 346 YTab* 347 BALMLayout::BottomOf(const BLayoutItem* item) const 348 { 349 Area* area = AreaFor(item); 350 if (!area) 351 return NULL; 352 return area->Bottom(); 353 } 354 355 356 BLayoutItem* 357 BALMLayout::AddView(BView* child) 358 { 359 return AddView(-1, child); 360 } 361 362 363 BLayoutItem* 364 BALMLayout::AddView(int32 index, BView* child) 365 { 366 return BAbstractLayout::AddView(index, child); 367 } 368 369 370 /** 371 * Adds a new area to the specification, automatically setting preferred size constraints. 372 * 373 * @param left left border 374 * @param top top border 375 * @param right right border 376 * @param bottom bottom border 377 * @param content the control which is the area content 378 * @return the new area 379 */ 380 Area* 381 BALMLayout::AddView(BView* view, XTab* left, YTab* top, XTab* right, 382 YTab* bottom) 383 { 384 BLayoutItem* item = _LayoutItemToAdd(view); 385 Area* area = AddItem(item, left, top, right, bottom); 386 if (!area) { 387 if (item != view->GetLayout()) 388 delete item; 389 return NULL; 390 } 391 return area; 392 } 393 394 395 /** 396 * Adds a new area to the specification, automatically setting preferred size constraints. 397 * 398 * @param row the row that defines the top and bottom border 399 * @param column the column that defines the left and right border 400 * @param content the control which is the area content 401 * @return the new area 402 */ 403 Area* 404 BALMLayout::AddView(BView* view, Row* row, Column* column) 405 { 406 BLayoutItem* item = _LayoutItemToAdd(view); 407 Area* area = AddItem(item, row, column); 408 if (!area) { 409 if (item != view->GetLayout()) 410 delete item; 411 return NULL; 412 } 413 return area; 414 } 415 416 417 bool 418 BALMLayout::AddItem(BLayoutItem* item) 419 { 420 return AddItem(-1, item); 421 } 422 423 424 bool 425 BALMLayout::AddItem(int32 index, BLayoutItem* item) 426 { 427 if (!item) 428 return false; 429 430 // simply add the item at the upper right corner of the previous item 431 // TODO maybe find a more elegant solution 432 XTab* left = Left(); 433 YTab* top = Top(); 434 435 // check range 436 if (index < 0 || index > CountItems()) 437 index = CountItems(); 438 439 // for index = 0 we already have set the right tabs 440 if (index != 0) { 441 BLayoutItem* prevItem = ItemAt(index - 1); 442 Area* area = AreaFor(prevItem); 443 if (area) { 444 left = area->Right(); 445 top = area->Top(); 446 } 447 } 448 Area* area = AddItem(item, left, top); 449 return area ? true : false; 450 } 451 452 453 Area* 454 BALMLayout::AddItem(BLayoutItem* item, XTab* left, YTab* top, XTab* _right, 455 YTab* _bottom) 456 { 457 BReference<XTab> right = _right; 458 if (right.Get() == NULL) 459 right = AddXTab(); 460 BReference<YTab> bottom = _bottom; 461 if (bottom.Get() == NULL) 462 bottom = AddYTab(); 463 464 // Area is added int ItemAdded 465 if (!BAbstractLayout::AddItem(-1, item)) 466 return NULL; 467 Area* area = AreaFor(item); 468 if (!area) 469 return NULL; 470 471 area->_Init(fSolver, left, top, right, bottom, fRowColumnManager); 472 473 fRowColumnManager->AddArea(area); 474 return area; 475 } 476 477 478 Area* 479 BALMLayout::AddItem(BLayoutItem* item, Row* row, Column* column) 480 { 481 if (!BAbstractLayout::AddItem(-1, item)) 482 return NULL; 483 Area* area = AreaFor(item); 484 if (!area) 485 return NULL; 486 487 area->_Init(fSolver, row, column, fRowColumnManager); 488 489 fRowColumnManager->AddArea(area); 490 return area; 491 } 492 493 494 enum { 495 kLeftBorderIndex = -2, 496 kTopBorderIndex = -3, 497 kRightBorderIndex = -4, 498 kBottomBorderIndex = -5, 499 }; 500 501 502 bool 503 BALMLayout::SaveLayout(BMessage* archive) const 504 { 505 archive->MakeEmpty(); 506 507 archive->AddInt32("nXTabs", CountXTabs()); 508 archive->AddInt32("nYTabs", CountYTabs()); 509 510 XTabList xTabs = fXTabList; 511 xTabs.RemoveItem(fLeft); 512 xTabs.RemoveItem(fRight); 513 YTabList yTabs = fYTabList; 514 yTabs.RemoveItem(fTop); 515 yTabs.RemoveItem(fBottom); 516 517 int32 nAreas = CountAreas(); 518 for (int32 i = 0; i < nAreas; i++) { 519 Area* area = AreaAt(i); 520 if (area->Left() == fLeft) 521 archive->AddInt32("left", kLeftBorderIndex); 522 else 523 archive->AddInt32("left", xTabs.IndexOf(area->Left())); 524 if (area->Top() == fTop) 525 archive->AddInt32("top", kTopBorderIndex); 526 else 527 archive->AddInt32("top", yTabs.IndexOf(area->Top())); 528 if (area->Right() == fRight) 529 archive->AddInt32("right", kRightBorderIndex); 530 else 531 archive->AddInt32("right", xTabs.IndexOf(area->Right())); 532 if (area->Bottom() == fBottom) 533 archive->AddInt32("bottom", kBottomBorderIndex); 534 else 535 archive->AddInt32("bottom", yTabs.IndexOf(area->Bottom())); 536 } 537 return true; 538 } 539 540 541 bool 542 BALMLayout::RestoreLayout(const BMessage* archive) 543 { 544 int32 neededXTabs; 545 int32 neededYTabs; 546 if (archive->FindInt32("nXTabs", &neededXTabs) != B_OK) 547 return false; 548 if (archive->FindInt32("nYTabs", &neededYTabs) != B_OK) 549 return false; 550 // First store a reference to all needed tabs otherwise they might get lost 551 // while editing the layout 552 std::vector<BReference<XTab> > newXTabs; 553 std::vector<BReference<YTab> > newYTabs; 554 int32 existingXTabs = fXTabList.CountItems(); 555 for (int32 i = 0; i < neededXTabs; i++) { 556 if (i < existingXTabs) 557 newXTabs.push_back(BReference<XTab>(fXTabList.ItemAt(i))); 558 else 559 newXTabs.push_back(AddXTab()); 560 } 561 int32 existingYTabs = fYTabList.CountItems(); 562 for (int32 i = 0; i < neededYTabs; i++) { 563 if (i < existingYTabs) 564 newYTabs.push_back(BReference<YTab>(fYTabList.ItemAt(i))); 565 else 566 newYTabs.push_back(AddYTab()); 567 } 568 569 XTabList xTabs = fXTabList; 570 xTabs.RemoveItem(fLeft); 571 xTabs.RemoveItem(fRight); 572 YTabList yTabs = fYTabList; 573 yTabs.RemoveItem(fTop); 574 yTabs.RemoveItem(fBottom); 575 576 int32 nAreas = CountAreas(); 577 for (int32 i = 0; i < nAreas; i++) { 578 Area* area = AreaAt(i); 579 if (area == NULL) 580 return false; 581 int32 left = -1; 582 if (archive->FindInt32("left", i, &left) != B_OK) 583 break; 584 int32 top = archive->FindInt32("top", i); 585 int32 right = archive->FindInt32("right", i); 586 int32 bottom = archive->FindInt32("bottom", i); 587 588 XTab* leftTab = NULL; 589 YTab* topTab = NULL; 590 XTab* rightTab = NULL; 591 YTab* bottomTab = NULL; 592 593 if (left == kLeftBorderIndex) 594 leftTab = fLeft; 595 else 596 leftTab = xTabs.ItemAt(left); 597 if (top == kTopBorderIndex) 598 topTab = fTop; 599 else 600 topTab = yTabs.ItemAt(top); 601 if (right == kRightBorderIndex) 602 rightTab = fRight; 603 else 604 rightTab = xTabs.ItemAt(right); 605 if (bottom == kBottomBorderIndex) 606 bottomTab = fBottom; 607 else 608 bottomTab = yTabs.ItemAt(bottom); 609 if (leftTab == NULL || topTab == NULL || rightTab == NULL 610 || bottomTab == NULL) 611 return false; 612 613 area->SetLeft(leftTab); 614 area->SetTop(topTab); 615 area->SetRight(rightTab); 616 area->SetBottom(bottomTab); 617 } 618 return true; 619 } 620 621 622 /** 623 * Gets the left variable. 624 */ 625 XTab* 626 BALMLayout::Left() const 627 { 628 return fLeft; 629 } 630 631 632 /** 633 * Gets the right variable. 634 */ 635 XTab* 636 BALMLayout::Right() const 637 { 638 return fRight; 639 } 640 641 642 /** 643 * Gets the top variable. 644 */ 645 YTab* 646 BALMLayout::Top() const 647 { 648 return fTop; 649 } 650 651 652 /** 653 * Gets the bottom variable. 654 */ 655 YTab* 656 BALMLayout::Bottom() const 657 { 658 return fBottom; 659 } 660 661 662 /** 663 * Gets minimum size. 664 */ 665 BSize 666 BALMLayout::BaseMinSize() { 667 if (fMinSize == kUnsetSize) 668 fMinSize = _CalculateMinSize(); 669 return fMinSize; 670 } 671 672 673 /** 674 * Gets maximum size. 675 */ 676 BSize 677 BALMLayout::BaseMaxSize() 678 { 679 if (fMaxSize == kUnsetSize) 680 fMaxSize = _CalculateMaxSize(); 681 return fMaxSize; 682 } 683 684 685 /** 686 * Gets preferred size. 687 */ 688 BSize 689 BALMLayout::BasePreferredSize() 690 { 691 if (fPreferredSize == kUnsetSize) 692 fPreferredSize = _CalculatePreferredSize(); 693 return fPreferredSize; 694 } 695 696 697 /** 698 * Gets the alignment. 699 */ 700 BAlignment 701 BALMLayout::BaseAlignment() 702 { 703 BAlignment alignment; 704 alignment.SetHorizontal(B_ALIGN_HORIZONTAL_CENTER); 705 alignment.SetVertical(B_ALIGN_VERTICAL_CENTER); 706 return alignment; 707 } 708 709 710 /** 711 * Invalidates the layout. 712 * Resets minimum/maximum/preferred size. 713 */ 714 void 715 BALMLayout::LayoutInvalidated(bool children) 716 { 717 fMinSize = kUnsetSize; 718 fMaxSize = kUnsetSize; 719 fPreferredSize = kUnsetSize; 720 } 721 722 723 bool 724 BALMLayout::ItemAdded(BLayoutItem* item, int32 atIndex) 725 { 726 item->SetLayoutData(new(std::nothrow) Area(item)); 727 return item->LayoutData() != NULL; 728 } 729 730 731 void 732 BALMLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex) 733 { 734 if (Area* area = AreaFor(item)) { 735 fRowColumnManager->RemoveArea(area); 736 item->SetLayoutData(NULL); 737 delete area; 738 } 739 } 740 741 742 /** 743 * Calculate and set the layout. 744 * If no layout specification is given, a specification is reverse engineered automatically. 745 */ 746 void 747 BALMLayout::DoLayout() 748 { 749 _UpdateAreaConstraints(); 750 751 // Enforced absolute positions of Right and Bottom 752 BRect area(LayoutArea()); 753 Right()->SetRange(area.right, area.right); 754 Bottom()->SetRange(area.bottom, area.bottom); 755 756 fSolver->Solve(); 757 758 // if new layout is infeasible, use previous layout 759 if (fSolver->Result() == kInfeasible) 760 return; 761 762 if (fSolver->Result() != kOptimal) { 763 fSolver->Save("failed-layout.txt"); 764 printf("Could not solve the layout specification (%d). ", 765 fSolver->Result()); 766 printf("Saved specification in file failed-layout.txt\n"); 767 } 768 769 // set the calculated positions and sizes for every area 770 for (int32 i = 0; i < CountItems(); i++) 771 AreaFor(ItemAt(i))->_DoLayout(); 772 } 773 774 775 LinearSpec* 776 BALMLayout::Solver() const 777 { 778 return const_cast<LinearSpec*>(fSolver); 779 } 780 781 782 void 783 BALMLayout::SetInsets(float left, float top, float right, 784 float bottom) 785 { 786 fLeftInset = BControlLook::ComposeSpacing(left); 787 fTopInset = BControlLook::ComposeSpacing(top); 788 fRightInset = BControlLook::ComposeSpacing(right); 789 fBottomInset = BControlLook::ComposeSpacing(bottom); 790 791 InvalidateLayout(); 792 } 793 794 795 void 796 BALMLayout::SetInsets(float horizontal, float vertical) 797 { 798 fLeftInset = BControlLook::ComposeSpacing(horizontal); 799 fRightInset = fLeftInset; 800 801 fTopInset = BControlLook::ComposeSpacing(vertical); 802 fBottomInset = fTopInset; 803 804 InvalidateLayout(); 805 } 806 807 808 void 809 BALMLayout::SetInsets(float insets) 810 { 811 fLeftInset = BControlLook::ComposeSpacing(insets); 812 fRightInset = fLeftInset; 813 fTopInset = fLeftInset; 814 fBottomInset = fLeftInset; 815 816 InvalidateLayout(); 817 } 818 819 820 void 821 BALMLayout::GetInsets(float* left, float* top, float* right, 822 float* bottom) const 823 { 824 if (left) 825 *left = fLeftInset; 826 if (top) 827 *top = fTopInset; 828 if (right) 829 *right = fRightInset; 830 if (bottom) 831 *bottom = fBottomInset; 832 } 833 834 835 void 836 BALMLayout::SetSpacing(float hSpacing, float vSpacing) 837 { 838 fHSpacing = BControlLook::ComposeSpacing(hSpacing); 839 fVSpacing = BControlLook::ComposeSpacing(vSpacing); 840 } 841 842 843 void 844 BALMLayout::GetSpacing(float *_hSpacing, float *_vSpacing) const 845 { 846 if (_hSpacing) 847 *_hSpacing = fHSpacing; 848 if (_vSpacing) 849 *_vSpacing = fVSpacing; 850 } 851 852 853 float 854 BALMLayout::InsetForTab(XTab* tab) const 855 { 856 if (tab == fLeft.Get()) 857 return fLeftInset; 858 if (tab == fRight.Get()) 859 return fRightInset; 860 return fHSpacing / 2; 861 } 862 863 864 float 865 BALMLayout::InsetForTab(YTab* tab) const 866 { 867 if (tab == fTop.Get()) 868 return fTopInset; 869 if (tab == fBottom.Get()) 870 return fBottomInset; 871 return fVSpacing / 2; 872 } 873 874 875 BLayoutItem* 876 BALMLayout::_LayoutItemToAdd(BView* view) 877 { 878 if (view->GetLayout()) 879 return view->GetLayout(); 880 return new(std::nothrow) BViewLayoutItem(view); 881 } 882 883 884 /** 885 * Caculates the miminum size. 886 */ 887 BSize 888 BALMLayout::_CalculateMinSize() 889 { 890 _UpdateAreaConstraints(); 891 892 Right()->SetRange(0, 20000); 893 Bottom()->SetRange(0, 20000); 894 895 return fSolver->MinSize(Right(), Bottom()); 896 } 897 898 899 /** 900 * Caculates the maximum size. 901 */ 902 BSize 903 BALMLayout::_CalculateMaxSize() 904 { 905 _UpdateAreaConstraints(); 906 907 Right()->SetRange(0, 20000); 908 Bottom()->SetRange(0, 20000); 909 910 return fSolver->MaxSize(Right(), Bottom()); 911 } 912 913 914 /** 915 * Caculates the preferred size. 916 */ 917 BSize 918 BALMLayout::_CalculatePreferredSize() 919 { 920 _UpdateAreaConstraints(); 921 922 Right()->SetRange(0, 20000); 923 Bottom()->SetRange(0, 20000); 924 925 fSolver->Solve(); 926 if (fSolver->Result() != kOptimal) { 927 fSolver->Save("failed-layout.txt"); 928 printf("Could not solve the layout specification (%d). " 929 "Saved specification in file failed-layout.txt", fSolver->Result()); 930 } 931 932 return BSize(Right()->Value() - Left()->Value(), 933 Bottom()->Value() - Top()->Value()); 934 } 935 936 937 void 938 BALMLayout::_UpdateAreaConstraints() 939 { 940 for (int i = 0; i < CountItems(); i++) 941 AreaFor(ItemAt(i))->InvalidateSizeConstraints(); 942 fRowColumnManager->UpdateConstraints(); 943 } 944