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