Automated Commit: Committing new LKGM version 7479.0.0 for chromeos.
[chromium-blink-merge.git] / ui / views / layout / grid_layout.cc
blob5cf91970269add681d65a58ca39986f8bbf89df5
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/views/layout/layout_constants.h"
12 #include "ui/views/view.h"
13 #include "ui/views/window/dialog_delegate.h"
15 namespace views {
17 // LayoutElement ------------------------------------------------------
19 // A LayoutElement has a size and location along one axis. It contains
20 // methods that are used along both axis.
21 class LayoutElement {
22 public:
23 // Invokes ResetSize on all the layout elements.
24 template <class T>
25 static void ResetSizes(std::vector<T*>* elements) {
26 // Reset the layout width of each column.
27 for (typename std::vector<T*>::iterator i = elements->begin();
28 i != elements->end(); ++i) {
29 (*i)->ResetSize();
33 // Sets the location of each element to be the sum of the sizes of the
34 // preceding elements.
35 template <class T>
36 static void CalculateLocationsFromSize(std::vector<T*>* elements) {
37 // Reset the layout width of each column.
38 int location = 0;
39 for (typename std::vector<T*>::iterator i = elements->begin();
40 i != elements->end(); ++i) {
41 (*i)->SetLocation(location);
42 location += (*i)->Size();
46 // Distributes delta among the resizable elements.
47 // Each resizable element is given ResizePercent / total_percent * delta
48 // pixels extra of space.
49 template <class T>
50 static void DistributeDelta(int delta, std::vector<T*>* elements) {
51 if (delta == 0)
52 return;
54 float total_percent = 0;
55 int resize_count = 0;
56 for (typename std::vector<T*>::iterator i = elements->begin();
57 i != elements->end(); ++i) {
58 total_percent += (*i)->ResizePercent();
59 if ((*i)->ResizePercent() > 0)
60 resize_count++;
62 if (total_percent == 0) {
63 // None of the elements are resizable, return.
64 return;
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;
80 element->SetSize(element->Size() + to_give);
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();
94 return size;
97 explicit LayoutElement(float resize_percent)
98 : resize_percent_(resize_percent) {
99 DCHECK(resize_percent >= 0);
102 virtual ~LayoutElement() {}
104 void SetLocation(int location) {
105 location_ = location;
108 int Location() {
109 return location_;
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);
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);
124 void SetSize(int size) {
125 size_ = size;
128 int Size() {
129 return size_;
132 void SetResizePercent(float percent) {
133 resize_percent_ = percent;
136 float ResizePercent() {
137 return resize_percent_;
140 bool IsResizable() {
141 return resize_percent_ > 0;
144 private:
145 float resize_percent_;
146 int location_;
147 int size_;
149 DISALLOW_COPY_AND_ASSIGN(LayoutElement);
152 // Column -------------------------------------------------------------
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 bool is_padding)
165 : LayoutElement(resize_percent),
166 h_align_(h_align),
167 v_align_(v_align),
168 size_type_(size_type),
169 same_size_column_(-1),
170 fixed_width_(fixed_width),
171 min_width_(min_width),
172 is_padding_(is_padding),
173 master_column_(NULL) {}
175 ~Column() override {}
177 GridLayout::Alignment h_align() { return h_align_; }
178 GridLayout::Alignment v_align() { return v_align_; }
180 void ResetSize() override;
182 private:
183 friend class ColumnSet;
184 friend class GridLayout;
186 Column* GetLastMasterColumn();
188 // Determines the max size of all linked columns, and sets each column
189 // to that size. This should only be used for the master column.
190 void UnifySameSizedColumnSizes();
192 void AdjustSize(int size) override;
194 const GridLayout::Alignment h_align_;
195 const GridLayout::Alignment v_align_;
196 const GridLayout::SizeType size_type_;
197 int same_size_column_;
198 const int fixed_width_;
199 const int min_width_;
201 const bool is_padding_;
203 // If multiple columns have their sizes linked, one is the
204 // master column. The master column is identified by the
205 // master_column field being equal to itself. The master columns
206 // same_size_columns field contains the set of Columns with the
207 // the same size. Columns who are linked to other columns, but
208 // are not the master column have their master_column pointing to
209 // one of the other linked columns. Use the method GetLastMasterColumn
210 // to resolve the true master column.
211 std::vector<Column*> same_size_columns_;
212 Column* master_column_;
214 DISALLOW_COPY_AND_ASSIGN(Column);
217 void Column::ResetSize() {
218 if (size_type_ == GridLayout::FIXED) {
219 SetSize(fixed_width_);
220 } else {
221 SetSize(min_width_);
225 Column* Column::GetLastMasterColumn() {
226 if (master_column_ == NULL) {
227 return NULL;
229 if (master_column_ == this) {
230 return this;
232 return master_column_->GetLastMasterColumn();
235 void Column::UnifySameSizedColumnSizes() {
236 DCHECK(master_column_ == this);
238 // Accumulate the size first.
239 int size = 0;
240 for (std::vector<Column*>::iterator i = same_size_columns_.begin();
241 i != same_size_columns_.end(); ++i) {
242 size = std::max(size, (*i)->Size());
245 // Then apply it.
246 for (std::vector<Column*>::iterator i = same_size_columns_.begin();
247 i != same_size_columns_.end(); ++i) {
248 (*i)->SetSize(size);
252 void Column::AdjustSize(int size) {
253 if (size_type_ == GridLayout::USE_PREF)
254 LayoutElement::AdjustSize(size);
257 // Row -------------------------------------------------------------
259 class Row : public LayoutElement {
260 public:
261 Row(int height, float resize_percent, ColumnSet* column_set)
262 : LayoutElement(resize_percent),
263 height_(height),
264 column_set_(column_set),
265 max_ascent_(0),
266 max_descent_(0) {
269 ~Row() override {}
271 void ResetSize() override {
272 max_ascent_ = max_descent_ = 0;
273 SetSize(height_);
276 ColumnSet* column_set() {
277 return column_set_;
280 // Adjusts the size to accomodate the specified ascent/descent.
281 void AdjustSizeForBaseline(int ascent, int descent) {
282 max_ascent_ = std::max(ascent, max_ascent_);
283 max_descent_ = std::max(descent, max_descent_);
284 AdjustSize(max_ascent_ + max_descent_);
287 int max_ascent() const {
288 return max_ascent_;
291 int max_descent() const {
292 return max_descent_;
295 private:
296 const int height_;
297 // The column set used for this row; null for padding rows.
298 ColumnSet* column_set_;
300 int max_ascent_;
301 int max_descent_;
303 DISALLOW_COPY_AND_ASSIGN(Row);
306 // ViewState -------------------------------------------------------------
308 // Identifies the location in the grid of a particular view, along with
309 // placement information and size information.
310 struct ViewState {
311 ViewState(ColumnSet* column_set, View* view, int start_col, int start_row,
312 int col_span, int row_span, GridLayout::Alignment h_align,
313 GridLayout::Alignment v_align, int pref_width, int pref_height)
314 : column_set(column_set),
315 view(view),
316 start_col(start_col),
317 start_row(start_row),
318 col_span(col_span),
319 row_span(row_span),
320 h_align(h_align),
321 v_align(v_align),
322 pref_width_fixed(pref_width > 0),
323 pref_height_fixed(pref_height > 0),
324 pref_width(pref_width),
325 pref_height(pref_height),
326 remaining_width(0),
327 remaining_height(0),
328 baseline(-1) {
329 DCHECK(view && start_col >= 0 && start_row >= 0 && col_span > 0 &&
330 row_span > 0 && start_col < column_set->num_columns() &&
331 (start_col + col_span) <= column_set->num_columns());
334 ColumnSet* const column_set;
335 View* const view;
336 const int start_col;
337 const int start_row;
338 const int col_span;
339 const int row_span;
340 const GridLayout::Alignment h_align;
341 const GridLayout::Alignment v_align;
343 // If true, the pref_width/pref_height were explicitly set and the view's
344 // preferred size is ignored.
345 const bool pref_width_fixed;
346 const bool pref_height_fixed;
348 // The preferred width/height. These are reset during the layout process.
349 int pref_width;
350 int pref_height;
352 // Used during layout. Gives how much width/height has not yet been
353 // distributed to the columns/rows the view is in.
354 int remaining_width;
355 int remaining_height;
357 // The baseline. Only used if the view is vertically aligned along the
358 // baseline.
359 int baseline;
362 static bool CompareByColumnSpan(const ViewState* v1, const ViewState* v2) {
363 return v1->col_span < v2->col_span;
366 static bool CompareByRowSpan(const ViewState* v1, const ViewState* v2) {
367 return v1->row_span < v2->row_span;
370 // ColumnSet -------------------------------------------------------------
372 ColumnSet::ColumnSet(int id) : id_(id) {
375 ColumnSet::~ColumnSet() {
376 STLDeleteElements(&columns_);
379 void ColumnSet::AddPaddingColumn(float resize_percent, int width) {
380 AddColumn(GridLayout::FILL, GridLayout::FILL, resize_percent,
381 GridLayout::FIXED, width, width, true);
384 void ColumnSet::AddColumn(GridLayout::Alignment h_align,
385 GridLayout::Alignment v_align,
386 float resize_percent,
387 GridLayout::SizeType size_type,
388 int fixed_width,
389 int min_width) {
390 AddColumn(h_align, v_align, resize_percent, size_type, fixed_width,
391 min_width, false);
395 void ColumnSet::LinkColumnSizes(int first, ...) {
396 va_list marker;
397 va_start(marker, first);
398 DCHECK(first >= 0 && first < num_columns());
399 for (int last = first, next = va_arg(marker, int); next != -1;
400 next = va_arg(marker, int)) {
401 DCHECK(next >= 0 && next < num_columns());
402 columns_[last]->same_size_column_ = next;
403 last = next;
405 va_end(marker);
408 void ColumnSet::AddColumn(GridLayout::Alignment h_align,
409 GridLayout::Alignment v_align,
410 float resize_percent,
411 GridLayout::SizeType size_type,
412 int fixed_width,
413 int min_width,
414 bool is_padding) {
415 Column* column = new Column(h_align, v_align, resize_percent, size_type,
416 fixed_width, min_width, is_padding);
417 columns_.push_back(column);
420 void ColumnSet::AddViewState(ViewState* view_state) {
421 // view_states are ordered by column_span (in ascending order).
422 std::vector<ViewState*>::iterator i = std::lower_bound(view_states_.begin(),
423 view_states_.end(),
424 view_state,
425 CompareByColumnSpan);
426 view_states_.insert(i, view_state);
429 void ColumnSet::CalculateMasterColumns() {
430 for (std::vector<Column*>::iterator i = columns_.begin();
431 i != columns_.end(); ++i) {
432 Column* column = *i;
433 int same_size_column_index = column->same_size_column_;
434 if (same_size_column_index != -1) {
435 DCHECK(same_size_column_index >= 0 &&
436 same_size_column_index < static_cast<int>(columns_.size()));
437 Column* master_column = column->master_column_;
438 Column* same_size_column = columns_[same_size_column_index];
439 Column* same_size_column_master = same_size_column->master_column_;
440 if (master_column == NULL) {
441 // Current column is not linked to any other column.
442 if (same_size_column_master == NULL) {
443 // Both columns are not linked.
444 column->master_column_ = column;
445 same_size_column->master_column_ = column;
446 column->same_size_columns_.push_back(same_size_column);
447 column->same_size_columns_.push_back(column);
448 } else {
449 // Column to link to is linked with other columns.
450 // Add current column to list of linked columns in other columns
451 // master column.
452 same_size_column->GetLastMasterColumn()->
453 same_size_columns_.push_back(column);
454 // And update the master column for the current column to that
455 // of the same sized column.
456 column->master_column_ = same_size_column;
458 } else {
459 // Current column is already linked with another column.
460 if (same_size_column_master == NULL) {
461 // Column to link with is not linked to any other columns.
462 // Update it's master_column.
463 same_size_column->master_column_ = column;
464 // Add linked column to list of linked column.
465 column->GetLastMasterColumn()->same_size_columns_.
466 push_back(same_size_column);
467 } else if (column->GetLastMasterColumn() !=
468 same_size_column->GetLastMasterColumn()) {
469 // The two columns are already linked with other columns.
470 std::vector<Column*>* same_size_columns =
471 &(column->GetLastMasterColumn()->same_size_columns_);
472 std::vector<Column*>* other_same_size_columns =
473 &(same_size_column->GetLastMasterColumn()->same_size_columns_);
474 // Add all the columns from the others master to current columns
475 // master.
476 same_size_columns->insert(same_size_columns->end(),
477 other_same_size_columns->begin(),
478 other_same_size_columns->end());
479 // The other master is no longer a master, clear its vector of
480 // linked columns, and reset its master_column.
481 other_same_size_columns->clear();
482 same_size_column->GetLastMasterColumn()->master_column_ = column;
487 AccumulateMasterColumns();
490 void ColumnSet::AccumulateMasterColumns() {
491 DCHECK(master_columns_.empty());
492 for (std::vector<Column*>::iterator i = columns_.begin();
493 i != columns_.end(); ++i) {
494 Column* column = *i;
495 Column* master_column = column->GetLastMasterColumn();
496 if (master_column &&
497 std::find(master_columns_.begin(), master_columns_.end(),
498 master_column) == master_columns_.end()) {
499 master_columns_.push_back(master_column);
501 // At this point, GetLastMasterColumn may not == master_column
502 // (may have to go through a few Columns)_. Reset master_column to
503 // avoid hops.
504 column->master_column_ = master_column;
508 void ColumnSet::UnifySameSizedColumnSizes() {
509 for (std::vector<Column*>::iterator i = master_columns_.begin();
510 i != master_columns_.end(); ++i) {
511 (*i)->UnifySameSizedColumnSizes();
515 void ColumnSet::UpdateRemainingWidth(ViewState* view_state) {
516 for (int i = view_state->start_col,
517 max_col = view_state->start_col + view_state->col_span;
518 i < max_col; ++i) {
519 view_state->remaining_width -= columns_[i]->Size();
523 void ColumnSet::DistributeRemainingWidth(ViewState* view_state) {
524 // This is nearly the same as that for rows, but differs in so far as how
525 // Rows and Columns are treated. Rows have two states, resizable or not.
526 // Columns have three, resizable, USE_PREF or not resizable. This results
527 // in slightly different handling for distributing unaccounted size.
528 int width = view_state->remaining_width;
529 if (width <= 0) {
530 // The columns this view is in are big enough to accommodate it.
531 return;
534 // Determine which columns are resizable, and which have a size type
535 // of USE_PREF.
536 int resizable_columns = 0;
537 int pref_size_columns = 0;
538 int start_col = view_state->start_col;
539 int max_col = view_state->start_col + view_state->col_span;
540 float total_resize = 0;
541 for (int i = start_col; i < max_col; ++i) {
542 if (columns_[i]->IsResizable()) {
543 total_resize += columns_[i]->ResizePercent();
544 resizable_columns++;
545 } else if (columns_[i]->size_type_ == GridLayout::USE_PREF) {
546 pref_size_columns++;
550 if (resizable_columns > 0) {
551 // There are resizable columns, give them the remaining width. The extra
552 // width is distributed using the resize values of each column.
553 int remaining_width = width;
554 for (int i = start_col, resize_i = 0; i < max_col; ++i) {
555 if (columns_[i]->IsResizable()) {
556 resize_i++;
557 int delta = (resize_i == resizable_columns) ? remaining_width :
558 static_cast<int>(width * columns_[i]->ResizePercent() /
559 total_resize);
560 remaining_width -= delta;
561 columns_[i]->SetSize(columns_[i]->Size() + delta);
564 } else if (pref_size_columns > 0) {
565 // None of the columns are resizable, distribute the width among those
566 // that use the preferred size.
567 int to_distribute = width / pref_size_columns;
568 for (int i = start_col; i < max_col; ++i) {
569 if (columns_[i]->size_type_ == GridLayout::USE_PREF) {
570 width -= to_distribute;
571 if (width < to_distribute)
572 to_distribute += width;
573 columns_[i]->SetSize(columns_[i]->Size() + to_distribute);
579 int ColumnSet::LayoutWidth() {
580 int width = 0;
581 for (std::vector<Column*>::iterator i = columns_.begin();
582 i != columns_.end(); ++i) {
583 width += (*i)->Size();
585 return width;
588 int ColumnSet::GetColumnWidth(int start_col, int col_span) {
589 return LayoutElement::TotalSize(start_col, col_span, &columns_);
592 void ColumnSet::ResetColumnXCoordinates() {
593 LayoutElement::CalculateLocationsFromSize(&columns_);
596 void ColumnSet::CalculateSize() {
597 gfx::Size pref;
598 // Reset the preferred and remaining sizes.
599 for (std::vector<ViewState*>::iterator i = view_states_.begin();
600 i != view_states_.end(); ++i) {
601 ViewState* view_state = *i;
602 if (!view_state->pref_width_fixed || !view_state->pref_height_fixed) {
603 pref = view_state->view->GetPreferredSize();
604 if (!view_state->pref_width_fixed)
605 view_state->pref_width = pref.width();
606 if (!view_state->pref_height_fixed)
607 view_state->pref_height = pref.height();
609 view_state->remaining_width = pref.width();
610 view_state->remaining_height = pref.height();
613 // Let layout element reset the sizes for us.
614 LayoutElement::ResetSizes(&columns_);
616 // Distribute the size of each view with a col span == 1.
617 std::vector<ViewState*>::iterator view_state_iterator =
618 view_states_.begin();
619 for (; view_state_iterator != view_states_.end() &&
620 (*view_state_iterator)->col_span == 1; ++view_state_iterator) {
621 ViewState* view_state = *view_state_iterator;
622 Column* column = columns_[view_state->start_col];
623 column->AdjustSize(view_state->pref_width);
624 view_state->remaining_width -= column->Size();
627 // Make sure all linked columns have the same size.
628 UnifySameSizedColumnSizes();
630 // Distribute the size of each view with a column span > 1.
631 for (; view_state_iterator != view_states_.end(); ++view_state_iterator) {
632 ViewState* view_state = *view_state_iterator;
634 // Update the remaining_width from columns this view_state touches.
635 UpdateRemainingWidth(view_state);
637 // Distribute the remaining width.
638 DistributeRemainingWidth(view_state);
640 // Update the size of linked columns.
641 // This may need to be combined with previous step.
642 UnifySameSizedColumnSizes();
646 void ColumnSet::Resize(int delta) {
647 LayoutElement::DistributeDelta(delta, &columns_);
650 // GridLayout -------------------------------------------------------------
652 GridLayout::GridLayout(View* host)
653 : host_(host),
654 calculated_master_columns_(false),
655 remaining_row_span_(0),
656 current_row_(-1),
657 next_column_(0),
658 current_row_col_set_(NULL),
659 adding_view_(false) {
660 DCHECK(host);
663 GridLayout::~GridLayout() {
664 STLDeleteElements(&column_sets_);
665 STLDeleteElements(&view_states_);
666 STLDeleteElements(&rows_);
669 // static
670 GridLayout* GridLayout::CreatePanel(View* host) {
671 GridLayout* layout = new GridLayout(host);
672 layout->SetInsets(kPanelVertMargin, kButtonHEdgeMarginNew,
673 kPanelVertMargin, kButtonHEdgeMarginNew);
674 return layout;
677 void GridLayout::SetInsets(int top, int left, int bottom, int right) {
678 insets_.Set(top, left, bottom, right);
681 void GridLayout::SetInsets(const gfx::Insets& insets) {
682 insets_ = insets;
685 ColumnSet* GridLayout::AddColumnSet(int id) {
686 DCHECK(GetColumnSet(id) == NULL);
687 ColumnSet* column_set = new ColumnSet(id);
688 column_sets_.push_back(column_set);
689 return column_set;
692 ColumnSet* GridLayout::GetColumnSet(int id) {
693 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
694 i != column_sets_.end(); ++i) {
695 if ((*i)->id_ == id) {
696 return *i;
699 return NULL;
702 void GridLayout::StartRowWithPadding(float vertical_resize, int column_set_id,
703 float padding_resize, int padding) {
704 AddPaddingRow(padding_resize, padding);
705 StartRow(vertical_resize, column_set_id);
708 void GridLayout::StartRow(float vertical_resize, int column_set_id) {
709 ColumnSet* column_set = GetColumnSet(column_set_id);
710 DCHECK(column_set);
711 AddRow(new Row(0, vertical_resize, column_set));
714 void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) {
715 AddRow(new Row(pixel_count, vertical_resize, NULL));
718 void GridLayout::SkipColumns(int col_count) {
719 DCHECK(col_count > 0);
720 next_column_ += col_count;
721 DCHECK(current_row_col_set_ &&
722 next_column_ <= current_row_col_set_->num_columns());
723 SkipPaddingColumns();
726 void GridLayout::AddView(View* view) {
727 AddView(view, 1, 1);
730 void GridLayout::AddView(View* view, int col_span, int row_span) {
731 DCHECK(current_row_col_set_ &&
732 next_column_ < current_row_col_set_->num_columns());
733 Column* column = current_row_col_set_->columns_[next_column_];
734 AddView(view, col_span, row_span, column->h_align(), column->v_align());
737 void GridLayout::AddView(View* view, int col_span, int row_span,
738 Alignment h_align, Alignment v_align) {
739 AddView(view, col_span, row_span, h_align, v_align, 0, 0);
742 void GridLayout::AddView(View* view, int col_span, int row_span,
743 Alignment h_align, Alignment v_align,
744 int pref_width, int pref_height) {
745 DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 &&
746 (next_column_ + col_span) <= current_row_col_set_->num_columns());
747 // We don't support baseline alignment of views spanning rows. Please add if
748 // you need it.
749 DCHECK(v_align != BASELINE || row_span == 1);
750 ViewState* state =
751 new ViewState(current_row_col_set_, view, next_column_, current_row_,
752 col_span, row_span, h_align, v_align, pref_width,
753 pref_height);
754 AddViewState(state);
757 static void CalculateSize(int pref_size, GridLayout::Alignment alignment,
758 int* location, int* size) {
759 if (alignment != GridLayout::FILL) {
760 int available_size = *size;
761 *size = std::min(*size, pref_size);
762 switch (alignment) {
763 case GridLayout::LEADING:
764 // Nothing to do, location already points to start.
765 break;
766 case GridLayout::BASELINE: // If we were asked to align on baseline, but
767 // the view doesn't have a baseline, fall back
768 // to center.
769 case GridLayout::CENTER:
770 *location += (available_size - *size) / 2;
771 break;
772 case GridLayout::TRAILING:
773 *location = *location + available_size - *size;
774 break;
775 default:
776 NOTREACHED();
781 void GridLayout::Installed(View* host) {
782 DCHECK(host_ == host);
785 void GridLayout::Uninstalled(View* host) {
786 DCHECK(host_ == host);
789 void GridLayout::ViewAdded(View* host, View* view) {
790 DCHECK(host_ == host && adding_view_);
793 void GridLayout::ViewRemoved(View* host, View* view) {
794 DCHECK(host_ == host);
797 void GridLayout::Layout(View* host) {
798 DCHECK(host_ == host);
799 // SizeRowsAndColumns sets the size and location of each row/column, but
800 // not of the views.
801 gfx::Size pref;
802 SizeRowsAndColumns(true, host_->width(), host_->height(), &pref);
804 // Size each view.
805 for (std::vector<ViewState*>::iterator i = view_states_.begin();
806 i != view_states_.end(); ++i) {
807 ViewState* view_state = *i;
808 ColumnSet* column_set = view_state->column_set;
809 View* view = (*i)->view;
810 DCHECK(view);
811 int x = column_set->columns_[view_state->start_col]->Location() +
812 insets_.left();
813 int width = column_set->GetColumnWidth(view_state->start_col,
814 view_state->col_span);
815 CalculateSize(view_state->pref_width, view_state->h_align,
816 &x, &width);
817 int y = rows_[view_state->start_row]->Location() + insets_.top();
818 int height = LayoutElement::TotalSize(view_state->start_row,
819 view_state->row_span, &rows_);
820 if (view_state->v_align == BASELINE && view_state->baseline != -1) {
821 y += rows_[view_state->start_row]->max_ascent() - view_state->baseline;
822 height = view_state->pref_height;
823 } else {
824 CalculateSize(view_state->pref_height, view_state->v_align, &y, &height);
826 view->SetBounds(x, y, width, height);
830 gfx::Size GridLayout::GetPreferredSize(const View* host) const {
831 DCHECK(host_ == host);
832 gfx::Size out;
833 SizeRowsAndColumns(false, 0, 0, &out);
834 out.SetSize(std::max(out.width(), minimum_size_.width()),
835 std::max(out.height(), minimum_size_.height()));
836 return out;
839 int GridLayout::GetPreferredHeightForWidth(const View* host, int width) const {
840 DCHECK(host_ == host);
841 gfx::Size pref;
842 SizeRowsAndColumns(false, width, 0, &pref);
843 return pref.height();
846 void GridLayout::SizeRowsAndColumns(bool layout, int width, int height,
847 gfx::Size* pref) const {
848 // Make sure the master columns have been calculated.
849 CalculateMasterColumnsIfNecessary();
850 pref->SetSize(0, 0);
851 if (rows_.empty())
852 return;
854 // Calculate the preferred width of each of the columns. Some views'
855 // preferred heights are derived from their width, as such we need to
856 // calculate the size of the columns first.
857 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
858 i != column_sets_.end(); ++i) {
859 (*i)->CalculateSize();
860 pref->set_width(std::max(pref->width(), (*i)->LayoutWidth()));
862 pref->set_width(pref->width() + insets_.width());
864 // Go over the columns again and set them all to the size we settled for.
865 width = width ? width : pref->width();
866 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
867 i != column_sets_.end(); ++i) {
868 // We're doing a layout, divy up any extra space.
869 (*i)->Resize(width - (*i)->LayoutWidth() - insets_.left() -
870 insets_.right());
871 // And reset the x coordinates.
872 (*i)->ResetColumnXCoordinates();
875 // Reset the height of each row.
876 LayoutElement::ResetSizes(&rows_);
878 // Do the following:
879 // . If the view is aligned along it's baseline, obtain the baseline from the
880 // view and update the rows ascent/descent.
881 // . Reset the remaining_height of each view state.
882 // . If the width the view will be given is different than it's pref, ask
883 // for the height given a particularly width.
884 for (std::vector<ViewState*>::iterator i= view_states_.begin();
885 i != view_states_.end() ; ++i) {
886 ViewState* view_state = *i;
887 view_state->remaining_height = view_state->pref_height;
889 if (view_state->v_align == BASELINE)
890 view_state->baseline = view_state->view->GetBaseline();
892 if (view_state->h_align == FILL) {
893 // The view is resizable. As the pref height may vary with the width,
894 // ask for the pref again.
895 int actual_width =
896 view_state->column_set->GetColumnWidth(view_state->start_col,
897 view_state->col_span);
898 if (actual_width != view_state->pref_width &&
899 !view_state->pref_height_fixed) {
900 // The width this view will get differs from its preferred. Some Views
901 // pref height varies with its width; ask for the preferred again.
902 view_state->pref_height =
903 view_state->view->GetHeightForWidth(actual_width);
904 view_state->remaining_height = view_state->pref_height;
909 // Update the height/ascent/descent of each row from the views.
910 std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin();
911 for (; view_states_iterator != view_states_.end() &&
912 (*view_states_iterator)->row_span == 1; ++view_states_iterator) {
913 ViewState* view_state = *view_states_iterator;
914 Row* row = rows_[view_state->start_row];
915 row->AdjustSize(view_state->remaining_height);
916 if (view_state->baseline != -1 &&
917 view_state->baseline <= view_state->pref_height) {
918 row->AdjustSizeForBaseline(view_state->baseline,
919 view_state->pref_height - view_state->baseline);
921 view_state->remaining_height = 0;
924 // Distribute the height of each view with a row span > 1.
925 for (; view_states_iterator != view_states_.end(); ++view_states_iterator) {
926 ViewState* view_state = *view_states_iterator;
928 // Update the remaining_width from columns this view_state touches.
929 UpdateRemainingHeightFromRows(view_state);
931 // Distribute the remaining height.
932 DistributeRemainingHeight(view_state);
935 // Update the location of each of the rows.
936 LayoutElement::CalculateLocationsFromSize(&rows_);
938 // We now know the preferred height, set it here.
939 pref->set_height(rows_[rows_.size() - 1]->Location() +
940 rows_[rows_.size() - 1]->Size() + insets_.height());
942 if (layout && height != pref->height()) {
943 // We're doing a layout, and the height differs from the preferred height,
944 // divy up the extra space.
945 LayoutElement::DistributeDelta(height - pref->height(), &rows_);
947 // Reset y locations.
948 LayoutElement::CalculateLocationsFromSize(&rows_);
952 void GridLayout::CalculateMasterColumnsIfNecessary() const {
953 if (!calculated_master_columns_) {
954 calculated_master_columns_ = true;
955 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
956 i != column_sets_.end(); ++i) {
957 (*i)->CalculateMasterColumns();
962 void GridLayout::AddViewState(ViewState* view_state) {
963 DCHECK(view_state->view && (view_state->view->parent() == NULL ||
964 view_state->view->parent() == host_));
965 if (!view_state->view->parent()) {
966 adding_view_ = true;
967 host_->AddChildView(view_state->view);
968 adding_view_ = false;
970 remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span);
971 next_column_ += view_state->col_span;
972 current_row_col_set_->AddViewState(view_state);
973 // view_states are ordered by row_span (in ascending order).
974 std::vector<ViewState*>::iterator i = std::lower_bound(view_states_.begin(),
975 view_states_.end(),
976 view_state,
977 CompareByRowSpan);
978 view_states_.insert(i, view_state);
979 SkipPaddingColumns();
982 void GridLayout::AddRow(Row* row) {
983 current_row_++;
984 remaining_row_span_--;
985 // GridLayout requires that if you add a View with a row span you use the same
986 // column set for each of the rows the view lands it. This DCHECK verifies
987 // that.
988 DCHECK(remaining_row_span_ <= 0 ||
989 row->column_set() == NULL ||
990 row->column_set() == GetLastValidColumnSet());
991 next_column_ = 0;
992 rows_.push_back(row);
993 current_row_col_set_ = row->column_set();
994 SkipPaddingColumns();
997 void GridLayout::UpdateRemainingHeightFromRows(ViewState* view_state) const {
998 for (int i = 0, start_row = view_state->start_row;
999 i < view_state->row_span; ++i) {
1000 view_state->remaining_height -= rows_[i + start_row]->Size();
1004 void GridLayout::DistributeRemainingHeight(ViewState* view_state) const {
1005 int height = view_state->remaining_height;
1006 if (height <= 0)
1007 return;
1009 // Determine the number of resizable rows the view touches.
1010 int resizable_rows = 0;
1011 int start_row = view_state->start_row;
1012 int max_row = view_state->start_row + view_state->row_span;
1013 for (int i = start_row; i < max_row; ++i) {
1014 if (rows_[i]->IsResizable()) {
1015 resizable_rows++;
1019 if (resizable_rows > 0) {
1020 // There are resizable rows, give the remaining height to them.
1021 int to_distribute = height / resizable_rows;
1022 for (int i = start_row; i < max_row; ++i) {
1023 if (rows_[i]->IsResizable()) {
1024 height -= to_distribute;
1025 if (height < to_distribute) {
1026 // Give all slop to the last column.
1027 to_distribute += height;
1029 rows_[i]->SetSize(rows_[i]->Size() + to_distribute);
1032 } else {
1033 // None of the rows are resizable, divy the remaining height up equally
1034 // among all rows the view touches.
1035 int each_row_height = height / view_state->row_span;
1036 for (int i = start_row; i < max_row; ++i) {
1037 height -= each_row_height;
1038 if (height < each_row_height)
1039 each_row_height += height;
1040 rows_[i]->SetSize(rows_[i]->Size() + each_row_height);
1042 view_state->remaining_height = 0;
1046 void GridLayout::SkipPaddingColumns() {
1047 if (!current_row_col_set_)
1048 return;
1049 while (next_column_ < current_row_col_set_->num_columns() &&
1050 current_row_col_set_->columns_[next_column_]->is_padding_) {
1051 next_column_++;
1055 ColumnSet* GridLayout::GetLastValidColumnSet() {
1056 for (int i = current_row_ - 1; i >= 0; --i) {
1057 if (rows_[i]->column_set())
1058 return rows_[i]->column_set();
1060 return NULL;
1063 } // namespace views