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"
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"
18 // LayoutElement ------------------------------------------------------
20 // A LayoutElement has a size and location along one axis. It contains
21 // methods that are used along both axis.
24 // Invokes ResetSize on all the layout elements.
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
) {
34 // Sets the location of each element to be the sum of the sizes of the
35 // preceding elements.
37 static void CalculateLocationsFromSize(std::vector
<T
*>* elements
) {
38 // Reset the layout width of each column.
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.
51 static void DistributeDelta(int delta
, std::vector
<T
*>* elements
) {
55 float total_percent
= 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)
63 if (total_percent
== 0) {
64 // None of the elements are resizable, 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
) {
72 if (element
->ResizePercent() > 0) {
77 to_give
= static_cast<int>(delta
*
78 (element
->resize_percent_
/ total_percent
));
81 element
->SetSize(element
->Size() + to_give
);
86 // Returns the sum of the size of the elements from start to start + length.
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()));
92 for (int i
= start
, max
= start
+ length
; i
< max
; ++i
) {
93 size
+= (*elements
)[i
]->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
;
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() {
125 void SetSize(int size
) {
133 void SetResizePercent(float percent
) {
134 resize_percent_
= percent
;
137 float ResizePercent() {
138 return resize_percent_
;
142 return resize_percent_
> 0;
146 float resize_percent_
;
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
{
159 Column(GridLayout::Alignment h_align
,
160 GridLayout::Alignment v_align
,
161 float resize_percent
,
162 GridLayout::SizeType size_type
,
166 : LayoutElement(resize_percent
),
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
) {}
178 GridLayout::Alignment
h_align() { return h_align_
; }
179 GridLayout::Alignment
v_align() { return v_align_
; }
181 virtual void ResetSize() OVERRIDE
;
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 virtual 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_
);
226 Column
* Column::GetLastMasterColumn() {
227 if (master_column_
== NULL
) {
230 if (master_column_
== this) {
233 return master_column_
->GetLastMasterColumn();
236 void Column::UnifySameSizedColumnSizes() {
237 DCHECK(master_column_
== this);
239 // Accumulate the size first.
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());
247 for (std::vector
<Column
*>::iterator i
= same_size_columns_
.begin();
248 i
!= same_size_columns_
.end(); ++i
) {
253 void Column::AdjustSize(int size
) {
254 if (size_type_
== GridLayout::USE_PREF
)
255 LayoutElement::AdjustSize(size
);
258 // Row -------------------------------------------------------------
260 class Row
: public LayoutElement
{
262 Row(int height
, float resize_percent
, ColumnSet
* column_set
)
263 : LayoutElement(resize_percent
),
265 column_set_(column_set
),
272 virtual void ResetSize() OVERRIDE
{
273 max_ascent_
= max_descent_
= 0;
277 ColumnSet
* 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 {
292 int max_descent() const {
298 // The column set used for this row; null for padding rows.
299 ColumnSet
* column_set_
;
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.
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
),
317 start_col(start_col
),
318 start_row(start_row
),
323 pref_width_fixed(pref_width
> 0),
324 pref_height_fixed(pref_height
> 0),
325 pref_width(pref_width
),
326 pref_height(pref_height
),
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
;
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.
353 // Used during layout. Gives how much width/height has not yet been
354 // distributed to the columns/rows the view is in.
356 int remaining_height
;
358 // The baseline. Only used if the view is vertically aligned along the
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
,
391 AddColumn(h_align
, v_align
, resize_percent
, size_type
, fixed_width
,
396 void ColumnSet::LinkColumnSizes(int first
, ...) {
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
;
409 void ColumnSet::AddColumn(GridLayout::Alignment h_align
,
410 GridLayout::Alignment v_align
,
411 float resize_percent
,
412 GridLayout::SizeType size_type
,
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(),
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
) {
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
);
450 // Column to link to is linked with other columns.
451 // Add current column to list of linked columns in other columns
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
;
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
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
) {
496 Column
* master_column
= column
->GetLastMasterColumn();
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
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
;
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
;
531 // The columns this view is in are big enough to accommodate it.
535 // Determine which columns are resizable, and which have a size type
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();
546 } else if (columns_
[i
]->size_type_
== GridLayout::USE_PREF
) {
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()) {
558 int delta
= (resize_i
== resizable_columns
) ? remaining_width
:
559 static_cast<int>(width
* columns_
[i
]->ResizePercent() /
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() {
582 for (std::vector
<Column
*>::iterator i
= columns_
.begin();
583 i
!= columns_
.end(); ++i
) {
584 width
+= (*i
)->Size();
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() {
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
)
655 calculated_master_columns_(false),
656 remaining_row_span_(0),
659 current_row_col_set_(NULL
),
660 adding_view_(false) {
664 GridLayout::~GridLayout() {
665 STLDeleteElements(&column_sets_
);
666 STLDeleteElements(&view_states_
);
667 STLDeleteElements(&rows_
);
671 GridLayout
* GridLayout::CreatePanel(View
* host
) {
672 GridLayout
* layout
= new GridLayout(host
);
673 layout
->SetInsets(kPanelVertMargin
, kButtonHEdgeMarginNew
,
674 kPanelVertMargin
, kButtonHEdgeMarginNew
);
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
) {
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
);
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
) {
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
);
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
) {
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
750 DCHECK(v_align
!= BASELINE
|| row_span
== 1);
752 new ViewState(current_row_col_set_
, view
, next_column_
, current_row_
,
753 col_span
, row_span
, h_align
, v_align
, pref_width
,
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
);
764 case GridLayout::LEADING
:
765 // Nothing to do, location already points to start.
767 case GridLayout::BASELINE
: // If we were asked to align on baseline, but
768 // the view doesn't have a baseline, fall back
770 case GridLayout::CENTER
:
771 *location
+= (available_size
- *size
) / 2;
773 case GridLayout::TRAILING
:
774 *location
= *location
+ available_size
- *size
;
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
803 SizeRowsAndColumns(true, host_
->width(), host_
->height(), &pref
);
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
;
812 int x
= column_set
->columns_
[view_state
->start_col
]->Location() +
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
,
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
;
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
);
834 SizeRowsAndColumns(false, 0, 0, &out
);
835 out
.SetSize(std::max(out
.width(), minimum_size_
.width()),
836 std::max(out
.height(), minimum_size_
.height()));
840 int GridLayout::GetPreferredHeightForWidth(const View
* host
, int width
) const {
841 DCHECK(host_
== host
);
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();
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() -
872 // And reset the x coordinates.
873 (*i
)->ResetColumnXCoordinates();
876 // Reset the height of each row.
877 LayoutElement::ResetSizes(&rows_
);
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.
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()) {
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(),
979 view_states_
.insert(i
, view_state
);
980 SkipPaddingColumns();
983 void GridLayout::AddRow(Row
* 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
989 DCHECK(remaining_row_span_
<= 0 ||
990 row
->column_set() == NULL
||
991 row
->column_set() == GetLastValidColumnSet());
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
;
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()) {
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
);
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_
)
1050 while (next_column_
< current_row_col_set_
->num_columns() &&
1051 current_row_col_set_
->columns_
[next_column_
]->is_padding_
) {
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();
1064 } // namespace views