• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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