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"
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();
61 if (total_percent
== 0) {
62 // None of the elements are resizable, return.
65 int remaining
= delta
;
66 int resized
= resize_count
;
67 for (typename
std::vector
<T
*>::iterator i
= elements
->begin();
68 i
!= elements
->end(); ++i
) {
70 if (element
->ResizePercent() > 0) {
75 to_give
= static_cast<int>(delta
*
76 (element
->resize_percent_
/ total_percent
));
79 element
->SetSize(element
->Size() + to_give
);
84 // Returns the sum of the size of the elements from start to start + length.
86 static int TotalSize(int start
, int length
, std::vector
<T
*>* elements
) {
87 DCHECK(start
>= 0 && length
> 0 &&
88 start
+ length
<= static_cast<int>(elements
->size()));
90 for (int i
= start
, max
= start
+ length
; i
< max
; ++i
) {
91 size
+= (*elements
)[i
]->Size();
96 explicit LayoutElement(float resize_percent
)
97 : resize_percent_(resize_percent
) {
98 DCHECK(resize_percent
>= 0);
101 virtual ~LayoutElement() {}
103 void SetLocation(int location
) {
104 location_
= location
;
111 // Adjusts the size of this LayoutElement to be the max of the current size
112 // and the specified size.
113 virtual void AdjustSize(int size
) {
114 size_
= std::max(size_
, size
);
117 // Resets the size to the initial size. This sets the size to 0, but
118 // subclasses that have a different initial size should override.
119 virtual void ResetSize() {
123 void SetSize(int size
) {
131 void SetResizePercent(float percent
) {
132 resize_percent_
= percent
;
135 float ResizePercent() {
136 return resize_percent_
;
140 return resize_percent_
> 0;
144 float resize_percent_
;
148 DISALLOW_COPY_AND_ASSIGN(LayoutElement
);
151 // Column -------------------------------------------------------------
153 // As the name implies, this represents a Column. Column contains default
154 // values for views originating in this column.
155 class Column
: public LayoutElement
{
157 Column(GridLayout::Alignment h_align
,
158 GridLayout::Alignment v_align
,
159 float resize_percent
,
160 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
),
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 // Index of this column in the ColumnSet.
205 const bool is_padding_
;
207 // If multiple columns have their sizes linked, one is the
208 // master column. The master column is identified by the
209 // master_column field being equal to itself. The master columns
210 // same_size_columns field contains the set of Columns with the
211 // the same size. Columns who are linked to other columns, but
212 // are not the master column have their master_column pointing to
213 // one of the other linked columns. Use the method GetLastMasterColumn
214 // to resolve the true master column.
215 std::vector
<Column
*> same_size_columns_
;
216 Column
* master_column_
;
218 DISALLOW_COPY_AND_ASSIGN(Column
);
221 void Column::ResetSize() {
222 if (size_type_
== GridLayout::FIXED
) {
223 SetSize(fixed_width_
);
229 Column
* Column::GetLastMasterColumn() {
230 if (master_column_
== NULL
) {
233 if (master_column_
== this) {
236 return master_column_
->GetLastMasterColumn();
239 void Column::UnifySameSizedColumnSizes() {
240 DCHECK(master_column_
== this);
242 // Accumulate the size first.
244 for (std::vector
<Column
*>::iterator i
= same_size_columns_
.begin();
245 i
!= same_size_columns_
.end(); ++i
) {
246 size
= std::max(size
, (*i
)->Size());
250 for (std::vector
<Column
*>::iterator i
= same_size_columns_
.begin();
251 i
!= same_size_columns_
.end(); ++i
) {
256 void Column::AdjustSize(int size
) {
257 if (size_type_
== GridLayout::USE_PREF
)
258 LayoutElement::AdjustSize(size
);
261 // Row -------------------------------------------------------------
263 class Row
: public LayoutElement
{
265 Row(bool fixed_height
, int height
, float resize_percent
,
266 ColumnSet
* column_set
)
267 : LayoutElement(resize_percent
),
268 fixed_height_(fixed_height
),
270 column_set_(column_set
),
277 virtual void ResetSize() OVERRIDE
{
278 max_ascent_
= max_descent_
= 0;
282 ColumnSet
* column_set() {
286 // Adjusts the size to accomodate the specified ascent/descent.
287 void AdjustSizeForBaseline(int ascent
, int descent
) {
288 max_ascent_
= std::max(ascent
, max_ascent_
);
289 max_descent_
= std::max(descent
, max_descent_
);
290 AdjustSize(max_ascent_
+ max_descent_
);
293 int max_ascent() const {
297 int max_descent() const {
302 const bool fixed_height_
;
304 // The column set used for this row; null for padding rows.
305 ColumnSet
* column_set_
;
310 DISALLOW_COPY_AND_ASSIGN(Row
);
313 // ViewState -------------------------------------------------------------
315 // Identifies the location in the grid of a particular view, along with
316 // placement information and size information.
318 ViewState(ColumnSet
* column_set
, View
* view
, int start_col
, int start_row
,
319 int col_span
, int row_span
, GridLayout::Alignment h_align
,
320 GridLayout::Alignment v_align
, int pref_width
, int pref_height
)
321 : column_set(column_set
),
323 start_col(start_col
),
324 start_row(start_row
),
329 pref_width_fixed(pref_width
> 0),
330 pref_height_fixed(pref_height
> 0),
331 pref_width(pref_width
),
332 pref_height(pref_height
),
336 DCHECK(view
&& start_col
>= 0 && start_row
>= 0 && col_span
> 0 &&
337 row_span
> 0 && start_col
< column_set
->num_columns() &&
338 (start_col
+ col_span
) <= column_set
->num_columns());
341 ColumnSet
* const column_set
;
347 const GridLayout::Alignment h_align
;
348 const GridLayout::Alignment v_align
;
350 // If true, the pref_width/pref_height were explicitly set and the view's
351 // preferred size is ignored.
352 const bool pref_width_fixed
;
353 const bool pref_height_fixed
;
355 // The preferred width/height. These are reset during the layout process.
359 // Used during layout. Gives how much width/height has not yet been
360 // distributed to the columns/rows the view is in.
362 int remaining_height
;
364 // The baseline. Only used if the view is vertically aligned along the
369 static bool CompareByColumnSpan(const ViewState
* v1
, const ViewState
* v2
) {
370 return v1
->col_span
< v2
->col_span
;
373 static bool CompareByRowSpan(const ViewState
* v1
, const ViewState
* v2
) {
374 return v1
->row_span
< v2
->row_span
;
377 // ColumnSet -------------------------------------------------------------
379 ColumnSet::ColumnSet(int id
) : id_(id
) {
382 ColumnSet::~ColumnSet() {
383 STLDeleteElements(&columns_
);
386 void ColumnSet::AddPaddingColumn(float resize_percent
, int width
) {
387 AddColumn(GridLayout::FILL
, GridLayout::FILL
, resize_percent
,
388 GridLayout::FIXED
, width
, width
, true);
391 void ColumnSet::AddColumn(GridLayout::Alignment h_align
,
392 GridLayout::Alignment v_align
,
393 float resize_percent
,
394 GridLayout::SizeType size_type
,
397 AddColumn(h_align
, v_align
, resize_percent
, size_type
, fixed_width
,
402 void ColumnSet::LinkColumnSizes(int first
, ...) {
404 va_start(marker
, first
);
405 DCHECK(first
>= 0 && first
< num_columns());
406 for (int last
= first
, next
= va_arg(marker
, int); next
!= -1;
407 next
= va_arg(marker
, int)) {
408 DCHECK(next
>= 0 && next
< num_columns());
409 columns_
[last
]->same_size_column_
= next
;
415 void ColumnSet::AddColumn(GridLayout::Alignment h_align
,
416 GridLayout::Alignment v_align
,
417 float resize_percent
,
418 GridLayout::SizeType size_type
,
422 Column
* column
= new Column(h_align
, v_align
, resize_percent
, size_type
,
423 fixed_width
, min_width
, columns_
.size(),
425 columns_
.push_back(column
);
428 void ColumnSet::AddViewState(ViewState
* view_state
) {
429 // view_states are ordered by column_span (in ascending order).
430 std::vector
<ViewState
*>::iterator i
= lower_bound(view_states_
.begin(),
433 CompareByColumnSpan
);
434 view_states_
.insert(i
, view_state
);
437 void ColumnSet::CalculateMasterColumns() {
438 for (std::vector
<Column
*>::iterator i
= columns_
.begin();
439 i
!= columns_
.end(); ++i
) {
441 int same_size_column_index
= column
->same_size_column_
;
442 if (same_size_column_index
!= -1) {
443 DCHECK(same_size_column_index
>= 0 &&
444 same_size_column_index
< static_cast<int>(columns_
.size()));
445 Column
* master_column
= column
->master_column_
;
446 Column
* same_size_column
= columns_
[same_size_column_index
];
447 Column
* same_size_column_master
= same_size_column
->master_column_
;
448 if (master_column
== NULL
) {
449 // Current column is not linked to any other column.
450 if (same_size_column_master
== NULL
) {
451 // Both columns are not linked.
452 column
->master_column_
= column
;
453 same_size_column
->master_column_
= column
;
454 column
->same_size_columns_
.push_back(same_size_column
);
455 column
->same_size_columns_
.push_back(column
);
457 // Column to link to is linked with other columns.
458 // Add current column to list of linked columns in other columns
460 same_size_column
->GetLastMasterColumn()->
461 same_size_columns_
.push_back(column
);
462 // And update the master column for the current column to that
463 // of the same sized column.
464 column
->master_column_
= same_size_column
;
467 // Current column is already linked with another column.
468 if (same_size_column_master
== NULL
) {
469 // Column to link with is not linked to any other columns.
470 // Update it's master_column.
471 same_size_column
->master_column_
= column
;
472 // Add linked column to list of linked column.
473 column
->GetLastMasterColumn()->same_size_columns_
.
474 push_back(same_size_column
);
475 } else if (column
->GetLastMasterColumn() !=
476 same_size_column
->GetLastMasterColumn()) {
477 // The two columns are already linked with other columns.
478 std::vector
<Column
*>* same_size_columns
=
479 &(column
->GetLastMasterColumn()->same_size_columns_
);
480 std::vector
<Column
*>* other_same_size_columns
=
481 &(same_size_column
->GetLastMasterColumn()->same_size_columns_
);
482 // Add all the columns from the others master to current columns
484 same_size_columns
->insert(same_size_columns
->end(),
485 other_same_size_columns
->begin(),
486 other_same_size_columns
->end());
487 // The other master is no longer a master, clear its vector of
488 // linked columns, and reset its master_column.
489 other_same_size_columns
->clear();
490 same_size_column
->GetLastMasterColumn()->master_column_
= column
;
495 AccumulateMasterColumns();
498 void ColumnSet::AccumulateMasterColumns() {
499 DCHECK(master_columns_
.empty());
500 for (std::vector
<Column
*>::iterator i
= columns_
.begin();
501 i
!= columns_
.end(); ++i
) {
503 Column
* master_column
= column
->GetLastMasterColumn();
505 find(master_columns_
.begin(), master_columns_
.end(),
506 master_column
) == master_columns_
.end()) {
507 master_columns_
.push_back(master_column
);
509 // At this point, GetLastMasterColumn may not == master_column
510 // (may have to go through a few Columns)_. Reset master_column to
512 column
->master_column_
= master_column
;
516 void ColumnSet::UnifySameSizedColumnSizes() {
517 for (std::vector
<Column
*>::iterator i
= master_columns_
.begin();
518 i
!= master_columns_
.end(); ++i
) {
519 (*i
)->UnifySameSizedColumnSizes();
523 void ColumnSet::UpdateRemainingWidth(ViewState
* view_state
) {
524 for (int i
= view_state
->start_col
; i
< view_state
->col_span
; ++i
) {
525 view_state
->remaining_width
-= columns_
[i
]->Size();
529 void ColumnSet::DistributeRemainingWidth(ViewState
* view_state
) {
530 // This is nearly the same as that for rows, but differs in so far as how
531 // Rows and Columns are treated. Rows have two states, resizable or not.
532 // Columns have three, resizable, USE_PREF or not resizable. This results
533 // in slightly different handling for distributing unaccounted size.
534 int width
= view_state
->remaining_width
;
536 // The columns this view is in are big enough to accommodate it.
540 // Determine which columns are resizable, and which have a size type
542 int resizable_columns
= 0;
543 int pref_size_columns
= 0;
544 int start_col
= view_state
->start_col
;
545 int max_col
= view_state
->start_col
+ view_state
->col_span
;
546 float total_resize
= 0;
547 for (int i
= start_col
; i
< max_col
; ++i
) {
548 if (columns_
[i
]->IsResizable()) {
549 total_resize
+= columns_
[i
]->ResizePercent();
551 } else if (columns_
[i
]->size_type_
== GridLayout::USE_PREF
) {
556 if (resizable_columns
> 0) {
557 // There are resizable columns, give them the remaining width. The extra
558 // width is distributed using the resize values of each column.
559 int remaining_width
= width
;
560 for (int i
= start_col
, resize_i
= 0; i
< max_col
; ++i
) {
561 if (columns_
[i
]->IsResizable()) {
563 int delta
= (resize_i
== resizable_columns
) ? remaining_width
:
564 static_cast<int>(width
* columns_
[i
]->ResizePercent() /
566 remaining_width
-= delta
;
567 columns_
[i
]->SetSize(columns_
[i
]->Size() + delta
);
570 } else if (pref_size_columns
> 0) {
571 // None of the columns are resizable, distribute the width among those
572 // that use the preferred size.
573 int to_distribute
= width
/ pref_size_columns
;
574 for (int i
= start_col
; i
< max_col
; ++i
) {
575 if (columns_
[i
]->size_type_
== GridLayout::USE_PREF
) {
576 width
-= to_distribute
;
577 if (width
< to_distribute
)
578 to_distribute
+= width
;
579 columns_
[i
]->SetSize(columns_
[i
]->Size() + to_distribute
);
585 int ColumnSet::LayoutWidth() {
587 for (std::vector
<Column
*>::iterator i
= columns_
.begin();
588 i
!= columns_
.end(); ++i
) {
589 width
+= (*i
)->Size();
594 int ColumnSet::GetColumnWidth(int start_col
, int col_span
) {
595 return LayoutElement::TotalSize(start_col
, col_span
, &columns_
);
598 void ColumnSet::ResetColumnXCoordinates() {
599 LayoutElement::CalculateLocationsFromSize(&columns_
);
602 void ColumnSet::CalculateSize() {
604 // Reset the preferred and remaining sizes.
605 for (std::vector
<ViewState
*>::iterator i
= view_states_
.begin();
606 i
!= view_states_
.end(); ++i
) {
607 ViewState
* view_state
= *i
;
608 if (!view_state
->pref_width_fixed
|| !view_state
->pref_height_fixed
) {
609 pref
= view_state
->view
->GetPreferredSize();
610 if (!view_state
->pref_width_fixed
)
611 view_state
->pref_width
= pref
.width();
612 if (!view_state
->pref_height_fixed
)
613 view_state
->pref_height
= pref
.height();
615 view_state
->remaining_width
= pref
.width();
616 view_state
->remaining_height
= pref
.height();
619 // Let layout element reset the sizes for us.
620 LayoutElement::ResetSizes(&columns_
);
622 // Distribute the size of each view with a col span == 1.
623 std::vector
<ViewState
*>::iterator view_state_iterator
=
624 view_states_
.begin();
625 for (; view_state_iterator
!= view_states_
.end() &&
626 (*view_state_iterator
)->col_span
== 1; ++view_state_iterator
) {
627 ViewState
* view_state
= *view_state_iterator
;
628 Column
* column
= columns_
[view_state
->start_col
];
629 column
->AdjustSize(view_state
->pref_width
);
630 view_state
->remaining_width
-= column
->Size();
633 // Make sure all linked columns have the same size.
634 UnifySameSizedColumnSizes();
636 // Distribute the size of each view with a column span > 1.
637 for (; view_state_iterator
!= view_states_
.end(); ++view_state_iterator
) {
638 ViewState
* view_state
= *view_state_iterator
;
640 // Update the remaining_width from columns this view_state touches.
641 UpdateRemainingWidth(view_state
);
643 // Distribute the remaining width.
644 DistributeRemainingWidth(view_state
);
646 // Update the size of linked columns.
647 // This may need to be combined with previous step.
648 UnifySameSizedColumnSizes();
652 void ColumnSet::Resize(int delta
) {
653 LayoutElement::DistributeDelta(delta
, &columns_
);
656 // GridLayout -------------------------------------------------------------
658 GridLayout::GridLayout(View
* host
)
660 calculated_master_columns_(false),
661 remaining_row_span_(0),
664 current_row_col_set_(NULL
),
665 adding_view_(false) {
669 GridLayout::~GridLayout() {
670 STLDeleteElements(&column_sets_
);
671 STLDeleteElements(&view_states_
);
672 STLDeleteElements(&rows_
);
676 GridLayout
* GridLayout::CreatePanel(View
* host
) {
677 GridLayout
* layout
= new GridLayout(host
);
678 layout
->SetInsets(kPanelVertMargin
, kPanelHorizMargin
,
679 kPanelVertMargin
, kPanelHorizMargin
);
683 void GridLayout::SetInsets(int top
, int left
, int bottom
, int right
) {
684 insets_
.Set(top
, left
, bottom
, right
);
687 void GridLayout::SetInsets(const gfx::Insets
& insets
) {
691 ColumnSet
* GridLayout::AddColumnSet(int id
) {
692 DCHECK(GetColumnSet(id
) == NULL
);
693 ColumnSet
* column_set
= new ColumnSet(id
);
694 column_sets_
.push_back(column_set
);
698 ColumnSet
* GridLayout::GetColumnSet(int id
) {
699 for (std::vector
<ColumnSet
*>::iterator i
= column_sets_
.begin();
700 i
!= column_sets_
.end(); ++i
) {
701 if ((*i
)->id_
== id
) {
708 void GridLayout::StartRowWithPadding(float vertical_resize
, int column_set_id
,
709 float padding_resize
, int padding
) {
710 AddPaddingRow(padding_resize
, padding
);
711 StartRow(vertical_resize
, column_set_id
);
714 void GridLayout::StartRow(float vertical_resize
, int column_set_id
) {
715 ColumnSet
* column_set
= GetColumnSet(column_set_id
);
717 AddRow(new Row(false, 0, vertical_resize
, column_set
));
720 void GridLayout::AddPaddingRow(float vertical_resize
, int pixel_count
) {
721 AddRow(new Row(true, pixel_count
, vertical_resize
, NULL
));
724 void GridLayout::SkipColumns(int col_count
) {
725 DCHECK(col_count
> 0);
726 next_column_
+= col_count
;
727 DCHECK(current_row_col_set_
&&
728 next_column_
<= current_row_col_set_
->num_columns());
729 SkipPaddingColumns();
732 void GridLayout::AddView(View
* view
) {
736 void GridLayout::AddView(View
* view
, int col_span
, int row_span
) {
737 DCHECK(current_row_col_set_
&&
738 next_column_
< current_row_col_set_
->num_columns());
739 Column
* column
= current_row_col_set_
->columns_
[next_column_
];
740 AddView(view
, col_span
, row_span
, column
->h_align(), column
->v_align());
743 void GridLayout::AddView(View
* view
, int col_span
, int row_span
,
744 Alignment h_align
, Alignment v_align
) {
745 AddView(view
, col_span
, row_span
, h_align
, v_align
, 0, 0);
748 void GridLayout::AddView(View
* view
, int col_span
, int row_span
,
749 Alignment h_align
, Alignment v_align
,
750 int pref_width
, int pref_height
) {
751 DCHECK(current_row_col_set_
&& col_span
> 0 && row_span
> 0 &&
752 (next_column_
+ col_span
) <= current_row_col_set_
->num_columns());
753 // We don't support baseline alignment of views spanning rows. Please add if
755 DCHECK(v_align
!= BASELINE
|| row_span
== 1);
757 new ViewState(current_row_col_set_
, view
, next_column_
, current_row_
,
758 col_span
, row_span
, h_align
, v_align
, pref_width
,
763 static void CalculateSize(int pref_size
, GridLayout::Alignment alignment
,
764 int* location
, int* size
) {
765 if (alignment
!= GridLayout::FILL
) {
766 int available_size
= *size
;
767 *size
= std::min(*size
, pref_size
);
769 case GridLayout::LEADING
:
770 // Nothing to do, location already points to start.
772 case GridLayout::BASELINE
: // If we were asked to align on baseline, but
773 // the view doesn't have a baseline, fall back
775 case GridLayout::CENTER
:
776 *location
+= (available_size
- *size
) / 2;
778 case GridLayout::TRAILING
:
779 *location
= *location
+ available_size
- *size
;
787 void GridLayout::Installed(View
* host
) {
788 DCHECK(host_
== host
);
791 void GridLayout::Uninstalled(View
* host
) {
792 DCHECK(host_
== host
);
795 void GridLayout::ViewAdded(View
* host
, View
* view
) {
796 DCHECK(host_
== host
&& adding_view_
);
799 void GridLayout::ViewRemoved(View
* host
, View
* view
) {
800 DCHECK(host_
== host
);
803 void GridLayout::Layout(View
* host
) {
804 DCHECK(host_
== host
);
805 // SizeRowsAndColumns sets the size and location of each row/column, but
808 SizeRowsAndColumns(true, host_
->width(), host_
->height(), &pref
);
811 for (std::vector
<ViewState
*>::iterator i
= view_states_
.begin();
812 i
!= view_states_
.end(); ++i
) {
813 ViewState
* view_state
= *i
;
814 ColumnSet
* column_set
= view_state
->column_set
;
815 View
* view
= (*i
)->view
;
817 int x
= column_set
->columns_
[view_state
->start_col
]->Location() +
819 int width
= column_set
->GetColumnWidth(view_state
->start_col
,
820 view_state
->col_span
);
821 CalculateSize(view_state
->pref_width
, view_state
->h_align
,
823 int y
= rows_
[view_state
->start_row
]->Location() + insets_
.top();
824 int height
= LayoutElement::TotalSize(view_state
->start_row
,
825 view_state
->row_span
, &rows_
);
826 if (view_state
->v_align
== BASELINE
&& view_state
->baseline
!= -1) {
827 y
+= rows_
[view_state
->start_row
]->max_ascent() - view_state
->baseline
;
828 height
= view_state
->pref_height
;
830 CalculateSize(view_state
->pref_height
, view_state
->v_align
, &y
, &height
);
832 view
->SetBounds(x
, y
, width
, height
);
836 gfx::Size
GridLayout::GetPreferredSize(View
* host
) {
837 DCHECK(host_
== host
);
839 SizeRowsAndColumns(false, 0, 0, &out
);
840 out
.SetSize(std::max(out
.width(), minimum_size_
.width()),
841 std::max(out
.height(), minimum_size_
.height()));
845 int GridLayout::GetPreferredHeightForWidth(View
* host
, int width
) {
846 DCHECK(host_
== host
);
848 SizeRowsAndColumns(false, width
, 0, &pref
);
849 return pref
.height();
852 void GridLayout::SizeRowsAndColumns(bool layout
, int width
, int height
,
854 // Make sure the master columns have been calculated.
855 CalculateMasterColumnsIfNecessary();
860 // Calculate the preferred width of each of the columns. Some views'
861 // preferred heights are derived from their width, as such we need to
862 // calculate the size of the columns first.
863 for (std::vector
<ColumnSet
*>::iterator i
= column_sets_
.begin();
864 i
!= column_sets_
.end(); ++i
) {
865 (*i
)->CalculateSize();
866 pref
->set_width(std::max(pref
->width(), (*i
)->LayoutWidth()));
868 pref
->set_width(pref
->width() + insets_
.width());
870 // Go over the columns again and set them all to the size we settled for.
871 width
= width
? width
: pref
->width();
872 for (std::vector
<ColumnSet
*>::iterator i
= column_sets_
.begin();
873 i
!= column_sets_
.end(); ++i
) {
874 // We're doing a layout, divy up any extra space.
875 (*i
)->Resize(width
- (*i
)->LayoutWidth() - insets_
.left() -
877 // And reset the x coordinates.
878 (*i
)->ResetColumnXCoordinates();
881 // Reset the height of each row.
882 LayoutElement::ResetSizes(&rows_
);
885 // . If the view is aligned along it's baseline, obtain the baseline from the
886 // view and update the rows ascent/descent.
887 // . Reset the remaining_height of each view state.
888 // . If the width the view will be given is different than it's pref, ask
889 // for the height given a particularly width.
890 for (std::vector
<ViewState
*>::iterator i
= view_states_
.begin();
891 i
!= view_states_
.end() ; ++i
) {
892 ViewState
* view_state
= *i
;
893 view_state
->remaining_height
= view_state
->pref_height
;
895 if (view_state
->v_align
== BASELINE
)
896 view_state
->baseline
= view_state
->view
->GetBaseline();
898 if (view_state
->h_align
== FILL
) {
899 // The view is resizable. As the pref height may vary with the width,
900 // ask for the pref again.
902 view_state
->column_set
->GetColumnWidth(view_state
->start_col
,
903 view_state
->col_span
);
904 if (actual_width
!= view_state
->pref_width
&&
905 !view_state
->pref_height_fixed
) {
906 // The width this view will get differs from its preferred. Some Views
907 // pref height varies with its width; ask for the preferred again.
908 view_state
->pref_height
=
909 view_state
->view
->GetHeightForWidth(actual_width
);
910 view_state
->remaining_height
= view_state
->pref_height
;
915 // Update the height/ascent/descent of each row from the views.
916 std::vector
<ViewState
*>::iterator view_states_iterator
= view_states_
.begin();
917 for (; view_states_iterator
!= view_states_
.end() &&
918 (*view_states_iterator
)->row_span
== 1; ++view_states_iterator
) {
919 ViewState
* view_state
= *view_states_iterator
;
920 Row
* row
= rows_
[view_state
->start_row
];
921 row
->AdjustSize(view_state
->remaining_height
);
922 if (view_state
->baseline
!= -1 &&
923 view_state
->baseline
<= view_state
->pref_height
) {
924 row
->AdjustSizeForBaseline(view_state
->baseline
,
925 view_state
->pref_height
- view_state
->baseline
);
927 view_state
->remaining_height
= 0;
930 // Distribute the height of each view with a row span > 1.
931 for (; view_states_iterator
!= view_states_
.end(); ++view_states_iterator
) {
932 ViewState
* view_state
= *view_states_iterator
;
934 // Update the remaining_width from columns this view_state touches.
935 UpdateRemainingHeightFromRows(view_state
);
937 // Distribute the remaining height.
938 DistributeRemainingHeight(view_state
);
941 // Update the location of each of the rows.
942 LayoutElement::CalculateLocationsFromSize(&rows_
);
944 // We now know the preferred height, set it here.
945 pref
->set_height(rows_
[rows_
.size() - 1]->Location() +
946 rows_
[rows_
.size() - 1]->Size() + insets_
.height());
948 if (layout
&& height
!= pref
->height()) {
949 // We're doing a layout, and the height differs from the preferred height,
950 // divy up the extra space.
951 LayoutElement::DistributeDelta(height
- pref
->height(), &rows_
);
953 // Reset y locations.
954 LayoutElement::CalculateLocationsFromSize(&rows_
);
958 void GridLayout::CalculateMasterColumnsIfNecessary() {
959 if (!calculated_master_columns_
) {
960 calculated_master_columns_
= true;
961 for (std::vector
<ColumnSet
*>::iterator i
= column_sets_
.begin();
962 i
!= column_sets_
.end(); ++i
) {
963 (*i
)->CalculateMasterColumns();
968 void GridLayout::AddViewState(ViewState
* view_state
) {
969 DCHECK(view_state
->view
&& (view_state
->view
->parent() == NULL
||
970 view_state
->view
->parent() == host_
));
971 if (!view_state
->view
->parent()) {
973 host_
->AddChildView(view_state
->view
);
974 adding_view_
= false;
976 remaining_row_span_
= std::max(remaining_row_span_
, view_state
->row_span
);
977 next_column_
+= view_state
->col_span
;
978 current_row_col_set_
->AddViewState(view_state
);
979 // view_states are ordered by row_span (in ascending order).
980 std::vector
<ViewState
*>::iterator i
= lower_bound(view_states_
.begin(),
984 view_states_
.insert(i
, view_state
);
985 SkipPaddingColumns();
988 void GridLayout::AddRow(Row
* row
) {
990 remaining_row_span_
--;
991 DCHECK(remaining_row_span_
<= 0 ||
992 row
->column_set() == NULL
||
993 row
->column_set() == GetLastValidColumnSet());
995 rows_
.push_back(row
);
996 current_row_col_set_
= row
->column_set();
997 SkipPaddingColumns();
1000 void GridLayout::UpdateRemainingHeightFromRows(ViewState
* view_state
) {
1001 for (int i
= 0, start_row
= view_state
->start_row
;
1002 i
< view_state
->row_span
; ++i
) {
1003 view_state
->remaining_height
-= rows_
[i
+ start_row
]->Size();
1007 void GridLayout::DistributeRemainingHeight(ViewState
* view_state
) {
1008 int height
= view_state
->remaining_height
;
1012 // Determine the number of resizable rows the view touches.
1013 int resizable_rows
= 0;
1014 int start_row
= view_state
->start_row
;
1015 int max_row
= view_state
->start_row
+ view_state
->row_span
;
1016 for (int i
= start_row
; i
< max_row
; ++i
) {
1017 if (rows_
[i
]->IsResizable()) {
1022 if (resizable_rows
> 0) {
1023 // There are resizable rows, give the remaining height to them.
1024 int to_distribute
= height
/ resizable_rows
;
1025 for (int i
= start_row
; i
< max_row
; ++i
) {
1026 if (rows_
[i
]->IsResizable()) {
1027 height
-= to_distribute
;
1028 if (height
< to_distribute
) {
1029 // Give all slop to the last column.
1030 to_distribute
+= height
;
1032 rows_
[i
]->SetSize(rows_
[i
]->Size() + to_distribute
);
1036 // None of the rows are resizable, divy the remaining height up equally
1037 // among all rows the view touches.
1038 int each_row_height
= height
/ view_state
->row_span
;
1039 for (int i
= start_row
; i
< max_row
; ++i
) {
1040 height
-= each_row_height
;
1041 if (height
< each_row_height
)
1042 each_row_height
+= height
;
1043 rows_
[i
]->SetSize(rows_
[i
]->Size() + each_row_height
);
1045 view_state
->remaining_height
= 0;
1049 void GridLayout::SkipPaddingColumns() {
1050 if (!current_row_col_set_
)
1052 while (next_column_
< current_row_col_set_
->num_columns() &&
1053 current_row_col_set_
->columns_
[next_column_
]->is_padding_
) {
1058 ColumnSet
* GridLayout::GetLastValidColumnSet() {
1059 for (int i
= current_row_
- 1; i
>= 0; --i
) {
1060 if (rows_
[i
]->column_set())
1061 return rows_
[i
]->column_set();
1066 } // namespace views