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 682 const int horizontal_margin = DialogDelegate::UseNewStyle() ? 683 kButtonHEdgeMarginNew : kPanelHorizMargin; 684 685 layout->SetInsets(kPanelVertMargin, horizontal_margin, 686 kPanelVertMargin, horizontal_margin); 687 return layout; 688 } 689 690 void GridLayout::SetInsets(int top, int left, int bottom, int right) { 691 insets_.Set(top, left, bottom, right); 692 } 693 694 void GridLayout::SetInsets(const gfx::Insets& insets) { 695 insets_ = insets; 696 } 697 698 ColumnSet* GridLayout::AddColumnSet(int id) { 699 DCHECK(GetColumnSet(id) == NULL); 700 ColumnSet* column_set = new ColumnSet(id); 701 column_sets_.push_back(column_set); 702 return column_set; 703 } 704 705 ColumnSet* GridLayout::GetColumnSet(int id) { 706 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); 707 i != column_sets_.end(); ++i) { 708 if ((*i)->id_ == id) { 709 return *i; 710 } 711 } 712 return NULL; 713 } 714 715 void GridLayout::StartRowWithPadding(float vertical_resize, int column_set_id, 716 float padding_resize, int padding) { 717 AddPaddingRow(padding_resize, padding); 718 StartRow(vertical_resize, column_set_id); 719 } 720 721 void GridLayout::StartRow(float vertical_resize, int column_set_id) { 722 ColumnSet* column_set = GetColumnSet(column_set_id); 723 DCHECK(column_set); 724 AddRow(new Row(false, 0, vertical_resize, column_set)); 725 } 726 727 void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) { 728 AddRow(new Row(true, pixel_count, vertical_resize, NULL)); 729 } 730 731 void GridLayout::SkipColumns(int col_count) { 732 DCHECK(col_count > 0); 733 next_column_ += col_count; 734 DCHECK(current_row_col_set_ && 735 next_column_ <= current_row_col_set_->num_columns()); 736 SkipPaddingColumns(); 737 } 738 739 void GridLayout::AddView(View* view) { 740 AddView(view, 1, 1); 741 } 742 743 void GridLayout::AddView(View* view, int col_span, int row_span) { 744 DCHECK(current_row_col_set_ && 745 next_column_ < current_row_col_set_->num_columns()); 746 Column* column = current_row_col_set_->columns_[next_column_]; 747 AddView(view, col_span, row_span, column->h_align(), column->v_align()); 748 } 749 750 void GridLayout::AddView(View* view, int col_span, int row_span, 751 Alignment h_align, Alignment v_align) { 752 AddView(view, col_span, row_span, h_align, v_align, 0, 0); 753 } 754 755 void GridLayout::AddView(View* view, int col_span, int row_span, 756 Alignment h_align, Alignment v_align, 757 int pref_width, int pref_height) { 758 DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 && 759 (next_column_ + col_span) <= current_row_col_set_->num_columns()); 760 // We don't support baseline alignment of views spanning rows. Please add if 761 // you need it. 762 DCHECK(v_align != BASELINE || row_span == 1); 763 ViewState* state = 764 new ViewState(current_row_col_set_, view, next_column_, current_row_, 765 col_span, row_span, h_align, v_align, pref_width, 766 pref_height); 767 AddViewState(state); 768 } 769 770 static void CalculateSize(int pref_size, GridLayout::Alignment alignment, 771 int* location, int* size) { 772 if (alignment != GridLayout::FILL) { 773 int available_size = *size; 774 *size = std::min(*size, pref_size); 775 switch (alignment) { 776 case GridLayout::LEADING: 777 // Nothing to do, location already points to start. 778 break; 779 case GridLayout::BASELINE: // If we were asked to align on baseline, but 780 // the view doesn't have a baseline, fall back 781 // to center. 782 case GridLayout::CENTER: 783 *location += (available_size - *size) / 2; 784 break; 785 case GridLayout::TRAILING: 786 *location = *location + available_size - *size; 787 break; 788 default: 789 NOTREACHED(); 790 } 791 } 792 } 793 794 void GridLayout::Installed(View* host) { 795 DCHECK(host_ == host); 796 } 797 798 void GridLayout::Uninstalled(View* host) { 799 DCHECK(host_ == host); 800 } 801 802 void GridLayout::ViewAdded(View* host, View* view) { 803 DCHECK(host_ == host && adding_view_); 804 } 805 806 void GridLayout::ViewRemoved(View* host, View* view) { 807 DCHECK(host_ == host); 808 } 809 810 void GridLayout::Layout(View* host) { 811 DCHECK(host_ == host); 812 // SizeRowsAndColumns sets the size and location of each row/column, but 813 // not of the views. 814 gfx::Size pref; 815 SizeRowsAndColumns(true, host_->width(), host_->height(), &pref); 816 817 // Size each view. 818 for (std::vector<ViewState*>::iterator i = view_states_.begin(); 819 i != view_states_.end(); ++i) { 820 ViewState* view_state = *i; 821 ColumnSet* column_set = view_state->column_set; 822 View* view = (*i)->view; 823 DCHECK(view); 824 int x = column_set->columns_[view_state->start_col]->Location() + 825 insets_.left(); 826 int width = column_set->GetColumnWidth(view_state->start_col, 827 view_state->col_span); 828 CalculateSize(view_state->pref_width, view_state->h_align, 829 &x, &width); 830 int y = rows_[view_state->start_row]->Location() + insets_.top(); 831 int height = LayoutElement::TotalSize(view_state->start_row, 832 view_state->row_span, &rows_); 833 if (view_state->v_align == BASELINE && view_state->baseline != -1) { 834 y += rows_[view_state->start_row]->max_ascent() - view_state->baseline; 835 height = view_state->pref_height; 836 } else { 837 CalculateSize(view_state->pref_height, view_state->v_align, &y, &height); 838 } 839 view->SetBounds(x, y, width, height); 840 } 841 } 842 843 gfx::Size GridLayout::GetPreferredSize(View* host) { 844 DCHECK(host_ == host); 845 gfx::Size out; 846 SizeRowsAndColumns(false, 0, 0, &out); 847 out.SetSize(std::max(out.width(), minimum_size_.width()), 848 std::max(out.height(), minimum_size_.height())); 849 return out; 850 } 851 852 int GridLayout::GetPreferredHeightForWidth(View* host, int width) { 853 DCHECK(host_ == host); 854 gfx::Size pref; 855 SizeRowsAndColumns(false, width, 0, &pref); 856 return pref.height(); 857 } 858 859 void GridLayout::SizeRowsAndColumns(bool layout, int width, int height, 860 gfx::Size* pref) { 861 // Make sure the master columns have been calculated. 862 CalculateMasterColumnsIfNecessary(); 863 pref->SetSize(0, 0); 864 if (rows_.empty()) 865 return; 866 867 // Calculate the preferred width of each of the columns. Some views' 868 // preferred heights are derived from their width, as such we need to 869 // calculate the size of the columns first. 870 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); 871 i != column_sets_.end(); ++i) { 872 (*i)->CalculateSize(); 873 pref->set_width(std::max(pref->width(), (*i)->LayoutWidth())); 874 } 875 pref->set_width(pref->width() + insets_.width()); 876 877 // Go over the columns again and set them all to the size we settled for. 878 width = width ? width : pref->width(); 879 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); 880 i != column_sets_.end(); ++i) { 881 // We're doing a layout, divy up any extra space. 882 (*i)->Resize(width - (*i)->LayoutWidth() - insets_.left() - 883 insets_.right()); 884 // And reset the x coordinates. 885 (*i)->ResetColumnXCoordinates(); 886 } 887 888 // Reset the height of each row. 889 LayoutElement::ResetSizes(&rows_); 890 891 // Do the following: 892 // . If the view is aligned along it's baseline, obtain the baseline from the 893 // view and update the rows ascent/descent. 894 // . Reset the remaining_height of each view state. 895 // . If the width the view will be given is different than it's pref, ask 896 // for the height given a particularly width. 897 for (std::vector<ViewState*>::iterator i= view_states_.begin(); 898 i != view_states_.end() ; ++i) { 899 ViewState* view_state = *i; 900 view_state->remaining_height = view_state->pref_height; 901 902 if (view_state->v_align == BASELINE) 903 view_state->baseline = view_state->view->GetBaseline(); 904 905 if (view_state->h_align == FILL) { 906 // The view is resizable. As the pref height may vary with the width, 907 // ask for the pref again. 908 int actual_width = 909 view_state->column_set->GetColumnWidth(view_state->start_col, 910 view_state->col_span); 911 if (actual_width != view_state->pref_width && 912 !view_state->pref_height_fixed) { 913 // The width this view will get differs from its preferred. Some Views 914 // pref height varies with its width; ask for the preferred again. 915 view_state->pref_height = 916 view_state->view->GetHeightForWidth(actual_width); 917 view_state->remaining_height = view_state->pref_height; 918 } 919 } 920 } 921 922 // Update the height/ascent/descent of each row from the views. 923 std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin(); 924 for (; view_states_iterator != view_states_.end() && 925 (*view_states_iterator)->row_span == 1; ++view_states_iterator) { 926 ViewState* view_state = *view_states_iterator; 927 Row* row = rows_[view_state->start_row]; 928 row->AdjustSize(view_state->remaining_height); 929 if (view_state->baseline != -1 && 930 view_state->baseline <= view_state->pref_height) { 931 row->AdjustSizeForBaseline(view_state->baseline, 932 view_state->pref_height - view_state->baseline); 933 } 934 view_state->remaining_height = 0; 935 } 936 937 // Distribute the height of each view with a row span > 1. 938 for (; view_states_iterator != view_states_.end(); ++view_states_iterator) { 939 ViewState* view_state = *view_states_iterator; 940 941 // Update the remaining_width from columns this view_state touches. 942 UpdateRemainingHeightFromRows(view_state); 943 944 // Distribute the remaining height. 945 DistributeRemainingHeight(view_state); 946 } 947 948 // Update the location of each of the rows. 949 LayoutElement::CalculateLocationsFromSize(&rows_); 950 951 // We now know the preferred height, set it here. 952 pref->set_height(rows_[rows_.size() - 1]->Location() + 953 rows_[rows_.size() - 1]->Size() + insets_.height()); 954 955 if (layout && height != pref->height()) { 956 // We're doing a layout, and the height differs from the preferred height, 957 // divy up the extra space. 958 LayoutElement::DistributeDelta(height - pref->height(), &rows_); 959 960 // Reset y locations. 961 LayoutElement::CalculateLocationsFromSize(&rows_); 962 } 963 } 964 965 void GridLayout::CalculateMasterColumnsIfNecessary() { 966 if (!calculated_master_columns_) { 967 calculated_master_columns_ = true; 968 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); 969 i != column_sets_.end(); ++i) { 970 (*i)->CalculateMasterColumns(); 971 } 972 } 973 } 974 975 void GridLayout::AddViewState(ViewState* view_state) { 976 DCHECK(view_state->view && (view_state->view->parent() == NULL || 977 view_state->view->parent() == host_)); 978 if (!view_state->view->parent()) { 979 adding_view_ = true; 980 host_->AddChildView(view_state->view); 981 adding_view_ = false; 982 } 983 remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span); 984 next_column_ += view_state->col_span; 985 current_row_col_set_->AddViewState(view_state); 986 // view_states are ordered by row_span (in ascending order). 987 std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(), 988 view_states_.end(), 989 view_state, 990 CompareByRowSpan); 991 view_states_.insert(i, view_state); 992 SkipPaddingColumns(); 993 } 994 995 void GridLayout::AddRow(Row* row) { 996 current_row_++; 997 remaining_row_span_--; 998 DCHECK(remaining_row_span_ <= 0 || 999 row->column_set() == NULL || 1000 row->column_set() == GetLastValidColumnSet()); 1001 next_column_ = 0; 1002 rows_.push_back(row); 1003 current_row_col_set_ = row->column_set(); 1004 SkipPaddingColumns(); 1005 } 1006 1007 void GridLayout::UpdateRemainingHeightFromRows(ViewState* view_state) { 1008 for (int i = 0, start_row = view_state->start_row; 1009 i < view_state->row_span; ++i) { 1010 view_state->remaining_height -= rows_[i + start_row]->Size(); 1011 } 1012 } 1013 1014 void GridLayout::DistributeRemainingHeight(ViewState* view_state) { 1015 int height = view_state->remaining_height; 1016 if (height <= 0) 1017 return; 1018 1019 // Determine the number of resizable rows the view touches. 1020 int resizable_rows = 0; 1021 int start_row = view_state->start_row; 1022 int max_row = view_state->start_row + view_state->row_span; 1023 for (int i = start_row; i < max_row; ++i) { 1024 if (rows_[i]->IsResizable()) { 1025 resizable_rows++; 1026 } 1027 } 1028 1029 if (resizable_rows > 0) { 1030 // There are resizable rows, give the remaining height to them. 1031 int to_distribute = height / resizable_rows; 1032 for (int i = start_row; i < max_row; ++i) { 1033 if (rows_[i]->IsResizable()) { 1034 height -= to_distribute; 1035 if (height < to_distribute) { 1036 // Give all slop to the last column. 1037 to_distribute += height; 1038 } 1039 rows_[i]->SetSize(rows_[i]->Size() + to_distribute); 1040 } 1041 } 1042 } else { 1043 // None of the rows are resizable, divy the remaining height up equally 1044 // among all rows the view touches. 1045 int each_row_height = height / view_state->row_span; 1046 for (int i = start_row; i < max_row; ++i) { 1047 height -= each_row_height; 1048 if (height < each_row_height) 1049 each_row_height += height; 1050 rows_[i]->SetSize(rows_[i]->Size() + each_row_height); 1051 } 1052 view_state->remaining_height = 0; 1053 } 1054 } 1055 1056 void GridLayout::SkipPaddingColumns() { 1057 if (!current_row_col_set_) 1058 return; 1059 while (next_column_ < current_row_col_set_->num_columns() && 1060 current_row_col_set_->columns_[next_column_]->is_padding_) { 1061 next_column_++; 1062 } 1063 } 1064 1065 ColumnSet* GridLayout::GetLastValidColumnSet() { 1066 for (int i = current_row_ - 1; i >= 0; --i) { 1067 if (rows_[i]->column_set()) 1068 return rows_[i]->column_set(); 1069 } 1070 return NULL; 1071 } 1072 1073 } // namespace views 1074