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