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/views/layout/layout_constants.h"
12 #include "ui/views/view.h"
13 #include "ui/views/window/dialog_delegate.h"
17 // LayoutElement ------------------------------------------------------
19 // A LayoutElement has a size and location along one axis. It contains
20 // methods that are used along both axis.
23 // Invokes ResetSize on all the layout elements.
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
) {
33 // Sets the location of each element to be the sum of the sizes of the
34 // preceding elements.
36 static void CalculateLocationsFromSize(std::vector
<T
*>* elements
) {
37 // Reset the layout width of each column.
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.
50 static void DistributeDelta(int delta
, std::vector
<T
*>* elements
) {
54 float total_percent
= 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)
62 if (total_percent
== 0) {
63 // None of the elements are resizable, 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
) {
71 if (element
->ResizePercent() > 0) {
76 to_give
= static_cast<int>(delta
*
77 (element
->resize_percent_
/ total_percent
));
80 element
->SetSize(element
->Size() + to_give
);
85 // Returns the sum of the size of the elements from start to start + length.
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()));
91 for (int i
= start
, max
= start
+ length
; i
< max
; ++i
) {
92 size
+= (*elements
)[i
]->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
;
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() {
124 void SetSize(int size
) {
132 void SetResizePercent(float percent
) {
133 resize_percent_
= percent
;
136 float ResizePercent() {
137 return resize_percent_
;
141 return resize_percent_
> 0;
145 float resize_percent_
;
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
{
158 Column(GridLayout::Alignment h_align
,
159 GridLayout::Alignment v_align
,
160 float resize_percent
,
161 GridLayout::SizeType size_type
,
165 : LayoutElement(resize_percent
),
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
;
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_
);
225 Column
* Column::GetLastMasterColumn() {
226 if (master_column_
== NULL
) {
229 if (master_column_
== this) {
232 return master_column_
->GetLastMasterColumn();
235 void Column::UnifySameSizedColumnSizes() {
236 DCHECK(master_column_
== this);
238 // Accumulate the size first.
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());
246 for (std::vector
<Column
*>::iterator i
= same_size_columns_
.begin();
247 i
!= same_size_columns_
.end(); ++i
) {
252 void Column::AdjustSize(int size
) {
253 if (size_type_
== GridLayout::USE_PREF
)
254 LayoutElement::AdjustSize(size
);
257 // Row -------------------------------------------------------------
259 class Row
: public LayoutElement
{
261 Row(int height
, float resize_percent
, ColumnSet
* column_set
)
262 : LayoutElement(resize_percent
),
264 column_set_(column_set
),
271 void ResetSize() override
{
272 max_ascent_
= max_descent_
= 0;
276 ColumnSet
* 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 {
291 int max_descent() const {
297 // The column set used for this row; null for padding rows.
298 ColumnSet
* column_set_
;
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.
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
),
316 start_col(start_col
),
317 start_row(start_row
),
322 pref_width_fixed(pref_width
> 0),
323 pref_height_fixed(pref_height
> 0),
324 pref_width(pref_width
),
325 pref_height(pref_height
),
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
;
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.
352 // Used during layout. Gives how much width/height has not yet been
353 // distributed to the columns/rows the view is in.
355 int remaining_height
;
357 // The baseline. Only used if the view is vertically aligned along the
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
,
390 AddColumn(h_align
, v_align
, resize_percent
, size_type
, fixed_width
,
395 void ColumnSet::LinkColumnSizes(int first
, ...) {
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
;
408 void ColumnSet::AddColumn(GridLayout::Alignment h_align
,
409 GridLayout::Alignment v_align
,
410 float resize_percent
,
411 GridLayout::SizeType size_type
,
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(),
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
) {
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
);
449 // Column to link to is linked with other columns.
450 // Add current column to list of linked columns in other columns
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
;
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
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
) {
495 Column
* master_column
= column
->GetLastMasterColumn();
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
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
;
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
;
530 // The columns this view is in are big enough to accommodate it.
534 // Determine which columns are resizable, and which have a size type
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();
545 } else if (columns_
[i
]->size_type_
== GridLayout::USE_PREF
) {
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()) {
557 int delta
= (resize_i
== resizable_columns
) ? remaining_width
:
558 static_cast<int>(width
* columns_
[i
]->ResizePercent() /
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() {
581 for (std::vector
<Column
*>::iterator i
= columns_
.begin();
582 i
!= columns_
.end(); ++i
) {
583 width
+= (*i
)->Size();
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() {
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
)
654 calculated_master_columns_(false),
655 remaining_row_span_(0),
658 current_row_col_set_(NULL
),
659 adding_view_(false) {
663 GridLayout::~GridLayout() {
664 STLDeleteElements(&column_sets_
);
665 STLDeleteElements(&view_states_
);
666 STLDeleteElements(&rows_
);
670 GridLayout
* GridLayout::CreatePanel(View
* host
) {
671 GridLayout
* layout
= new GridLayout(host
);
672 layout
->SetInsets(kPanelVertMargin
, kButtonHEdgeMarginNew
,
673 kPanelVertMargin
, kButtonHEdgeMarginNew
);
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
) {
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
);
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
) {
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
);
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
) {
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
749 DCHECK(v_align
!= BASELINE
|| row_span
== 1);
751 new ViewState(current_row_col_set_
, view
, next_column_
, current_row_
,
752 col_span
, row_span
, h_align
, v_align
, pref_width
,
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
);
763 case GridLayout::LEADING
:
764 // Nothing to do, location already points to start.
766 case GridLayout::BASELINE
: // If we were asked to align on baseline, but
767 // the view doesn't have a baseline, fall back
769 case GridLayout::CENTER
:
770 *location
+= (available_size
- *size
) / 2;
772 case GridLayout::TRAILING
:
773 *location
= *location
+ available_size
- *size
;
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
802 SizeRowsAndColumns(true, host_
->width(), host_
->height(), &pref
);
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
;
811 int x
= column_set
->columns_
[view_state
->start_col
]->Location() +
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
,
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
;
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
);
833 SizeRowsAndColumns(false, 0, 0, &out
);
834 out
.SetSize(std::max(out
.width(), minimum_size_
.width()),
835 std::max(out
.height(), minimum_size_
.height()));
839 int GridLayout::GetPreferredHeightForWidth(const View
* host
, int width
) const {
840 DCHECK(host_
== host
);
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();
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() -
871 // And reset the x coordinates.
872 (*i
)->ResetColumnXCoordinates();
875 // Reset the height of each row.
876 LayoutElement::ResetSizes(&rows_
);
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.
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()) {
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(),
978 view_states_
.insert(i
, view_state
);
979 SkipPaddingColumns();
982 void GridLayout::AddRow(Row
* 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
988 DCHECK(remaining_row_span_
<= 0 ||
989 row
->column_set() == NULL
||
990 row
->column_set() == GetLastValidColumnSet());
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
;
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()) {
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
);
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_
)
1049 while (next_column_
< current_row_col_set_
->num_columns() &&
1050 current_row_col_set_
->columns_
[next_column_
]->is_padding_
) {
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();
1063 } // namespace views