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