1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/views/layout/grid_layout.h" 6 7 #include <algorithm> 8 9 #include "base/logging.h" 10 #include "base/stl_util.h" 11 #include "ui/gfx/insets.h" 12 #include "ui/views/layout/layout_constants.h" 13 #include "ui/views/view.h" 14 #include "ui/views/window/dialog_delegate.h" 15 16 namespace views { 17 18 // LayoutElement ------------------------------------------------------ 19 20 // A LayoutElement has a size and location along one axis. It contains 21 // methods that are used along both axis. 22 class LayoutElement { 23 public: 24 // Invokes ResetSize on all the layout elements. 25 template <class T> 26 static void ResetSizes(std::vector<T*>* elements) { 27 // Reset the layout width of each column. 28 for (typename std::vector<T*>::iterator i = elements->begin(); 29 i != elements->end(); ++i) { 30 (*i)->ResetSize(); 31 } 32 } 33 34 // Sets the location of each element to be the sum of the sizes of the 35 // preceding elements. 36 template <class T> 37 static void CalculateLocationsFromSize(std::vector<T*>* elements) { 38 // Reset the layout width of each column. 39 int location = 0; 40 for (typename std::vector<T*>::iterator i = elements->begin(); 41 i != elements->end(); ++i) { 42 (*i)->SetLocation(location); 43 location += (*i)->Size(); 44 } 45 } 46 47 // Distributes delta among the resizable elements. 48 // Each resizable element is given ResizePercent / total_percent * delta 49 // pixels extra of space. 50 template <class T> 51 static void DistributeDelta(int delta, std::vector<T*>* elements) { 52 if (delta == 0) 53 return; 54 55 float total_percent = 0; 56 int resize_count = 0; 57 for (typename std::vector<T*>::iterator i = elements->begin(); 58 i != elements->end(); ++i) { 59 total_percent += (*i)->ResizePercent(); 60 resize_count++; 61 } 62 if (total_percent == 0) { 63 // None of the elements are resizable, return. 64 return; 65 } 66 int remaining = delta; 67 int resized = resize_count; 68 for (typename std::vector<T*>::iterator i = elements->begin(); 69 i != elements->end(); ++i) { 70 T* element = *i; 71 if (element->ResizePercent() > 0) { 72 int to_give; 73 if (--resized == 0) { 74 to_give = remaining; 75 } else { 76 to_give = static_cast<int>(delta * 77 (element->resize_percent_ / total_percent)); 78 remaining -= to_give; 79 } 80 element->SetSize(element->Size() + to_give); 81 } 82 } 83 } 84 85 // Returns the sum of the size of the elements from start to start + length. 86 template <class T> 87 static int TotalSize(int start, int length, std::vector<T*>* elements) { 88 DCHECK(start >= 0 && length > 0 && 89 start + length <= static_cast<int>(elements->size())); 90 int size = 0; 91 for (int i = start, max = start + length; i < max; ++i) { 92 size += (*elements)[i]->Size(); 93 } 94 return size; 95 } 96 97 explicit LayoutElement(float resize_percent) 98 : resize_percent_(resize_percent) { 99 DCHECK(resize_percent >= 0); 100 } 101 102 virtual ~LayoutElement() {} 103 104 void SetLocation(int location) { 105 location_ = location; 106 } 107 108 int Location() { 109 return location_; 110 } 111 112 // Adjusts the size of this LayoutElement to be the max of the current size 113 // and the specified size. 114 virtual void AdjustSize(int size) { 115 size_ = std::max(size_, size); 116 } 117 118 // Resets the size to the initial size. This sets the size to 0, but 119 // subclasses that have a different initial size should override. 120 virtual void ResetSize() { 121 SetSize(0); 122 } 123 124 void SetSize(int size) { 125 size_ = size; 126 } 127 128 int Size() { 129 return size_; 130 } 131 132 void SetResizePercent(float percent) { 133 resize_percent_ = percent; 134 } 135 136 float ResizePercent() { 137 return resize_percent_; 138 } 139 140 bool IsResizable() { 141 return resize_percent_ > 0; 142 } 143 144 private: 145 float resize_percent_; 146 int location_; 147 int size_; 148 149 DISALLOW_COPY_AND_ASSIGN(LayoutElement); 150 }; 151 152 // Column ------------------------------------------------------------- 153 154 // As the name implies, this represents a Column. Column contains default 155 // values for views originating in this column. 156 class Column : public LayoutElement { 157 public: 158 Column(GridLayout::Alignment h_align, 159 GridLayout::Alignment v_align, 160 float resize_percent, 161 GridLayout::SizeType size_type, 162 int fixed_width, 163 int min_width, 164 size_t index, 165 bool is_padding) 166 : LayoutElement(resize_percent), 167 h_align_(h_align), 168 v_align_(v_align), 169 size_type_(size_type), 170 same_size_column_(-1), 171 fixed_width_(fixed_width), 172 min_width_(min_width), 173 index_(index), 174 is_padding_(is_padding), 175 master_column_(NULL) {} 176 177 virtual ~Column() {} 178 179 GridLayout::Alignment h_align() { return h_align_; } 180 GridLayout::Alignment v_align() { return v_align_; } 181 182 virtual void ResetSize() OVERRIDE; 183 184 private: 185 friend class ColumnSet; 186 friend class GridLayout; 187 188 Column* GetLastMasterColumn(); 189 190 // Determines the max size of all linked columns, and sets each column 191 // to that size. This should only be used for the master column. 192 void UnifySameSizedColumnSizes(); 193 194 virtual void AdjustSize(int size) OVERRIDE; 195 196 const GridLayout::Alignment h_align_; 197 const GridLayout::Alignment v_align_; 198 const GridLayout::SizeType size_type_; 199 int same_size_column_; 200 const int fixed_width_; 201 const int min_width_; 202 203 // Index of this column in the ColumnSet. 204 const size_t index_; 205 206 const bool is_padding_; 207 208 // If multiple columns have their sizes linked, one is the 209 // master column. The master column is identified by the 210 // master_column field being equal to itself. The master columns 211 // same_size_columns field contains the set of Columns with the 212 // the same size. Columns who are linked to other columns, but 213 // are not the master column have their master_column pointing to 214 // one of the other linked columns. Use the method GetLastMasterColumn 215 // to resolve the true master column. 216 std::vector<Column*> same_size_columns_; 217 Column* master_column_; 218 219 DISALLOW_COPY_AND_ASSIGN(Column); 220 }; 221 222 void Column::ResetSize() { 223 if (size_type_ == GridLayout::FIXED) { 224 SetSize(fixed_width_); 225 } else { 226 SetSize(min_width_); 227 } 228 } 229 230 Column* Column::GetLastMasterColumn() { 231 if (master_column_ == NULL) { 232 return NULL; 233 } 234 if (master_column_ == this) { 235 return this; 236 } 237 return master_column_->GetLastMasterColumn(); 238 } 239 240 void Column::UnifySameSizedColumnSizes() { 241 DCHECK(master_column_ == this); 242 243 // Accumulate the size first. 244 int size = 0; 245 for (std::vector<Column*>::iterator i = same_size_columns_.begin(); 246 i != same_size_columns_.end(); ++i) { 247 size = std::max(size, (*i)->Size()); 248 } 249 250 // Then apply it. 251 for (std::vector<Column*>::iterator i = same_size_columns_.begin(); 252 i != same_size_columns_.end(); ++i) { 253 (*i)->SetSize(size); 254 } 255 } 256 257 void Column::AdjustSize(int size) { 258 if (size_type_ == GridLayout::USE_PREF) 259 LayoutElement::AdjustSize(size); 260 } 261 262 // Row ------------------------------------------------------------- 263 264 class Row : public LayoutElement { 265 public: 266 Row(bool fixed_height, int height, float resize_percent, 267 ColumnSet* column_set) 268 : LayoutElement(resize_percent), 269 fixed_height_(fixed_height), 270 height_(height), 271 column_set_(column_set), 272 max_ascent_(0), 273 max_descent_(0) { 274 } 275 276 virtual ~Row() {} 277 278 virtual void ResetSize() OVERRIDE { 279 max_ascent_ = max_descent_ = 0; 280 SetSize(height_); 281 } 282 283 ColumnSet* column_set() { 284 return column_set_; 285 } 286 287 // Adjusts the size to accomodate the specified ascent/descent. 288 void AdjustSizeForBaseline(int ascent, int descent) { 289 max_ascent_ = std::max(ascent, max_ascent_); 290 max_descent_ = std::max(descent, max_descent_); 291 AdjustSize(max_ascent_ + max_descent_); 292 } 293 294 int max_ascent() const { 295 return max_ascent_; 296 } 297 298 int max_descent() const { 299 return max_descent_; 300 } 301 302 private: 303 const bool fixed_height_; 304 const int height_; 305 // The column set used for this row; null for padding rows. 306 ColumnSet* column_set_; 307 308 int max_ascent_; 309 int max_descent_; 310 311 DISALLOW_COPY_AND_ASSIGN(Row); 312 }; 313 314 // ViewState ------------------------------------------------------------- 315 316 // Identifies the location in the grid of a particular view, along with 317 // placement information and size information. 318 struct ViewState { 319 ViewState(ColumnSet* column_set, View* view, int start_col, int start_row, 320 int col_span, int row_span, GridLayout::Alignment h_align, 321 GridLayout::Alignment v_align, int pref_width, int pref_height) 322 : column_set(column_set), 323 view(view), 324 start_col(start_col), 325 start_row(start_row), 326 col_span(col_span), 327 row_span(row_span), 328 h_align(h_align), 329 v_align(v_align), 330 pref_width_fixed(pref_width > 0), 331 pref_height_fixed(pref_height > 0), 332 pref_width(pref_width), 333 pref_height(pref_height), 334 remaining_width(0), 335 remaining_height(0), 336 baseline(-1) { 337 DCHECK(view && start_col >= 0 && start_row >= 0 && col_span > 0 && 338 row_span > 0 && start_col < column_set->num_columns() && 339 (start_col + col_span) <= column_set->num_columns()); 340 } 341 342 ColumnSet* const column_set; 343 View* const view; 344 const int start_col; 345 const int start_row; 346 const int col_span; 347 const int row_span; 348 const GridLayout::Alignment h_align; 349 const GridLayout::Alignment v_align; 350 351 // If true, the pref_width/pref_height were explicitly set and the view's 352 // preferred size is ignored. 353 const bool pref_width_fixed; 354 const bool pref_height_fixed; 355 356 // The preferred width/height. These are reset during the layout process. 357 int pref_width; 358 int pref_height; 359 360 // Used during layout. Gives how much width/height has not yet been 361 // distributed to the columns/rows the view is in. 362 int remaining_width; 363 int remaining_height; 364 365 // The baseline. Only used if the view is vertically aligned along the 366 // baseline. 367 int baseline; 368 }; 369 370 static bool CompareByColumnSpan(const ViewState* v1, const ViewState* v2) { 371 return v1->col_span < v2->col_span; 372 } 373 374 static bool CompareByRowSpan(const ViewState* v1, const ViewState* v2) { 375 return v1->row_span < v2->row_span; 376 } 377 378 // ColumnSet ------------------------------------------------------------- 379 380 ColumnSet::ColumnSet(int id) : id_(id) { 381 } 382 383 ColumnSet::~ColumnSet() { 384 STLDeleteElements(&columns_); 385 } 386 387 void ColumnSet::AddPaddingColumn(float resize_percent, int width) { 388 AddColumn(GridLayout::FILL, GridLayout::FILL, resize_percent, 389 GridLayout::FIXED, width, width, true); 390 } 391 392 void ColumnSet::AddColumn(GridLayout::Alignment h_align, 393 GridLayout::Alignment v_align, 394 float resize_percent, 395 GridLayout::SizeType size_type, 396 int fixed_width, 397 int min_width) { 398 AddColumn(h_align, v_align, resize_percent, size_type, fixed_width, 399 min_width, false); 400 } 401 402 403 void ColumnSet::LinkColumnSizes(int first, ...) { 404 va_list marker; 405 va_start(marker, first); 406 DCHECK(first >= 0 && first < num_columns()); 407 for (int last = first, next = va_arg(marker, int); next != -1; 408 next = va_arg(marker, int)) { 409 DCHECK(next >= 0 && next < num_columns()); 410 columns_[last]->same_size_column_ = next; 411 last = next; 412 } 413 va_end(marker); 414 } 415 416 void ColumnSet::AddColumn(GridLayout::Alignment h_align, 417 GridLayout::Alignment v_align, 418 float resize_percent, 419 GridLayout::SizeType size_type, 420 int fixed_width, 421 int min_width, 422 bool is_padding) { 423 Column* column = new Column(h_align, v_align, resize_percent, size_type, 424 fixed_width, min_width, columns_.size(), 425 is_padding); 426 columns_.push_back(column); 427 } 428 429 void ColumnSet::AddViewState(ViewState* view_state) { 430 // view_states are ordered by column_span (in ascending order). 431 std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(), 432 view_states_.end(), 433 view_state, 434 CompareByColumnSpan); 435 view_states_.insert(i, view_state); 436 } 437 438 void ColumnSet::CalculateMasterColumns() { 439 for (std::vector<Column*>::iterator i = columns_.begin(); 440 i != columns_.end(); ++i) { 441 Column* column = *i; 442 int same_size_column_index = column->same_size_column_; 443 if (same_size_column_index != -1) { 444 DCHECK(same_size_column_index >= 0 && 445 same_size_column_index < static_cast<int>(columns_.size())); 446 Column* master_column = column->master_column_; 447 Column* same_size_column = columns_[same_size_column_index]; 448 Column* same_size_column_master = same_size_column->master_column_; 449 if (master_column == NULL) { 450 // Current column is not linked to any other column. 451 if (same_size_column_master == NULL) { 452 // Both columns are not linked. 453 column->master_column_ = column; 454 same_size_column->master_column_ = column; 455 column->same_size_columns_.push_back(same_size_column); 456 column->same_size_columns_.push_back(column); 457 } else { 458 // Column to link to is linked with other columns. 459 // Add current column to list of linked columns in other columns 460 // master column. 461 same_size_column->GetLastMasterColumn()-> 462 same_size_columns_.push_back(column); 463 // And update the master column for the current column to that 464 // of the same sized column. 465 column->master_column_ = same_size_column; 466 } 467 } else { 468 // Current column is already linked with another column. 469 if (same_size_column_master == NULL) { 470 // Column to link with is not linked to any other columns. 471 // Update it's master_column. 472 same_size_column->master_column_ = column; 473 // Add linked column to list of linked column. 474 column->GetLastMasterColumn()->same_size_columns_. 475 push_back(same_size_column); 476 } else if (column->GetLastMasterColumn() != 477 same_size_column->GetLastMasterColumn()) { 478 // The two columns are already linked with other columns. 479 std::vector<Column*>* same_size_columns = 480 &(column->GetLastMasterColumn()->same_size_columns_); 481 std::vector<Column*>* other_same_size_columns = 482 &(same_size_column->GetLastMasterColumn()->same_size_columns_); 483 // Add all the columns from the others master to current columns 484 // master. 485 same_size_columns->insert(same_size_columns->end(), 486 other_same_size_columns->begin(), 487 other_same_size_columns->end()); 488 // The other master is no longer a master, clear its vector of 489 // linked columns, and reset its master_column. 490 other_same_size_columns->clear(); 491 same_size_column->GetLastMasterColumn()->master_column_ = column; 492 } 493 } 494 } 495 } 496 AccumulateMasterColumns(); 497 } 498 499 void ColumnSet::AccumulateMasterColumns() { 500 DCHECK(master_columns_.empty()); 501 for (std::vector<Column*>::iterator i = columns_.begin(); 502 i != columns_.end(); ++i) { 503 Column* column = *i; 504 Column* master_column = column->GetLastMasterColumn(); 505 if (master_column && 506 find(master_columns_.begin(), master_columns_.end(), 507 master_column) == master_columns_.end()) { 508 master_columns_.push_back(master_column); 509 } 510 // At this point, GetLastMasterColumn may not == master_column 511 // (may have to go through a few Columns)_. Reset master_column to 512 // avoid hops. 513 column->master_column_ = master_column; 514 } 515 } 516 517 void ColumnSet::UnifySameSizedColumnSizes() { 518 for (std::vector<Column*>::iterator i = master_columns_.begin(); 519 i != master_columns_.end(); ++i) { 520 (*i)->UnifySameSizedColumnSizes(); 521 } 522 } 523 524 void ColumnSet::UpdateRemainingWidth(ViewState* view_state) { 525 for (int i = view_state->start_col, 526 max_col = view_state->start_col + view_state->col_span; 527 i < max_col; ++i) { 528 view_state->remaining_width -= columns_[i]->Size(); 529 } 530 } 531 532 void ColumnSet::DistributeRemainingWidth(ViewState* view_state) { 533 // This is nearly the same as that for rows, but differs in so far as how 534 // Rows and Columns are treated. Rows have two states, resizable or not. 535 // Columns have three, resizable, USE_PREF or not resizable. This results 536 // in slightly different handling for distributing unaccounted size. 537 int width = view_state->remaining_width; 538 if (width <= 0) { 539 // The columns this view is in are big enough to accommodate it. 540 return; 541 } 542 543 // Determine which columns are resizable, and which have a size type 544 // of USE_PREF. 545 int resizable_columns = 0; 546 int pref_size_columns = 0; 547 int start_col = view_state->start_col; 548 int max_col = view_state->start_col + view_state->col_span; 549 float total_resize = 0; 550 for (int i = start_col; i < max_col; ++i) { 551 if (columns_[i]->IsResizable()) { 552 total_resize += columns_[i]->ResizePercent(); 553 resizable_columns++; 554 } else if (columns_[i]->size_type_ == GridLayout::USE_PREF) { 555 pref_size_columns++; 556 } 557 } 558 559 if (resizable_columns > 0) { 560 // There are resizable columns, give them the remaining width. The extra 561 // width is distributed using the resize values of each column. 562 int remaining_width = width; 563 for (int i = start_col, resize_i = 0; i < max_col; ++i) { 564 if (columns_[i]->IsResizable()) { 565 resize_i++; 566 int delta = (resize_i == resizable_columns) ? remaining_width : 567 static_cast<int>(width * columns_[i]->ResizePercent() / 568 total_resize); 569 remaining_width -= delta; 570 columns_[i]->SetSize(columns_[i]->Size() + delta); 571 } 572 } 573 } else if (pref_size_columns > 0) { 574 // None of the columns are resizable, distribute the width among those 575 // that use the preferred size. 576 int to_distribute = width / pref_size_columns; 577 for (int i = start_col; i < max_col; ++i) { 578 if (columns_[i]->size_type_ == GridLayout::USE_PREF) { 579 width -= to_distribute; 580 if (width < to_distribute) 581 to_distribute += width; 582 columns_[i]->SetSize(columns_[i]->Size() + to_distribute); 583 } 584 } 585 } 586 } 587 588 int ColumnSet::LayoutWidth() { 589 int width = 0; 590 for (std::vector<Column*>::iterator i = columns_.begin(); 591 i != columns_.end(); ++i) { 592 width += (*i)->Size(); 593 } 594 return width; 595 } 596 597 int ColumnSet::GetColumnWidth(int start_col, int col_span) { 598 return LayoutElement::TotalSize(start_col, col_span, &columns_); 599 } 600 601 void ColumnSet::ResetColumnXCoordinates() { 602 LayoutElement::CalculateLocationsFromSize(&columns_); 603 } 604 605 void ColumnSet::CalculateSize() { 606 gfx::Size pref; 607 // Reset the preferred and remaining sizes. 608 for (std::vector<ViewState*>::iterator i = view_states_.begin(); 609 i != view_states_.end(); ++i) { 610 ViewState* view_state = *i; 611 if (!view_state->pref_width_fixed || !view_state->pref_height_fixed) { 612 pref = view_state->view->GetPreferredSize(); 613 if (!view_state->pref_width_fixed) 614 view_state->pref_width = pref.width(); 615 if (!view_state->pref_height_fixed) 616 view_state->pref_height = pref.height(); 617 } 618 view_state->remaining_width = pref.width(); 619 view_state->remaining_height = pref.height(); 620 } 621 622 // Let layout element reset the sizes for us. 623 LayoutElement::ResetSizes(&columns_); 624 625 // Distribute the size of each view with a col span == 1. 626 std::vector<ViewState*>::iterator view_state_iterator = 627 view_states_.begin(); 628 for (; view_state_iterator != view_states_.end() && 629 (*view_state_iterator)->col_span == 1; ++view_state_iterator) { 630 ViewState* view_state = *view_state_iterator; 631 Column* column = columns_[view_state->start_col]; 632 column->AdjustSize(view_state->pref_width); 633 view_state->remaining_width -= column->Size(); 634 } 635 636 // Make sure all linked columns have the same size. 637 UnifySameSizedColumnSizes(); 638 639 // Distribute the size of each view with a column span > 1. 640 for (; view_state_iterator != view_states_.end(); ++view_state_iterator) { 641 ViewState* view_state = *view_state_iterator; 642 643 // Update the remaining_width from columns this view_state touches. 644 UpdateRemainingWidth(view_state); 645 646 // Distribute the remaining width. 647 DistributeRemainingWidth(view_state); 648 649 // Update the size of linked columns. 650 // This may need to be combined with previous step. 651 UnifySameSizedColumnSizes(); 652 } 653 } 654 655 void ColumnSet::Resize(int delta) { 656 LayoutElement::DistributeDelta(delta, &columns_); 657 } 658 659 // GridLayout ------------------------------------------------------------- 660 661 GridLayout::GridLayout(View* host) 662 : host_(host), 663 calculated_master_columns_(false), 664 remaining_row_span_(0), 665 current_row_(-1), 666 next_column_(0), 667 current_row_col_set_(NULL), 668 adding_view_(false) { 669 DCHECK(host); 670 } 671 672 GridLayout::~GridLayout() { 673 STLDeleteElements(&column_sets_); 674 STLDeleteElements(&view_states_); 675 STLDeleteElements(&rows_); 676 } 677 678 // static 679 GridLayout* GridLayout::CreatePanel(View* host) { 680 GridLayout* layout = new GridLayout(host); 681 layout->SetInsets(kPanelVertMargin, kButtonHEdgeMarginNew, 682 kPanelVertMargin, kButtonHEdgeMarginNew); 683 return layout; 684 } 685 686 void GridLayout::SetInsets(int top, int left, int bottom, int right) { 687 insets_.Set(top, left, bottom, right); 688 } 689 690 void GridLayout::SetInsets(const gfx::Insets& insets) { 691 insets_ = insets; 692 } 693 694 ColumnSet* GridLayout::AddColumnSet(int id) { 695 DCHECK(GetColumnSet(id) == NULL); 696 ColumnSet* column_set = new ColumnSet(id); 697 column_sets_.push_back(column_set); 698 return column_set; 699 } 700 701 ColumnSet* GridLayout::GetColumnSet(int id) { 702 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); 703 i != column_sets_.end(); ++i) { 704 if ((*i)->id_ == id) { 705 return *i; 706 } 707 } 708 return NULL; 709 } 710 711 void GridLayout::StartRowWithPadding(float vertical_resize, int column_set_id, 712 float padding_resize, int padding) { 713 AddPaddingRow(padding_resize, padding); 714 StartRow(vertical_resize, column_set_id); 715 } 716 717 void GridLayout::StartRow(float vertical_resize, int column_set_id) { 718 ColumnSet* column_set = GetColumnSet(column_set_id); 719 DCHECK(column_set); 720 AddRow(new Row(false, 0, vertical_resize, column_set)); 721 } 722 723 void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) { 724 AddRow(new Row(true, pixel_count, vertical_resize, NULL)); 725 } 726 727 void GridLayout::SkipColumns(int col_count) { 728 DCHECK(col_count > 0); 729 next_column_ += col_count; 730 DCHECK(current_row_col_set_ && 731 next_column_ <= current_row_col_set_->num_columns()); 732 SkipPaddingColumns(); 733 } 734 735 void GridLayout::AddView(View* view) { 736 AddView(view, 1, 1); 737 } 738 739 void GridLayout::AddView(View* view, int col_span, int row_span) { 740 DCHECK(current_row_col_set_ && 741 next_column_ < current_row_col_set_->num_columns()); 742 Column* column = current_row_col_set_->columns_[next_column_]; 743 AddView(view, col_span, row_span, column->h_align(), column->v_align()); 744 } 745 746 void GridLayout::AddView(View* view, int col_span, int row_span, 747 Alignment h_align, Alignment v_align) { 748 AddView(view, col_span, row_span, h_align, v_align, 0, 0); 749 } 750 751 void GridLayout::AddView(View* view, int col_span, int row_span, 752 Alignment h_align, Alignment v_align, 753 int pref_width, int pref_height) { 754 DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 && 755 (next_column_ + col_span) <= current_row_col_set_->num_columns()); 756 // We don't support baseline alignment of views spanning rows. Please add if 757 // you need it. 758 DCHECK(v_align != BASELINE || row_span == 1); 759 ViewState* state = 760 new ViewState(current_row_col_set_, view, next_column_, current_row_, 761 col_span, row_span, h_align, v_align, pref_width, 762 pref_height); 763 AddViewState(state); 764 } 765 766 static void CalculateSize(int pref_size, GridLayout::Alignment alignment, 767 int* location, int* size) { 768 if (alignment != GridLayout::FILL) { 769 int available_size = *size; 770 *size = std::min(*size, pref_size); 771 switch (alignment) { 772 case GridLayout::LEADING: 773 // Nothing to do, location already points to start. 774 break; 775 case GridLayout::BASELINE: // If we were asked to align on baseline, but 776 // the view doesn't have a baseline, fall back 777 // to center. 778 case GridLayout::CENTER: 779 *location += (available_size - *size) / 2; 780 break; 781 case GridLayout::TRAILING: 782 *location = *location + available_size - *size; 783 break; 784 default: 785 NOTREACHED(); 786 } 787 } 788 } 789 790 void GridLayout::Installed(View* host) { 791 DCHECK(host_ == host); 792 } 793 794 void GridLayout::Uninstalled(View* host) { 795 DCHECK(host_ == host); 796 } 797 798 void GridLayout::ViewAdded(View* host, View* view) { 799 DCHECK(host_ == host && adding_view_); 800 } 801 802 void GridLayout::ViewRemoved(View* host, View* view) { 803 DCHECK(host_ == host); 804 } 805 806 void GridLayout::Layout(View* host) { 807 DCHECK(host_ == host); 808 // SizeRowsAndColumns sets the size and location of each row/column, but 809 // not of the views. 810 gfx::Size pref; 811 SizeRowsAndColumns(true, host_->width(), host_->height(), &pref); 812 813 // Size each view. 814 for (std::vector<ViewState*>::iterator i = view_states_.begin(); 815 i != view_states_.end(); ++i) { 816 ViewState* view_state = *i; 817 ColumnSet* column_set = view_state->column_set; 818 View* view = (*i)->view; 819 DCHECK(view); 820 int x = column_set->columns_[view_state->start_col]->Location() + 821 insets_.left(); 822 int width = column_set->GetColumnWidth(view_state->start_col, 823 view_state->col_span); 824 CalculateSize(view_state->pref_width, view_state->h_align, 825 &x, &width); 826 int y = rows_[view_state->start_row]->Location() + insets_.top(); 827 int height = LayoutElement::TotalSize(view_state->start_row, 828 view_state->row_span, &rows_); 829 if (view_state->v_align == BASELINE && view_state->baseline != -1) { 830 y += rows_[view_state->start_row]->max_ascent() - view_state->baseline; 831 height = view_state->pref_height; 832 } else { 833 CalculateSize(view_state->pref_height, view_state->v_align, &y, &height); 834 } 835 view->SetBounds(x, y, width, height); 836 } 837 } 838 839 gfx::Size GridLayout::GetPreferredSize(View* host) { 840 DCHECK(host_ == host); 841 gfx::Size out; 842 SizeRowsAndColumns(false, 0, 0, &out); 843 out.SetSize(std::max(out.width(), minimum_size_.width()), 844 std::max(out.height(), minimum_size_.height())); 845 return out; 846 } 847 848 int GridLayout::GetPreferredHeightForWidth(View* host, int width) { 849 DCHECK(host_ == host); 850 gfx::Size pref; 851 SizeRowsAndColumns(false, width, 0, &pref); 852 return pref.height(); 853 } 854 855 void GridLayout::SizeRowsAndColumns(bool layout, int width, int height, 856 gfx::Size* pref) { 857 // Make sure the master columns have been calculated. 858 CalculateMasterColumnsIfNecessary(); 859 pref->SetSize(0, 0); 860 if (rows_.empty()) 861 return; 862 863 // Calculate the preferred width of each of the columns. Some views' 864 // preferred heights are derived from their width, as such we need to 865 // calculate the size of the columns first. 866 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); 867 i != column_sets_.end(); ++i) { 868 (*i)->CalculateSize(); 869 pref->set_width(std::max(pref->width(), (*i)->LayoutWidth())); 870 } 871 pref->set_width(pref->width() + insets_.width()); 872 873 // Go over the columns again and set them all to the size we settled for. 874 width = width ? width : pref->width(); 875 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); 876 i != column_sets_.end(); ++i) { 877 // We're doing a layout, divy up any extra space. 878 (*i)->Resize(width - (*i)->LayoutWidth() - insets_.left() - 879 insets_.right()); 880 // And reset the x coordinates. 881 (*i)->ResetColumnXCoordinates(); 882 } 883 884 // Reset the height of each row. 885 LayoutElement::ResetSizes(&rows_); 886 887 // Do the following: 888 // . If the view is aligned along it's baseline, obtain the baseline from the 889 // view and update the rows ascent/descent. 890 // . Reset the remaining_height of each view state. 891 // . If the width the view will be given is different than it's pref, ask 892 // for the height given a particularly width. 893 for (std::vector<ViewState*>::iterator i= view_states_.begin(); 894 i != view_states_.end() ; ++i) { 895 ViewState* view_state = *i; 896 view_state->remaining_height = view_state->pref_height; 897 898 if (view_state->v_align == BASELINE) 899 view_state->baseline = view_state->view->GetBaseline(); 900 901 if (view_state->h_align == FILL) { 902 // The view is resizable. As the pref height may vary with the width, 903 // ask for the pref again. 904 int actual_width = 905 view_state->column_set->GetColumnWidth(view_state->start_col, 906 view_state->col_span); 907 if (actual_width != view_state->pref_width && 908 !view_state->pref_height_fixed) { 909 // The width this view will get differs from its preferred. Some Views 910 // pref height varies with its width; ask for the preferred again. 911 view_state->pref_height = 912 view_state->view->GetHeightForWidth(actual_width); 913 view_state->remaining_height = view_state->pref_height; 914 } 915 } 916 } 917 918 // Update the height/ascent/descent of each row from the views. 919 std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin(); 920 for (; view_states_iterator != view_states_.end() && 921 (*view_states_iterator)->row_span == 1; ++view_states_iterator) { 922 ViewState* view_state = *view_states_iterator; 923 Row* row = rows_[view_state->start_row]; 924 row->AdjustSize(view_state->remaining_height); 925 if (view_state->baseline != -1 && 926 view_state->baseline <= view_state->pref_height) { 927 row->AdjustSizeForBaseline(view_state->baseline, 928 view_state->pref_height - view_state->baseline); 929 } 930 view_state->remaining_height = 0; 931 } 932 933 // Distribute the height of each view with a row span > 1. 934 for (; view_states_iterator != view_states_.end(); ++view_states_iterator) { 935 ViewState* view_state = *view_states_iterator; 936 937 // Update the remaining_width from columns this view_state touches. 938 UpdateRemainingHeightFromRows(view_state); 939 940 // Distribute the remaining height. 941 DistributeRemainingHeight(view_state); 942 } 943 944 // Update the location of each of the rows. 945 LayoutElement::CalculateLocationsFromSize(&rows_); 946 947 // We now know the preferred height, set it here. 948 pref->set_height(rows_[rows_.size() - 1]->Location() + 949 rows_[rows_.size() - 1]->Size() + insets_.height()); 950 951 if (layout && height != pref->height()) { 952 // We're doing a layout, and the height differs from the preferred height, 953 // divy up the extra space. 954 LayoutElement::DistributeDelta(height - pref->height(), &rows_); 955 956 // Reset y locations. 957 LayoutElement::CalculateLocationsFromSize(&rows_); 958 } 959 } 960 961 void GridLayout::CalculateMasterColumnsIfNecessary() { 962 if (!calculated_master_columns_) { 963 calculated_master_columns_ = true; 964 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); 965 i != column_sets_.end(); ++i) { 966 (*i)->CalculateMasterColumns(); 967 } 968 } 969 } 970 971 void GridLayout::AddViewState(ViewState* view_state) { 972 DCHECK(view_state->view && (view_state->view->parent() == NULL || 973 view_state->view->parent() == host_)); 974 if (!view_state->view->parent()) { 975 adding_view_ = true; 976 host_->AddChildView(view_state->view); 977 adding_view_ = false; 978 } 979 remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span); 980 next_column_ += view_state->col_span; 981 current_row_col_set_->AddViewState(view_state); 982 // view_states are ordered by row_span (in ascending order). 983 std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(), 984 view_states_.end(), 985 view_state, 986 CompareByRowSpan); 987 view_states_.insert(i, view_state); 988 SkipPaddingColumns(); 989 } 990 991 void GridLayout::AddRow(Row* row) { 992 current_row_++; 993 remaining_row_span_--; 994 // GridLayout requires that if you add a View with a row span you use the same 995 // column set for each of the rows the view lands it. This DCHECK verifies 996 // that. 997 DCHECK(remaining_row_span_ <= 0 || 998 row->column_set() == NULL || 999 row->column_set() == GetLastValidColumnSet()); 1000 next_column_ = 0; 1001 rows_.push_back(row); 1002 current_row_col_set_ = row->column_set(); 1003 SkipPaddingColumns(); 1004 } 1005 1006 void GridLayout::UpdateRemainingHeightFromRows(ViewState* view_state) { 1007 for (int i = 0, start_row = view_state->start_row; 1008 i < view_state->row_span; ++i) { 1009 view_state->remaining_height -= rows_[i + start_row]->Size(); 1010 } 1011 } 1012 1013 void GridLayout::DistributeRemainingHeight(ViewState* view_state) { 1014 int height = view_state->remaining_height; 1015 if (height <= 0) 1016 return; 1017 1018 // Determine the number of resizable rows the view touches. 1019 int resizable_rows = 0; 1020 int start_row = view_state->start_row; 1021 int max_row = view_state->start_row + view_state->row_span; 1022 for (int i = start_row; i < max_row; ++i) { 1023 if (rows_[i]->IsResizable()) { 1024 resizable_rows++; 1025 } 1026 } 1027 1028 if (resizable_rows > 0) { 1029 // There are resizable rows, give the remaining height to them. 1030 int to_distribute = height / resizable_rows; 1031 for (int i = start_row; i < max_row; ++i) { 1032 if (rows_[i]->IsResizable()) { 1033 height -= to_distribute; 1034 if (height < to_distribute) { 1035 // Give all slop to the last column. 1036 to_distribute += height; 1037 } 1038 rows_[i]->SetSize(rows_[i]->Size() + to_distribute); 1039 } 1040 } 1041 } else { 1042 // None of the rows are resizable, divy the remaining height up equally 1043 // among all rows the view touches. 1044 int each_row_height = height / view_state->row_span; 1045 for (int i = start_row; i < max_row; ++i) { 1046 height -= each_row_height; 1047 if (height < each_row_height) 1048 each_row_height += height; 1049 rows_[i]->SetSize(rows_[i]->Size() + each_row_height); 1050 } 1051 view_state->remaining_height = 0; 1052 } 1053 } 1054 1055 void GridLayout::SkipPaddingColumns() { 1056 if (!current_row_col_set_) 1057 return; 1058 while (next_column_ < current_row_col_set_->num_columns() && 1059 current_row_col_set_->columns_[next_column_]->is_padding_) { 1060 next_column_++; 1061 } 1062 } 1063 1064 ColumnSet* GridLayout::GetLastValidColumnSet() { 1065 for (int i = current_row_ - 1; i >= 0; --i) { 1066 if (rows_[i]->column_set()) 1067 return rows_[i]->column_set(); 1068 } 1069 return NULL; 1070 } 1071 1072 } // namespace views 1073