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.
4
5 #include "ui/views/layout/grid_layout.h"
6
7 #include <algorithm>
8
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"
15
16 namespace views {
17
18 // LayoutElement ------------------------------------------------------
19
20 // A LayoutElement has a size and location along one axis. It contains
21 // methods that are used along both axis.
22 class LayoutElement {
23 public:
24 // Invokes ResetSize on all the layout elements.
25 template <class T>
ResetSizes(std::vector<T * > * 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) {
30 (*i)->ResetSize();
31 }
32 }
33
34 // Sets the location of each element to be the sum of the sizes of the
35 // preceding elements.
36 template <class T>
CalculateLocationsFromSize(std::vector<T * > * elements)37 static void CalculateLocationsFromSize(std::vector<T*>* elements) {
38 // Reset the layout width of each column.
39 int location = 0;
40 for (typename std::vector<T*>::iterator i = elements->begin();
41 i != elements->end(); ++i) {
42 (*i)->SetLocation(location);
43 location += (*i)->Size();
44 }
45 }
46
47 // Distributes delta among the resizable elements.
48 // Each resizable element is given ResizePercent / total_percent * delta
49 // pixels extra of space.
50 template <class T>
DistributeDelta(int delta,std::vector<T * > * elements)51 static void DistributeDelta(int delta, std::vector<T*>* elements) {
52 if (delta == 0)
53 return;
54
55 float total_percent = 0;
56 int resize_count = 0;
57 for (typename std::vector<T*>::iterator i = elements->begin();
58 i != elements->end(); ++i) {
59 total_percent += (*i)->ResizePercent();
60 resize_count++;
61 }
62 if (total_percent == 0) {
63 // None of the elements are resizable, return.
64 return;
65 }
66 int remaining = delta;
67 int resized = resize_count;
68 for (typename std::vector<T*>::iterator i = elements->begin();
69 i != elements->end(); ++i) {
70 T* element = *i;
71 if (element->ResizePercent() > 0) {
72 int to_give;
73 if (--resized == 0) {
74 to_give = remaining;
75 } else {
76 to_give = static_cast<int>(delta *
77 (element->resize_percent_ / total_percent));
78 remaining -= to_give;
79 }
80 element->SetSize(element->Size() + to_give);
81 }
82 }
83 }
84
85 // Returns the sum of the size of the elements from start to start + length.
86 template <class T>
TotalSize(int start,int length,std::vector<T * > * elements)87 static int TotalSize(int start, int length, std::vector<T*>* elements) {
88 DCHECK(start >= 0 && length > 0 &&
89 start + length <= static_cast<int>(elements->size()));
90 int size = 0;
91 for (int i = start, max = start + length; i < max; ++i) {
92 size += (*elements)[i]->Size();
93 }
94 return size;
95 }
96
LayoutElement(float resize_percent)97 explicit LayoutElement(float resize_percent)
98 : resize_percent_(resize_percent) {
99 DCHECK(resize_percent >= 0);
100 }
101
~LayoutElement()102 virtual ~LayoutElement() {}
103
SetLocation(int location)104 void SetLocation(int location) {
105 location_ = location;
106 }
107
Location()108 int Location() {
109 return location_;
110 }
111
112 // Adjusts the size of this LayoutElement to be the max of the current size
113 // and the specified size.
AdjustSize(int size)114 virtual void AdjustSize(int size) {
115 size_ = std::max(size_, size);
116 }
117
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.
ResetSize()120 virtual void ResetSize() {
121 SetSize(0);
122 }
123
SetSize(int size)124 void SetSize(int size) {
125 size_ = size;
126 }
127
Size()128 int Size() {
129 return size_;
130 }
131
SetResizePercent(float percent)132 void SetResizePercent(float percent) {
133 resize_percent_ = percent;
134 }
135
ResizePercent()136 float ResizePercent() {
137 return resize_percent_;
138 }
139
IsResizable()140 bool IsResizable() {
141 return resize_percent_ > 0;
142 }
143
144 private:
145 float resize_percent_;
146 int location_;
147 int size_;
148
149 DISALLOW_COPY_AND_ASSIGN(LayoutElement);
150 };
151
152 // Column -------------------------------------------------------------
153
154 // As the name implies, this represents a Column. Column contains default
155 // values for views originating in this column.
156 class Column : public LayoutElement {
157 public:
Column(GridLayout::Alignment h_align,GridLayout::Alignment v_align,float resize_percent,GridLayout::SizeType size_type,int fixed_width,int min_width,size_t index,bool is_padding)158 Column(GridLayout::Alignment h_align,
159 GridLayout::Alignment v_align,
160 float resize_percent,
161 GridLayout::SizeType size_type,
162 int fixed_width,
163 int min_width,
164 size_t index,
165 bool is_padding)
166 : LayoutElement(resize_percent),
167 h_align_(h_align),
168 v_align_(v_align),
169 size_type_(size_type),
170 same_size_column_(-1),
171 fixed_width_(fixed_width),
172 min_width_(min_width),
173 index_(index),
174 is_padding_(is_padding),
175 master_column_(NULL) {}
176
~Column()177 virtual ~Column() {}
178
h_align()179 GridLayout::Alignment h_align() { return h_align_; }
v_align()180 GridLayout::Alignment v_align() { return v_align_; }
181
182 virtual void ResetSize() OVERRIDE;
183
184 private:
185 friend class ColumnSet;
186 friend class GridLayout;
187
188 Column* GetLastMasterColumn();
189
190 // Determines the max size of all linked columns, and sets each column
191 // to that size. This should only be used for the master column.
192 void UnifySameSizedColumnSizes();
193
194 virtual void AdjustSize(int size) OVERRIDE;
195
196 const GridLayout::Alignment h_align_;
197 const GridLayout::Alignment v_align_;
198 const GridLayout::SizeType size_type_;
199 int same_size_column_;
200 const int fixed_width_;
201 const int min_width_;
202
203 // Index of this column in the ColumnSet.
204 const size_t index_;
205
206 const bool is_padding_;
207
208 // If multiple columns have their sizes linked, one is the
209 // master column. The master column is identified by the
210 // master_column field being equal to itself. The master columns
211 // same_size_columns field contains the set of Columns with the
212 // the same size. Columns who are linked to other columns, but
213 // are not the master column have their master_column pointing to
214 // one of the other linked columns. Use the method GetLastMasterColumn
215 // to resolve the true master column.
216 std::vector<Column*> same_size_columns_;
217 Column* master_column_;
218
219 DISALLOW_COPY_AND_ASSIGN(Column);
220 };
221
ResetSize()222 void Column::ResetSize() {
223 if (size_type_ == GridLayout::FIXED) {
224 SetSize(fixed_width_);
225 } else {
226 SetSize(min_width_);
227 }
228 }
229
GetLastMasterColumn()230 Column* Column::GetLastMasterColumn() {
231 if (master_column_ == NULL) {
232 return NULL;
233 }
234 if (master_column_ == this) {
235 return this;
236 }
237 return master_column_->GetLastMasterColumn();
238 }
239
UnifySameSizedColumnSizes()240 void Column::UnifySameSizedColumnSizes() {
241 DCHECK(master_column_ == this);
242
243 // Accumulate the size first.
244 int size = 0;
245 for (std::vector<Column*>::iterator i = same_size_columns_.begin();
246 i != same_size_columns_.end(); ++i) {
247 size = std::max(size, (*i)->Size());
248 }
249
250 // Then apply it.
251 for (std::vector<Column*>::iterator i = same_size_columns_.begin();
252 i != same_size_columns_.end(); ++i) {
253 (*i)->SetSize(size);
254 }
255 }
256
AdjustSize(int size)257 void Column::AdjustSize(int size) {
258 if (size_type_ == GridLayout::USE_PREF)
259 LayoutElement::AdjustSize(size);
260 }
261
262 // Row -------------------------------------------------------------
263
264 class Row : public LayoutElement {
265 public:
Row(bool fixed_height,int height,float resize_percent,ColumnSet * column_set)266 Row(bool fixed_height, int height, float resize_percent,
267 ColumnSet* column_set)
268 : LayoutElement(resize_percent),
269 fixed_height_(fixed_height),
270 height_(height),
271 column_set_(column_set),
272 max_ascent_(0),
273 max_descent_(0) {
274 }
275
~Row()276 virtual ~Row() {}
277
ResetSize()278 virtual void ResetSize() OVERRIDE {
279 max_ascent_ = max_descent_ = 0;
280 SetSize(height_);
281 }
282
column_set()283 ColumnSet* column_set() {
284 return column_set_;
285 }
286
287 // Adjusts the size to accomodate the specified ascent/descent.
AdjustSizeForBaseline(int ascent,int descent)288 void AdjustSizeForBaseline(int ascent, int descent) {
289 max_ascent_ = std::max(ascent, max_ascent_);
290 max_descent_ = std::max(descent, max_descent_);
291 AdjustSize(max_ascent_ + max_descent_);
292 }
293
max_ascent() const294 int max_ascent() const {
295 return max_ascent_;
296 }
297
max_descent() const298 int max_descent() const {
299 return max_descent_;
300 }
301
302 private:
303 const bool fixed_height_;
304 const int height_;
305 // The column set used for this row; null for padding rows.
306 ColumnSet* column_set_;
307
308 int max_ascent_;
309 int max_descent_;
310
311 DISALLOW_COPY_AND_ASSIGN(Row);
312 };
313
314 // ViewState -------------------------------------------------------------
315
316 // Identifies the location in the grid of a particular view, along with
317 // placement information and size information.
318 struct ViewState {
ViewStateviews::ViewState319 ViewState(ColumnSet* column_set, View* view, int start_col, int start_row,
320 int col_span, int row_span, GridLayout::Alignment h_align,
321 GridLayout::Alignment v_align, int pref_width, int pref_height)
322 : column_set(column_set),
323 view(view),
324 start_col(start_col),
325 start_row(start_row),
326 col_span(col_span),
327 row_span(row_span),
328 h_align(h_align),
329 v_align(v_align),
330 pref_width_fixed(pref_width > 0),
331 pref_height_fixed(pref_height > 0),
332 pref_width(pref_width),
333 pref_height(pref_height),
334 remaining_width(0),
335 remaining_height(0),
336 baseline(-1) {
337 DCHECK(view && start_col >= 0 && start_row >= 0 && col_span > 0 &&
338 row_span > 0 && start_col < column_set->num_columns() &&
339 (start_col + col_span) <= column_set->num_columns());
340 }
341
342 ColumnSet* const column_set;
343 View* const view;
344 const int start_col;
345 const int start_row;
346 const int col_span;
347 const int row_span;
348 const GridLayout::Alignment h_align;
349 const GridLayout::Alignment v_align;
350
351 // If true, the pref_width/pref_height were explicitly set and the view's
352 // preferred size is ignored.
353 const bool pref_width_fixed;
354 const bool pref_height_fixed;
355
356 // The preferred width/height. These are reset during the layout process.
357 int pref_width;
358 int pref_height;
359
360 // Used during layout. Gives how much width/height has not yet been
361 // distributed to the columns/rows the view is in.
362 int remaining_width;
363 int remaining_height;
364
365 // The baseline. Only used if the view is vertically aligned along the
366 // baseline.
367 int baseline;
368 };
369
CompareByColumnSpan(const ViewState * v1,const ViewState * v2)370 static bool CompareByColumnSpan(const ViewState* v1, const ViewState* v2) {
371 return v1->col_span < v2->col_span;
372 }
373
CompareByRowSpan(const ViewState * v1,const ViewState * v2)374 static bool CompareByRowSpan(const ViewState* v1, const ViewState* v2) {
375 return v1->row_span < v2->row_span;
376 }
377
378 // ColumnSet -------------------------------------------------------------
379
ColumnSet(int id)380 ColumnSet::ColumnSet(int id) : id_(id) {
381 }
382
~ColumnSet()383 ColumnSet::~ColumnSet() {
384 STLDeleteElements(&columns_);
385 }
386
AddPaddingColumn(float resize_percent,int width)387 void ColumnSet::AddPaddingColumn(float resize_percent, int width) {
388 AddColumn(GridLayout::FILL, GridLayout::FILL, resize_percent,
389 GridLayout::FIXED, width, width, true);
390 }
391
AddColumn(GridLayout::Alignment h_align,GridLayout::Alignment v_align,float resize_percent,GridLayout::SizeType size_type,int fixed_width,int min_width)392 void ColumnSet::AddColumn(GridLayout::Alignment h_align,
393 GridLayout::Alignment v_align,
394 float resize_percent,
395 GridLayout::SizeType size_type,
396 int fixed_width,
397 int min_width) {
398 AddColumn(h_align, v_align, resize_percent, size_type, fixed_width,
399 min_width, false);
400 }
401
402
LinkColumnSizes(int first,...)403 void ColumnSet::LinkColumnSizes(int first, ...) {
404 va_list marker;
405 va_start(marker, first);
406 DCHECK(first >= 0 && first < num_columns());
407 for (int last = first, next = va_arg(marker, int); next != -1;
408 next = va_arg(marker, int)) {
409 DCHECK(next >= 0 && next < num_columns());
410 columns_[last]->same_size_column_ = next;
411 last = next;
412 }
413 va_end(marker);
414 }
415
AddColumn(GridLayout::Alignment h_align,GridLayout::Alignment v_align,float resize_percent,GridLayout::SizeType size_type,int fixed_width,int min_width,bool is_padding)416 void ColumnSet::AddColumn(GridLayout::Alignment h_align,
417 GridLayout::Alignment v_align,
418 float resize_percent,
419 GridLayout::SizeType size_type,
420 int fixed_width,
421 int min_width,
422 bool is_padding) {
423 Column* column = new Column(h_align, v_align, resize_percent, size_type,
424 fixed_width, min_width, columns_.size(),
425 is_padding);
426 columns_.push_back(column);
427 }
428
AddViewState(ViewState * view_state)429 void ColumnSet::AddViewState(ViewState* view_state) {
430 // view_states are ordered by column_span (in ascending order).
431 std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(),
432 view_states_.end(),
433 view_state,
434 CompareByColumnSpan);
435 view_states_.insert(i, view_state);
436 }
437
CalculateMasterColumns()438 void ColumnSet::CalculateMasterColumns() {
439 for (std::vector<Column*>::iterator i = columns_.begin();
440 i != columns_.end(); ++i) {
441 Column* column = *i;
442 int same_size_column_index = column->same_size_column_;
443 if (same_size_column_index != -1) {
444 DCHECK(same_size_column_index >= 0 &&
445 same_size_column_index < static_cast<int>(columns_.size()));
446 Column* master_column = column->master_column_;
447 Column* same_size_column = columns_[same_size_column_index];
448 Column* same_size_column_master = same_size_column->master_column_;
449 if (master_column == NULL) {
450 // Current column is not linked to any other column.
451 if (same_size_column_master == NULL) {
452 // Both columns are not linked.
453 column->master_column_ = column;
454 same_size_column->master_column_ = column;
455 column->same_size_columns_.push_back(same_size_column);
456 column->same_size_columns_.push_back(column);
457 } else {
458 // Column to link to is linked with other columns.
459 // Add current column to list of linked columns in other columns
460 // master column.
461 same_size_column->GetLastMasterColumn()->
462 same_size_columns_.push_back(column);
463 // And update the master column for the current column to that
464 // of the same sized column.
465 column->master_column_ = same_size_column;
466 }
467 } else {
468 // Current column is already linked with another column.
469 if (same_size_column_master == NULL) {
470 // Column to link with is not linked to any other columns.
471 // Update it's master_column.
472 same_size_column->master_column_ = column;
473 // Add linked column to list of linked column.
474 column->GetLastMasterColumn()->same_size_columns_.
475 push_back(same_size_column);
476 } else if (column->GetLastMasterColumn() !=
477 same_size_column->GetLastMasterColumn()) {
478 // The two columns are already linked with other columns.
479 std::vector<Column*>* same_size_columns =
480 &(column->GetLastMasterColumn()->same_size_columns_);
481 std::vector<Column*>* other_same_size_columns =
482 &(same_size_column->GetLastMasterColumn()->same_size_columns_);
483 // Add all the columns from the others master to current columns
484 // master.
485 same_size_columns->insert(same_size_columns->end(),
486 other_same_size_columns->begin(),
487 other_same_size_columns->end());
488 // The other master is no longer a master, clear its vector of
489 // linked columns, and reset its master_column.
490 other_same_size_columns->clear();
491 same_size_column->GetLastMasterColumn()->master_column_ = column;
492 }
493 }
494 }
495 }
496 AccumulateMasterColumns();
497 }
498
AccumulateMasterColumns()499 void ColumnSet::AccumulateMasterColumns() {
500 DCHECK(master_columns_.empty());
501 for (std::vector<Column*>::iterator i = columns_.begin();
502 i != columns_.end(); ++i) {
503 Column* column = *i;
504 Column* master_column = column->GetLastMasterColumn();
505 if (master_column &&
506 find(master_columns_.begin(), master_columns_.end(),
507 master_column) == master_columns_.end()) {
508 master_columns_.push_back(master_column);
509 }
510 // At this point, GetLastMasterColumn may not == master_column
511 // (may have to go through a few Columns)_. Reset master_column to
512 // avoid hops.
513 column->master_column_ = master_column;
514 }
515 }
516
UnifySameSizedColumnSizes()517 void ColumnSet::UnifySameSizedColumnSizes() {
518 for (std::vector<Column*>::iterator i = master_columns_.begin();
519 i != master_columns_.end(); ++i) {
520 (*i)->UnifySameSizedColumnSizes();
521 }
522 }
523
UpdateRemainingWidth(ViewState * view_state)524 void ColumnSet::UpdateRemainingWidth(ViewState* view_state) {
525 for (int i = view_state->start_col,
526 max_col = view_state->start_col + view_state->col_span;
527 i < max_col; ++i) {
528 view_state->remaining_width -= columns_[i]->Size();
529 }
530 }
531
DistributeRemainingWidth(ViewState * view_state)532 void ColumnSet::DistributeRemainingWidth(ViewState* view_state) {
533 // This is nearly the same as that for rows, but differs in so far as how
534 // Rows and Columns are treated. Rows have two states, resizable or not.
535 // Columns have three, resizable, USE_PREF or not resizable. This results
536 // in slightly different handling for distributing unaccounted size.
537 int width = view_state->remaining_width;
538 if (width <= 0) {
539 // The columns this view is in are big enough to accommodate it.
540 return;
541 }
542
543 // Determine which columns are resizable, and which have a size type
544 // of USE_PREF.
545 int resizable_columns = 0;
546 int pref_size_columns = 0;
547 int start_col = view_state->start_col;
548 int max_col = view_state->start_col + view_state->col_span;
549 float total_resize = 0;
550 for (int i = start_col; i < max_col; ++i) {
551 if (columns_[i]->IsResizable()) {
552 total_resize += columns_[i]->ResizePercent();
553 resizable_columns++;
554 } else if (columns_[i]->size_type_ == GridLayout::USE_PREF) {
555 pref_size_columns++;
556 }
557 }
558
559 if (resizable_columns > 0) {
560 // There are resizable columns, give them the remaining width. The extra
561 // width is distributed using the resize values of each column.
562 int remaining_width = width;
563 for (int i = start_col, resize_i = 0; i < max_col; ++i) {
564 if (columns_[i]->IsResizable()) {
565 resize_i++;
566 int delta = (resize_i == resizable_columns) ? remaining_width :
567 static_cast<int>(width * columns_[i]->ResizePercent() /
568 total_resize);
569 remaining_width -= delta;
570 columns_[i]->SetSize(columns_[i]->Size() + delta);
571 }
572 }
573 } else if (pref_size_columns > 0) {
574 // None of the columns are resizable, distribute the width among those
575 // that use the preferred size.
576 int to_distribute = width / pref_size_columns;
577 for (int i = start_col; i < max_col; ++i) {
578 if (columns_[i]->size_type_ == GridLayout::USE_PREF) {
579 width -= to_distribute;
580 if (width < to_distribute)
581 to_distribute += width;
582 columns_[i]->SetSize(columns_[i]->Size() + to_distribute);
583 }
584 }
585 }
586 }
587
LayoutWidth()588 int ColumnSet::LayoutWidth() {
589 int width = 0;
590 for (std::vector<Column*>::iterator i = columns_.begin();
591 i != columns_.end(); ++i) {
592 width += (*i)->Size();
593 }
594 return width;
595 }
596
GetColumnWidth(int start_col,int col_span)597 int ColumnSet::GetColumnWidth(int start_col, int col_span) {
598 return LayoutElement::TotalSize(start_col, col_span, &columns_);
599 }
600
ResetColumnXCoordinates()601 void ColumnSet::ResetColumnXCoordinates() {
602 LayoutElement::CalculateLocationsFromSize(&columns_);
603 }
604
CalculateSize()605 void ColumnSet::CalculateSize() {
606 gfx::Size pref;
607 // Reset the preferred and remaining sizes.
608 for (std::vector<ViewState*>::iterator i = view_states_.begin();
609 i != view_states_.end(); ++i) {
610 ViewState* view_state = *i;
611 if (!view_state->pref_width_fixed || !view_state->pref_height_fixed) {
612 pref = view_state->view->GetPreferredSize();
613 if (!view_state->pref_width_fixed)
614 view_state->pref_width = pref.width();
615 if (!view_state->pref_height_fixed)
616 view_state->pref_height = pref.height();
617 }
618 view_state->remaining_width = pref.width();
619 view_state->remaining_height = pref.height();
620 }
621
622 // Let layout element reset the sizes for us.
623 LayoutElement::ResetSizes(&columns_);
624
625 // Distribute the size of each view with a col span == 1.
626 std::vector<ViewState*>::iterator view_state_iterator =
627 view_states_.begin();
628 for (; view_state_iterator != view_states_.end() &&
629 (*view_state_iterator)->col_span == 1; ++view_state_iterator) {
630 ViewState* view_state = *view_state_iterator;
631 Column* column = columns_[view_state->start_col];
632 column->AdjustSize(view_state->pref_width);
633 view_state->remaining_width -= column->Size();
634 }
635
636 // Make sure all linked columns have the same size.
637 UnifySameSizedColumnSizes();
638
639 // Distribute the size of each view with a column span > 1.
640 for (; view_state_iterator != view_states_.end(); ++view_state_iterator) {
641 ViewState* view_state = *view_state_iterator;
642
643 // Update the remaining_width from columns this view_state touches.
644 UpdateRemainingWidth(view_state);
645
646 // Distribute the remaining width.
647 DistributeRemainingWidth(view_state);
648
649 // Update the size of linked columns.
650 // This may need to be combined with previous step.
651 UnifySameSizedColumnSizes();
652 }
653 }
654
Resize(int delta)655 void ColumnSet::Resize(int delta) {
656 LayoutElement::DistributeDelta(delta, &columns_);
657 }
658
659 // GridLayout -------------------------------------------------------------
660
GridLayout(View * host)661 GridLayout::GridLayout(View* host)
662 : host_(host),
663 calculated_master_columns_(false),
664 remaining_row_span_(0),
665 current_row_(-1),
666 next_column_(0),
667 current_row_col_set_(NULL),
668 adding_view_(false) {
669 DCHECK(host);
670 }
671
~GridLayout()672 GridLayout::~GridLayout() {
673 STLDeleteElements(&column_sets_);
674 STLDeleteElements(&view_states_);
675 STLDeleteElements(&rows_);
676 }
677
678 // static
CreatePanel(View * host)679 GridLayout* GridLayout::CreatePanel(View* host) {
680 GridLayout* layout = new GridLayout(host);
681 layout->SetInsets(kPanelVertMargin, kButtonHEdgeMarginNew,
682 kPanelVertMargin, kButtonHEdgeMarginNew);
683 return layout;
684 }
685
SetInsets(int top,int left,int bottom,int right)686 void GridLayout::SetInsets(int top, int left, int bottom, int right) {
687 insets_.Set(top, left, bottom, right);
688 }
689
SetInsets(const gfx::Insets & insets)690 void GridLayout::SetInsets(const gfx::Insets& insets) {
691 insets_ = insets;
692 }
693
AddColumnSet(int id)694 ColumnSet* GridLayout::AddColumnSet(int id) {
695 DCHECK(GetColumnSet(id) == NULL);
696 ColumnSet* column_set = new ColumnSet(id);
697 column_sets_.push_back(column_set);
698 return column_set;
699 }
700
GetColumnSet(int id)701 ColumnSet* GridLayout::GetColumnSet(int id) {
702 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
703 i != column_sets_.end(); ++i) {
704 if ((*i)->id_ == id) {
705 return *i;
706 }
707 }
708 return NULL;
709 }
710
StartRowWithPadding(float vertical_resize,int column_set_id,float padding_resize,int padding)711 void GridLayout::StartRowWithPadding(float vertical_resize, int column_set_id,
712 float padding_resize, int padding) {
713 AddPaddingRow(padding_resize, padding);
714 StartRow(vertical_resize, column_set_id);
715 }
716
StartRow(float vertical_resize,int column_set_id)717 void GridLayout::StartRow(float vertical_resize, int column_set_id) {
718 ColumnSet* column_set = GetColumnSet(column_set_id);
719 DCHECK(column_set);
720 AddRow(new Row(false, 0, vertical_resize, column_set));
721 }
722
AddPaddingRow(float vertical_resize,int pixel_count)723 void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) {
724 AddRow(new Row(true, pixel_count, vertical_resize, NULL));
725 }
726
SkipColumns(int col_count)727 void GridLayout::SkipColumns(int col_count) {
728 DCHECK(col_count > 0);
729 next_column_ += col_count;
730 DCHECK(current_row_col_set_ &&
731 next_column_ <= current_row_col_set_->num_columns());
732 SkipPaddingColumns();
733 }
734
AddView(View * view)735 void GridLayout::AddView(View* view) {
736 AddView(view, 1, 1);
737 }
738
AddView(View * view,int col_span,int row_span)739 void GridLayout::AddView(View* view, int col_span, int row_span) {
740 DCHECK(current_row_col_set_ &&
741 next_column_ < current_row_col_set_->num_columns());
742 Column* column = current_row_col_set_->columns_[next_column_];
743 AddView(view, col_span, row_span, column->h_align(), column->v_align());
744 }
745
AddView(View * view,int col_span,int row_span,Alignment h_align,Alignment v_align)746 void GridLayout::AddView(View* view, int col_span, int row_span,
747 Alignment h_align, Alignment v_align) {
748 AddView(view, col_span, row_span, h_align, v_align, 0, 0);
749 }
750
AddView(View * view,int col_span,int row_span,Alignment h_align,Alignment v_align,int pref_width,int pref_height)751 void GridLayout::AddView(View* view, int col_span, int row_span,
752 Alignment h_align, Alignment v_align,
753 int pref_width, int pref_height) {
754 DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 &&
755 (next_column_ + col_span) <= current_row_col_set_->num_columns());
756 // We don't support baseline alignment of views spanning rows. Please add if
757 // you need it.
758 DCHECK(v_align != BASELINE || row_span == 1);
759 ViewState* state =
760 new ViewState(current_row_col_set_, view, next_column_, current_row_,
761 col_span, row_span, h_align, v_align, pref_width,
762 pref_height);
763 AddViewState(state);
764 }
765
CalculateSize(int pref_size,GridLayout::Alignment alignment,int * location,int * size)766 static void CalculateSize(int pref_size, GridLayout::Alignment alignment,
767 int* location, int* size) {
768 if (alignment != GridLayout::FILL) {
769 int available_size = *size;
770 *size = std::min(*size, pref_size);
771 switch (alignment) {
772 case GridLayout::LEADING:
773 // Nothing to do, location already points to start.
774 break;
775 case GridLayout::BASELINE: // If we were asked to align on baseline, but
776 // the view doesn't have a baseline, fall back
777 // to center.
778 case GridLayout::CENTER:
779 *location += (available_size - *size) / 2;
780 break;
781 case GridLayout::TRAILING:
782 *location = *location + available_size - *size;
783 break;
784 default:
785 NOTREACHED();
786 }
787 }
788 }
789
Installed(View * host)790 void GridLayout::Installed(View* host) {
791 DCHECK(host_ == host);
792 }
793
Uninstalled(View * host)794 void GridLayout::Uninstalled(View* host) {
795 DCHECK(host_ == host);
796 }
797
ViewAdded(View * host,View * view)798 void GridLayout::ViewAdded(View* host, View* view) {
799 DCHECK(host_ == host && adding_view_);
800 }
801
ViewRemoved(View * host,View * view)802 void GridLayout::ViewRemoved(View* host, View* view) {
803 DCHECK(host_ == host);
804 }
805
Layout(View * host)806 void GridLayout::Layout(View* host) {
807 DCHECK(host_ == host);
808 // SizeRowsAndColumns sets the size and location of each row/column, but
809 // not of the views.
810 gfx::Size pref;
811 SizeRowsAndColumns(true, host_->width(), host_->height(), &pref);
812
813 // Size each view.
814 for (std::vector<ViewState*>::iterator i = view_states_.begin();
815 i != view_states_.end(); ++i) {
816 ViewState* view_state = *i;
817 ColumnSet* column_set = view_state->column_set;
818 View* view = (*i)->view;
819 DCHECK(view);
820 int x = column_set->columns_[view_state->start_col]->Location() +
821 insets_.left();
822 int width = column_set->GetColumnWidth(view_state->start_col,
823 view_state->col_span);
824 CalculateSize(view_state->pref_width, view_state->h_align,
825 &x, &width);
826 int y = rows_[view_state->start_row]->Location() + insets_.top();
827 int height = LayoutElement::TotalSize(view_state->start_row,
828 view_state->row_span, &rows_);
829 if (view_state->v_align == BASELINE && view_state->baseline != -1) {
830 y += rows_[view_state->start_row]->max_ascent() - view_state->baseline;
831 height = view_state->pref_height;
832 } else {
833 CalculateSize(view_state->pref_height, view_state->v_align, &y, &height);
834 }
835 view->SetBounds(x, y, width, height);
836 }
837 }
838
GetPreferredSize(View * host)839 gfx::Size GridLayout::GetPreferredSize(View* host) {
840 DCHECK(host_ == host);
841 gfx::Size out;
842 SizeRowsAndColumns(false, 0, 0, &out);
843 out.SetSize(std::max(out.width(), minimum_size_.width()),
844 std::max(out.height(), minimum_size_.height()));
845 return out;
846 }
847
GetPreferredHeightForWidth(View * host,int width)848 int GridLayout::GetPreferredHeightForWidth(View* host, int width) {
849 DCHECK(host_ == host);
850 gfx::Size pref;
851 SizeRowsAndColumns(false, width, 0, &pref);
852 return pref.height();
853 }
854
SizeRowsAndColumns(bool layout,int width,int height,gfx::Size * pref)855 void GridLayout::SizeRowsAndColumns(bool layout, int width, int height,
856 gfx::Size* pref) {
857 // Make sure the master columns have been calculated.
858 CalculateMasterColumnsIfNecessary();
859 pref->SetSize(0, 0);
860 if (rows_.empty())
861 return;
862
863 // Calculate the preferred width of each of the columns. Some views'
864 // preferred heights are derived from their width, as such we need to
865 // calculate the size of the columns first.
866 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
867 i != column_sets_.end(); ++i) {
868 (*i)->CalculateSize();
869 pref->set_width(std::max(pref->width(), (*i)->LayoutWidth()));
870 }
871 pref->set_width(pref->width() + insets_.width());
872
873 // Go over the columns again and set them all to the size we settled for.
874 width = width ? width : pref->width();
875 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
876 i != column_sets_.end(); ++i) {
877 // We're doing a layout, divy up any extra space.
878 (*i)->Resize(width - (*i)->LayoutWidth() - insets_.left() -
879 insets_.right());
880 // And reset the x coordinates.
881 (*i)->ResetColumnXCoordinates();
882 }
883
884 // Reset the height of each row.
885 LayoutElement::ResetSizes(&rows_);
886
887 // Do the following:
888 // . If the view is aligned along it's baseline, obtain the baseline from the
889 // view and update the rows ascent/descent.
890 // . Reset the remaining_height of each view state.
891 // . If the width the view will be given is different than it's pref, ask
892 // for the height given a particularly width.
893 for (std::vector<ViewState*>::iterator i= view_states_.begin();
894 i != view_states_.end() ; ++i) {
895 ViewState* view_state = *i;
896 view_state->remaining_height = view_state->pref_height;
897
898 if (view_state->v_align == BASELINE)
899 view_state->baseline = view_state->view->GetBaseline();
900
901 if (view_state->h_align == FILL) {
902 // The view is resizable. As the pref height may vary with the width,
903 // ask for the pref again.
904 int actual_width =
905 view_state->column_set->GetColumnWidth(view_state->start_col,
906 view_state->col_span);
907 if (actual_width != view_state->pref_width &&
908 !view_state->pref_height_fixed) {
909 // The width this view will get differs from its preferred. Some Views
910 // pref height varies with its width; ask for the preferred again.
911 view_state->pref_height =
912 view_state->view->GetHeightForWidth(actual_width);
913 view_state->remaining_height = view_state->pref_height;
914 }
915 }
916 }
917
918 // Update the height/ascent/descent of each row from the views.
919 std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin();
920 for (; view_states_iterator != view_states_.end() &&
921 (*view_states_iterator)->row_span == 1; ++view_states_iterator) {
922 ViewState* view_state = *view_states_iterator;
923 Row* row = rows_[view_state->start_row];
924 row->AdjustSize(view_state->remaining_height);
925 if (view_state->baseline != -1 &&
926 view_state->baseline <= view_state->pref_height) {
927 row->AdjustSizeForBaseline(view_state->baseline,
928 view_state->pref_height - view_state->baseline);
929 }
930 view_state->remaining_height = 0;
931 }
932
933 // Distribute the height of each view with a row span > 1.
934 for (; view_states_iterator != view_states_.end(); ++view_states_iterator) {
935 ViewState* view_state = *view_states_iterator;
936
937 // Update the remaining_width from columns this view_state touches.
938 UpdateRemainingHeightFromRows(view_state);
939
940 // Distribute the remaining height.
941 DistributeRemainingHeight(view_state);
942 }
943
944 // Update the location of each of the rows.
945 LayoutElement::CalculateLocationsFromSize(&rows_);
946
947 // We now know the preferred height, set it here.
948 pref->set_height(rows_[rows_.size() - 1]->Location() +
949 rows_[rows_.size() - 1]->Size() + insets_.height());
950
951 if (layout && height != pref->height()) {
952 // We're doing a layout, and the height differs from the preferred height,
953 // divy up the extra space.
954 LayoutElement::DistributeDelta(height - pref->height(), &rows_);
955
956 // Reset y locations.
957 LayoutElement::CalculateLocationsFromSize(&rows_);
958 }
959 }
960
CalculateMasterColumnsIfNecessary()961 void GridLayout::CalculateMasterColumnsIfNecessary() {
962 if (!calculated_master_columns_) {
963 calculated_master_columns_ = true;
964 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin();
965 i != column_sets_.end(); ++i) {
966 (*i)->CalculateMasterColumns();
967 }
968 }
969 }
970
AddViewState(ViewState * view_state)971 void GridLayout::AddViewState(ViewState* view_state) {
972 DCHECK(view_state->view && (view_state->view->parent() == NULL ||
973 view_state->view->parent() == host_));
974 if (!view_state->view->parent()) {
975 adding_view_ = true;
976 host_->AddChildView(view_state->view);
977 adding_view_ = false;
978 }
979 remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span);
980 next_column_ += view_state->col_span;
981 current_row_col_set_->AddViewState(view_state);
982 // view_states are ordered by row_span (in ascending order).
983 std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(),
984 view_states_.end(),
985 view_state,
986 CompareByRowSpan);
987 view_states_.insert(i, view_state);
988 SkipPaddingColumns();
989 }
990
AddRow(Row * row)991 void GridLayout::AddRow(Row* row) {
992 current_row_++;
993 remaining_row_span_--;
994 // GridLayout requires that if you add a View with a row span you use the same
995 // column set for each of the rows the view lands it. This DCHECK verifies
996 // that.
997 DCHECK(remaining_row_span_ <= 0 ||
998 row->column_set() == NULL ||
999 row->column_set() == GetLastValidColumnSet());
1000 next_column_ = 0;
1001 rows_.push_back(row);
1002 current_row_col_set_ = row->column_set();
1003 SkipPaddingColumns();
1004 }
1005
UpdateRemainingHeightFromRows(ViewState * view_state)1006 void GridLayout::UpdateRemainingHeightFromRows(ViewState* view_state) {
1007 for (int i = 0, start_row = view_state->start_row;
1008 i < view_state->row_span; ++i) {
1009 view_state->remaining_height -= rows_[i + start_row]->Size();
1010 }
1011 }
1012
DistributeRemainingHeight(ViewState * view_state)1013 void GridLayout::DistributeRemainingHeight(ViewState* view_state) {
1014 int height = view_state->remaining_height;
1015 if (height <= 0)
1016 return;
1017
1018 // Determine the number of resizable rows the view touches.
1019 int resizable_rows = 0;
1020 int start_row = view_state->start_row;
1021 int max_row = view_state->start_row + view_state->row_span;
1022 for (int i = start_row; i < max_row; ++i) {
1023 if (rows_[i]->IsResizable()) {
1024 resizable_rows++;
1025 }
1026 }
1027
1028 if (resizable_rows > 0) {
1029 // There are resizable rows, give the remaining height to them.
1030 int to_distribute = height / resizable_rows;
1031 for (int i = start_row; i < max_row; ++i) {
1032 if (rows_[i]->IsResizable()) {
1033 height -= to_distribute;
1034 if (height < to_distribute) {
1035 // Give all slop to the last column.
1036 to_distribute += height;
1037 }
1038 rows_[i]->SetSize(rows_[i]->Size() + to_distribute);
1039 }
1040 }
1041 } else {
1042 // None of the rows are resizable, divy the remaining height up equally
1043 // among all rows the view touches.
1044 int each_row_height = height / view_state->row_span;
1045 for (int i = start_row; i < max_row; ++i) {
1046 height -= each_row_height;
1047 if (height < each_row_height)
1048 each_row_height += height;
1049 rows_[i]->SetSize(rows_[i]->Size() + each_row_height);
1050 }
1051 view_state->remaining_height = 0;
1052 }
1053 }
1054
SkipPaddingColumns()1055 void GridLayout::SkipPaddingColumns() {
1056 if (!current_row_col_set_)
1057 return;
1058 while (next_column_ < current_row_col_set_->num_columns() &&
1059 current_row_col_set_->columns_[next_column_]->is_padding_) {
1060 next_column_++;
1061 }
1062 }
1063
GetLastValidColumnSet()1064 ColumnSet* GridLayout::GetLastValidColumnSet() {
1065 for (int i = current_row_ - 1; i >= 0; --i) {
1066 if (rows_[i]->column_set())
1067 return rows_[i]->column_set();
1068 }
1069 return NULL;
1070 }
1071
1072 } // namespace views
1073