Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / views / layout / grid_layout.cc
blob38d20c4929ce5ff0aa79705f530957487334ca25
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.
5 #include "ui/views/layout/grid_layout.h"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "ui/gfx/geometry/insets.h"
12 #include "ui/views/layout/layout_constants.h"
13 #include "ui/views/view.h"
14 #include "ui/views/window/dialog_delegate.h"
16 namespace views {
18 // LayoutElement ------------------------------------------------------
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();
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();
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;
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++;
63 if (total_percent == 0) {
64 // None of the elements are resizable, return.
65 return;
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;
81 element->SetSize(element->Size() + to_give);
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();
95 return size;
98 explicit LayoutElement(float resize_percent)
99 : resize_percent_(resize_percent) {
100 DCHECK(resize_percent >= 0);
103 virtual ~LayoutElement() {}
105 void SetLocation(int location) {
106 location_ = location;
109 int Location() {
110 return location_;
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);
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);
125 void SetSize(int size) {
126 size_ = size;
129 int Size() {
130 return size_;
133 void SetResizePercent(float percent) {
134 resize_percent_ = percent;
137 float ResizePercent() {
138 return resize_percent_;
141 bool IsResizable() {
142 return resize_percent_ > 0;
145 private:
146 float resize_percent_;
147 int location_;
148 int size_;
150 DISALLOW_COPY_AND_ASSIGN(LayoutElement);
153 // Column -------------------------------------------------------------
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) {}
176 ~Column() override {}
178 GridLayout::Alignment h_align() { return h_align_; }
179 GridLayout::Alignment v_align() { return v_align_; }
181 void ResetSize() override;
183 private:
184 friend class ColumnSet;
185 friend class GridLayout;
187 Column* GetLastMasterColumn();
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();
193 void AdjustSize(int size) override;
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_;
202 const bool is_padding_;
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_;
215 DISALLOW_COPY_AND_ASSIGN(Column);
218 void Column::ResetSize() {
219 if (size_type_ == GridLayout::FIXED) {
220 SetSize(fixed_width_);
221 } else {
222 SetSize(min_width_);
226 Column* Column::GetLastMasterColumn() {
227 if (master_column_ == NULL) {
228 return NULL;
230 if (master_column_ == this) {
231 return this;
233 return master_column_->GetLastMasterColumn();
236 void Column::UnifySameSizedColumnSizes() {
237 DCHECK(master_column_ == this);
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());
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);
253 void Column::AdjustSize(int size) {
254 if (size_type_ == GridLayout::USE_PREF)
255 LayoutElement::AdjustSize(size);
258 // Row -------------------------------------------------------------
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) {
270 ~Row() override {}
272 void ResetSize() override {
273 max_ascent_ = max_descent_ = 0;
274 SetSize(height_);
277 ColumnSet* column_set() {
278 return column_set_;
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_);
288 int max_ascent() const {
289 return max_ascent_;
292 int max_descent() const {
293 return max_descent_;
296 private:
297 const int height_;
298 // The column set used for this row; null for padding rows.
299 ColumnSet* column_set_;
301 int max_ascent_;
302 int max_descent_;
304 DISALLOW_COPY_AND_ASSIGN(Row);
307 // ViewState -------------------------------------------------------------
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());
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;
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;
349 // The preferred width/height. These are reset during the layout process.
350 int pref_width;
351 int pref_height;
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;
358 // The baseline. Only used if the view is vertically aligned along the
359 // baseline.
360 int baseline;
363 static bool CompareByColumnSpan(const ViewState* v1, const ViewState* v2) {
364 return v1->col_span < v2->col_span;
367 static bool CompareByRowSpan(const ViewState* v1, const ViewState* v2) {
368 return v1->row_span < v2->row_span;
371 // ColumnSet -------------------------------------------------------------
373 ColumnSet::ColumnSet(int id) : id_(id) {
376 ColumnSet::~ColumnSet() {
377 STLDeleteElements(&columns_);
380 void ColumnSet::AddPaddingColumn(float resize_percent, int width) {
381 AddColumn(GridLayout::FILL, GridLayout::FILL, resize_percent,
382 GridLayout::FIXED, width, width, true);
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);
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;
406 va_end(marker);
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);
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);
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;
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;
488 AccumulateMasterColumns();
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);
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;
509 void ColumnSet::UnifySameSizedColumnSizes() {
510 for (std::vector<Column*>::iterator i = master_columns_.begin();
511 i != master_columns_.end(); ++i) {
512 (*i)->UnifySameSizedColumnSizes();
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();
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;
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++;
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);
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);
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();
586 return width;
589 int ColumnSet::GetColumnWidth(int start_col, int col_span) {
590 return LayoutElement::TotalSize(start_col, col_span, &columns_);
593 void ColumnSet::ResetColumnXCoordinates() {
594 LayoutElement::CalculateLocationsFromSize(&columns_);
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();
610 view_state->remaining_width = pref.width();
611 view_state->remaining_height = pref.height();
614 // Let layout element reset the sizes for us.
615 LayoutElement::ResetSizes(&columns_);
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();
628 // Make sure all linked columns have the same size.
629 UnifySameSizedColumnSizes();
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;
635 // Update the remaining_width from columns this view_state touches.
636 UpdateRemainingWidth(view_state);
638 // Distribute the remaining width.
639 DistributeRemainingWidth(view_state);
641 // Update the size of linked columns.
642 // This may need to be combined with previous step.
643 UnifySameSizedColumnSizes();
647 void ColumnSet::Resize(int delta) {
648 LayoutElement::DistributeDelta(delta, &columns_);
651 // GridLayout -------------------------------------------------------------
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);
664 GridLayout::~GridLayout() {
665 STLDeleteElements(&column_sets_);
666 STLDeleteElements(&view_states_);
667 STLDeleteElements(&rows_);
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;
678 void GridLayout::SetInsets(int top, int left, int bottom, int right) {
679 insets_.Set(top, left, bottom, right);
682 void GridLayout::SetInsets(const gfx::Insets& insets) {
683 insets_ = insets;
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;
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;
700 return NULL;
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);
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));
715 void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) {
716 AddRow(new Row(pixel_count, vertical_resize, NULL));
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();
727 void GridLayout::AddView(View* view) {
728 AddView(view, 1, 1);
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());
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);
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);
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();
782 void GridLayout::Installed(View* host) {
783 DCHECK(host_ == host);
786 void GridLayout::Uninstalled(View* host) {
787 DCHECK(host_ == host);
790 void GridLayout::ViewAdded(View* host, View* view) {
791 DCHECK(host_ == host && adding_view_);
794 void GridLayout::ViewRemoved(View* host, View* view) {
795 DCHECK(host_ == host);
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);
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);
827 view->SetBounds(x, y, width, height);
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;
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();
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;
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()));
863 pref->set_width(pref->width() + insets_.width());
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();
876 // Reset the height of each row.
877 LayoutElement::ResetSizes(&rows_);
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;
890 if (view_state->v_align == BASELINE)
891 view_state->baseline = view_state->view->GetBaseline();
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;
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);
922 view_state->remaining_height = 0;
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;
929 // Update the remaining_width from columns this view_state touches.
930 UpdateRemainingHeightFromRows(view_state);
932 // Distribute the remaining height.
933 DistributeRemainingHeight(view_state);
936 // Update the location of each of the rows.
937 LayoutElement::CalculateLocationsFromSize(&rows_);
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());
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_);
948 // Reset y locations.
949 LayoutElement::CalculateLocationsFromSize(&rows_);
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();
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;
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();
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();
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();
1005 void GridLayout::DistributeRemainingHeight(ViewState* view_state) const {
1006 int height = view_state->remaining_height;
1007 if (height <= 0)
1008 return;
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++;
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;
1030 rows_[i]->SetSize(rows_[i]->Size() + to_distribute);
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);
1043 view_state->remaining_height = 0;
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_++;
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();
1061 return NULL;
1064 } // namespace views