OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 "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 "views/layout/layout_constants.h" | |
13 #include "views/view.h" | |
14 | |
15 namespace views { | |
16 | |
17 // LayoutElement ------------------------------------------------------ | |
18 | |
19 // A LayoutElement has a size and location along one axis. It contains | |
20 // methods that are used along both axis. | |
21 class 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. | |
27 for (typename std::vector<T*>::iterator i = elements->begin(); | |
28 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; | |
39 for (typename std::vector<T*>::iterator i = elements->begin(); | |
40 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; | |
56 for (typename std::vector<T*>::iterator i = elements->begin(); | |
57 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; | |
67 for (typename std::vector<T*>::iterator i = elements->begin(); | |
68 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 | |
148 DISALLOW_COPY_AND_ASSIGN(LayoutElement); | |
149 }; | |
150 | |
151 // Column ------------------------------------------------------------- | |
152 | |
153 // As the name implies, this represents a Column. Column contains default | |
154 // values for views originating in this column. | |
155 class Column : public LayoutElement { | |
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 | |
218 DISALLOW_COPY_AND_ASSIGN(Column); | |
219 }; | |
220 | |
221 void Column::ResetSize() { | |
222 if (size_type_ == GridLayout::FIXED) { | |
223 SetSize(fixed_width_); | |
224 } else { | |
225 SetSize(min_width_); | |
226 } | |
227 } | |
228 | |
229 Column* 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 | |
239 void 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 | |
256 void Column::AdjustSize(int size) { | |
257 if (size_type_ == GridLayout::USE_PREF) | |
258 LayoutElement::AdjustSize(size); | |
259 } | |
260 | |
261 // Row ------------------------------------------------------------- | |
262 | |
263 class 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), | |
270 column_set_(column_set), | |
271 max_ascent_(0), | |
272 max_descent_(0) { | |
273 } | |
274 | |
275 virtual ~Row() {} | |
276 | |
277 virtual void ResetSize() { | |
278 max_ascent_ = max_descent_ = 0; | |
279 SetSize(height_); | |
280 } | |
281 | |
282 ColumnSet* column_set() { | |
283 return column_set_; | |
284 } | |
285 | |
286 // Adjusts the size to accomodate the specified ascent/descent. | |
287 void AdjustSizeForBaseline(int ascent, int descent) { | |
288 max_ascent_ = std::max(ascent, max_ascent_); | |
289 max_descent_ = std::max(descent, max_descent_); | |
290 AdjustSize(max_ascent_ + max_descent_); | |
291 } | |
292 | |
293 int max_ascent() const { | |
294 return max_ascent_; | |
295 } | |
296 | |
297 int max_descent() const { | |
298 return max_descent_; | |
299 } | |
300 | |
301 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 | |
307 int max_ascent_; | |
308 int max_descent_; | |
309 | |
310 DISALLOW_COPY_AND_ASSIGN(Row); | |
311 }; | |
312 | |
313 // ViewState ------------------------------------------------------------- | |
314 | |
315 // Identifies the location in the grid of a particular view, along with | |
316 // placement information and size information. | |
317 struct 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), | |
334 remaining_height(0), | |
335 baseline(-1) { | |
336 DCHECK(view && start_col >= 0 && start_row >= 0 && col_span > 0 && | |
337 row_span > 0 && start_col < column_set->num_columns() && | |
338 (start_col + col_span) <= column_set->num_columns()); | |
339 } | |
340 | |
341 ColumnSet* const column_set; | |
342 View* const view; | |
343 const int start_col; | |
344 const int start_row; | |
345 const int col_span; | |
346 const int row_span; | |
347 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; | |
363 | |
364 // The baseline. Only used if the view is vertically aligned along the | |
365 // baseline. | |
366 int baseline; | |
367 }; | |
368 | |
369 static bool CompareByColumnSpan(const ViewState* v1, const ViewState* v2) { | |
370 return v1->col_span < v2->col_span; | |
371 } | |
372 | |
373 static bool CompareByRowSpan(const ViewState* v1, const ViewState* v2) { | |
374 return v1->row_span < v2->row_span; | |
375 } | |
376 | |
377 // ColumnSet ------------------------------------------------------------- | |
378 | |
379 ColumnSet::ColumnSet(int id) : id_(id) { | |
380 } | |
381 | |
382 ColumnSet::~ColumnSet() { | |
383 STLDeleteElements(&columns_); | |
384 } | |
385 | |
386 void ColumnSet::AddPaddingColumn(float resize_percent, int width) { | |
387 AddColumn(GridLayout::FILL, GridLayout::FILL, resize_percent, | |
388 GridLayout::FIXED, width, width, true); | |
389 } | |
390 | |
391 void 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 | |
402 void 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 | |
415 void 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 | |
428 void ColumnSet::AddViewState(ViewState* view_state) { | |
429 // view_states are ordered by column_span (in ascending order). | |
430 std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(), | |
431 view_states_.end(), | |
432 view_state, | |
433 CompareByColumnSpan); | |
434 view_states_.insert(i, view_state); | |
435 } | |
436 | |
437 void 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 | |
498 void 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 | |
516 void ColumnSet::UnifySameSizedColumnSizes() { | |
517 for (std::vector<Column*>::iterator i = master_columns_.begin(); | |
518 i != master_columns_.end(); ++i) { | |
519 (*i)->UnifySameSizedColumnSizes(); | |
520 } | |
521 } | |
522 | |
523 void ColumnSet::UpdateRemainingWidth(ViewState* view_state) { | |
524 for (int i = view_state->start_col; i < view_state->col_span; ++i) { | |
525 view_state->remaining_width -= columns_[i]->Size(); | |
526 } | |
527 } | |
528 | |
529 void ColumnSet::DistributeRemainingWidth(ViewState* view_state) { | |
530 // This is nearly the same as that for rows, but differs in so far as how | |
531 // Rows and Columns are treated. Rows have two states, resizable or not. | |
532 // Columns have three, resizable, USE_PREF or not resizable. This results | |
533 // in slightly different handling for distributing unaccounted size. | |
534 int width = view_state->remaining_width; | |
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; | |
546 float total_resize = 0; | |
547 for (int i = start_col; i < max_col; ++i) { | |
548 if (columns_[i]->IsResizable()) { | |
549 total_resize += columns_[i]->ResizePercent(); | |
550 resizable_columns++; | |
551 } else if (columns_[i]->size_type_ == GridLayout::USE_PREF) { | |
552 pref_size_columns++; | |
553 } | |
554 } | |
555 | |
556 if (resizable_columns > 0) { | |
557 // There are resizable columns, give them the remaining width. The extra | |
558 // width is distributed using the resize values of each column. | |
559 int remaining_width = width; | |
560 for (int i = start_col, resize_i = 0; i < max_col; ++i) { | |
561 if (columns_[i]->IsResizable()) { | |
562 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); | |
568 } | |
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 | |
585 int 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 | |
594 int ColumnSet::GetColumnWidth(int start_col, int col_span) { | |
595 return LayoutElement::TotalSize(start_col, col_span, &columns_); | |
596 } | |
597 | |
598 void ColumnSet::ResetColumnXCoordinates() { | |
599 LayoutElement::CalculateLocationsFromSize(&columns_); | |
600 } | |
601 | |
602 void ColumnSet::CalculateSize() { | |
603 gfx::Size pref; | |
604 // Reset the preferred and remaining sizes. | |
605 for (std::vector<ViewState*>::iterator i = view_states_.begin(); | |
606 i != view_states_.end(); ++i) { | |
607 ViewState* view_state = *i; | |
608 if (!view_state->pref_width_fixed || !view_state->pref_height_fixed) { | |
609 pref = view_state->view->GetPreferredSize(); | |
610 if (!view_state->pref_width_fixed) | |
611 view_state->pref_width = pref.width(); | |
612 if (!view_state->pref_height_fixed) | |
613 view_state->pref_height = pref.height(); | |
614 } | |
615 view_state->remaining_width = pref.width(); | |
616 view_state->remaining_height = pref.height(); | |
617 } | |
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 | |
652 void ColumnSet::Resize(int delta) { | |
653 LayoutElement::DistributeDelta(delta, &columns_); | |
654 } | |
655 | |
656 // GridLayout ------------------------------------------------------------- | |
657 | |
658 GridLayout::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 | |
673 GridLayout::~GridLayout() { | |
674 STLDeleteElements(&column_sets_); | |
675 STLDeleteElements(&view_states_); | |
676 STLDeleteElements(&rows_); | |
677 } | |
678 | |
679 // static | |
680 GridLayout* GridLayout::CreatePanel(View* host) { | |
681 GridLayout* layout = new GridLayout(host); | |
682 layout->SetInsets(kPanelVertMargin, kPanelHorizMargin, | |
683 kPanelVertMargin, kPanelHorizMargin); | |
684 return layout; | |
685 } | |
686 | |
687 void GridLayout::SetInsets(int top, int left, int bottom, int right) { | |
688 top_inset_ = top; | |
689 bottom_inset_ = bottom; | |
690 left_inset_ = left; | |
691 right_inset_ = right; | |
692 } | |
693 | |
694 void GridLayout::SetInsets(const gfx::Insets& insets) { | |
695 SetInsets(insets.top(), insets.left(), insets.bottom(), insets.right()); | |
696 } | |
697 | |
698 ColumnSet* GridLayout::AddColumnSet(int id) { | |
699 DCHECK(GetColumnSet(id) == NULL); | |
700 ColumnSet* column_set = new ColumnSet(id); | |
701 column_sets_.push_back(column_set); | |
702 return column_set; | |
703 } | |
704 | |
705 void GridLayout::StartRowWithPadding(float vertical_resize, int column_set_id, | |
706 float padding_resize, int padding) { | |
707 AddPaddingRow(padding_resize, padding); | |
708 StartRow(vertical_resize, column_set_id); | |
709 } | |
710 | |
711 void GridLayout::StartRow(float vertical_resize, int column_set_id) { | |
712 ColumnSet* column_set = GetColumnSet(column_set_id); | |
713 DCHECK(column_set); | |
714 AddRow(new Row(false, 0, vertical_resize, column_set)); | |
715 } | |
716 | |
717 void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) { | |
718 AddRow(new Row(true, pixel_count, vertical_resize, NULL)); | |
719 } | |
720 | |
721 void GridLayout::SkipColumns(int col_count) { | |
722 DCHECK(col_count > 0); | |
723 next_column_ += col_count; | |
724 DCHECK(current_row_col_set_ && | |
725 next_column_ <= current_row_col_set_->num_columns()); | |
726 SkipPaddingColumns(); | |
727 } | |
728 | |
729 void GridLayout::AddView(View* view) { | |
730 AddView(view, 1, 1); | |
731 } | |
732 | |
733 void GridLayout::AddView(View* view, int col_span, int row_span) { | |
734 DCHECK(current_row_col_set_ && | |
735 next_column_ < current_row_col_set_->num_columns()); | |
736 Column* column = current_row_col_set_->columns_[next_column_]; | |
737 AddView(view, col_span, row_span, column->h_align(), column->v_align()); | |
738 } | |
739 | |
740 void GridLayout::AddView(View* view, int col_span, int row_span, | |
741 Alignment h_align, Alignment v_align) { | |
742 AddView(view, col_span, row_span, h_align, v_align, 0, 0); | |
743 } | |
744 | |
745 void GridLayout::AddView(View* view, int col_span, int row_span, | |
746 Alignment h_align, Alignment v_align, | |
747 int pref_width, int pref_height) { | |
748 DCHECK(current_row_col_set_ && col_span > 0 && row_span > 0 && | |
749 (next_column_ + col_span) <= current_row_col_set_->num_columns()); | |
750 // We don't support baseline alignment of views spanning rows. Please add if | |
751 // you need it. | |
752 DCHECK(v_align != BASELINE || row_span == 1); | |
753 ViewState* state = | |
754 new ViewState(current_row_col_set_, view, next_column_, current_row_, | |
755 col_span, row_span, h_align, v_align, pref_width, | |
756 pref_height); | |
757 AddViewState(state); | |
758 } | |
759 | |
760 static void CalculateSize(int pref_size, GridLayout::Alignment alignment, | |
761 int* location, int* size) { | |
762 if (alignment != GridLayout::FILL) { | |
763 int available_size = *size; | |
764 *size = std::min(*size, pref_size); | |
765 switch (alignment) { | |
766 case GridLayout::LEADING: | |
767 // Nothing to do, location already points to start. | |
768 break; | |
769 case GridLayout::BASELINE: // If we were asked to align on baseline, but | |
770 // the view doesn't have a baseline, fall back | |
771 // to center. | |
772 case GridLayout::CENTER: | |
773 *location += (available_size - *size) / 2; | |
774 break; | |
775 case GridLayout::TRAILING: | |
776 *location = *location + available_size - *size; | |
777 break; | |
778 default: | |
779 NOTREACHED(); | |
780 } | |
781 } | |
782 } | |
783 | |
784 void GridLayout::Installed(View* host) { | |
785 DCHECK(host_ == host); | |
786 } | |
787 | |
788 void GridLayout::Uninstalled(View* host) { | |
789 DCHECK(host_ == host); | |
790 } | |
791 | |
792 void GridLayout::ViewAdded(View* host, View* view) { | |
793 DCHECK(host_ == host && adding_view_); | |
794 } | |
795 | |
796 void GridLayout::ViewRemoved(View* host, View* view) { | |
797 DCHECK(host_ == host); | |
798 } | |
799 | |
800 void GridLayout::Layout(View* host) { | |
801 DCHECK(host_ == host); | |
802 // SizeRowsAndColumns sets the size and location of each row/column, but | |
803 // not of the views. | |
804 gfx::Size pref; | |
805 SizeRowsAndColumns(true, host_->width(), host_->height(), &pref); | |
806 | |
807 // Size each view. | |
808 for (std::vector<ViewState*>::iterator i = view_states_.begin(); | |
809 i != view_states_.end(); ++i) { | |
810 ViewState* view_state = *i; | |
811 ColumnSet* column_set = view_state->column_set; | |
812 View* view = (*i)->view; | |
813 DCHECK(view); | |
814 int x = column_set->columns_[view_state->start_col]->Location() + | |
815 left_inset_; | |
816 int width = column_set->GetColumnWidth(view_state->start_col, | |
817 view_state->col_span); | |
818 CalculateSize(view_state->pref_width, view_state->h_align, | |
819 &x, &width); | |
820 int y = rows_[view_state->start_row]->Location() + top_inset_; | |
821 int height = LayoutElement::TotalSize(view_state->start_row, | |
822 view_state->row_span, &rows_); | |
823 if (view_state->v_align == BASELINE && view_state->baseline != -1) { | |
824 y += rows_[view_state->start_row]->max_ascent() - view_state->baseline; | |
825 height = view_state->pref_height; | |
826 } else { | |
827 CalculateSize(view_state->pref_height, view_state->v_align, &y, &height); | |
828 } | |
829 view->SetBounds(x, y, width, height); | |
830 } | |
831 } | |
832 | |
833 gfx::Size GridLayout::GetPreferredSize(View* host) { | |
834 DCHECK(host_ == host); | |
835 gfx::Size out; | |
836 SizeRowsAndColumns(false, 0, 0, &out); | |
837 return out; | |
838 } | |
839 | |
840 int GridLayout::GetPreferredHeightForWidth(View* host, int width) { | |
841 DCHECK(host_ == host); | |
842 gfx::Size pref; | |
843 SizeRowsAndColumns(false, width, 0, &pref); | |
844 return pref.height(); | |
845 } | |
846 | |
847 void GridLayout::SizeRowsAndColumns(bool layout, int width, int height, | |
848 gfx::Size* pref) { | |
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() + left_inset_ + right_inset_); | |
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() - left_inset_ - right_inset_); | |
871 // And reset the x coordinates. | |
872 (*i)->ResetColumnXCoordinates(); | |
873 } | |
874 | |
875 // Reset the height of each row. | |
876 LayoutElement::ResetSizes(&rows_); | |
877 | |
878 // Do the following: | |
879 // . If the view is aligned along it's baseline, obtain the baseline from the | |
880 // view and update the rows ascent/descent. | |
881 // . Reset the remaining_height of each view state. | |
882 // . If the width the view will be given is different than it's pref, ask | |
883 // for the height given a particularly width. | |
884 for (std::vector<ViewState*>::iterator i= view_states_.begin(); | |
885 i != view_states_.end() ; ++i) { | |
886 ViewState* view_state = *i; | |
887 view_state->remaining_height = view_state->pref_height; | |
888 | |
889 if (view_state->v_align == BASELINE) | |
890 view_state->baseline = view_state->view->GetBaseline(); | |
891 | |
892 if (view_state->h_align == FILL) { | |
893 // The view is resizable. As the pref height may vary with the width, | |
894 // ask for the pref again. | |
895 int actual_width = | |
896 view_state->column_set->GetColumnWidth(view_state->start_col, | |
897 view_state->col_span); | |
898 if (actual_width != view_state->pref_width && | |
899 !view_state->pref_height_fixed) { | |
900 // The width this view will get differs from it's preferred. Some Views | |
901 // pref height varies with it's width; ask for the preferred again. | |
902 view_state->pref_height = | |
903 view_state->view->GetHeightForWidth(actual_width); | |
904 view_state->remaining_height = view_state->pref_height; | |
905 } | |
906 } | |
907 } | |
908 | |
909 // Update the height/ascent/descent of each row from the views. | |
910 std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin(); | |
911 for (; view_states_iterator != view_states_.end() && | |
912 (*view_states_iterator)->row_span == 1; ++view_states_iterator) { | |
913 ViewState* view_state = *view_states_iterator; | |
914 Row* row = rows_[view_state->start_row]; | |
915 row->AdjustSize(view_state->remaining_height); | |
916 if (view_state->baseline != -1 && | |
917 view_state->baseline <= view_state->pref_height) { | |
918 row->AdjustSizeForBaseline(view_state->baseline, | |
919 view_state->pref_height - view_state->baseline); | |
920 } | |
921 view_state->remaining_height = 0; | |
922 } | |
923 | |
924 // Distribute the height of each view with a row span > 1. | |
925 for (; view_states_iterator != view_states_.end(); ++view_states_iterator) { | |
926 ViewState* view_state = *view_states_iterator; | |
927 | |
928 // Update the remaining_width from columns this view_state touches. | |
929 UpdateRemainingHeightFromRows(view_state); | |
930 | |
931 // Distribute the remaining height. | |
932 DistributeRemainingHeight(view_state); | |
933 } | |
934 | |
935 // Update the location of each of the rows. | |
936 LayoutElement::CalculateLocationsFromSize(&rows_); | |
937 | |
938 // We now know the preferred height, set it here. | |
939 pref->set_height(rows_[rows_.size() - 1]->Location() + | |
940 rows_[rows_.size() - 1]->Size() + top_inset_ + bottom_inset_); | |
941 | |
942 if (layout && height != pref->height()) { | |
943 // We're doing a layout, and the height differs from the preferred height, | |
944 // divy up the extra space. | |
945 LayoutElement::DistributeDelta(height - pref->height(), &rows_); | |
946 | |
947 // Reset y locations. | |
948 LayoutElement::CalculateLocationsFromSize(&rows_); | |
949 } | |
950 } | |
951 | |
952 void GridLayout::CalculateMasterColumnsIfNecessary() { | |
953 if (!calculated_master_columns_) { | |
954 calculated_master_columns_ = true; | |
955 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); | |
956 i != column_sets_.end(); ++i) { | |
957 (*i)->CalculateMasterColumns(); | |
958 } | |
959 } | |
960 } | |
961 | |
962 void GridLayout::AddViewState(ViewState* view_state) { | |
963 DCHECK(view_state->view && (view_state->view->parent() == NULL || | |
964 view_state->view->parent() == host_)); | |
965 if (!view_state->view->parent()) { | |
966 adding_view_ = true; | |
967 host_->AddChildView(view_state->view); | |
968 adding_view_ = false; | |
969 } | |
970 remaining_row_span_ = std::max(remaining_row_span_, view_state->row_span); | |
971 next_column_ += view_state->col_span; | |
972 current_row_col_set_->AddViewState(view_state); | |
973 // view_states are ordered by row_span (in ascending order). | |
974 std::vector<ViewState*>::iterator i = lower_bound(view_states_.begin(), | |
975 view_states_.end(), | |
976 view_state, | |
977 CompareByRowSpan); | |
978 view_states_.insert(i, view_state); | |
979 SkipPaddingColumns(); | |
980 } | |
981 | |
982 ColumnSet* GridLayout::GetColumnSet(int id) { | |
983 for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); | |
984 i != column_sets_.end(); ++i) { | |
985 if ((*i)->id_ == id) { | |
986 return *i; | |
987 } | |
988 } | |
989 return NULL; | |
990 } | |
991 | |
992 void GridLayout::AddRow(Row* row) { | |
993 current_row_++; | |
994 remaining_row_span_--; | |
995 DCHECK(remaining_row_span_ <= 0 || | |
996 row->column_set() == NULL || | |
997 row->column_set() == GetLastValidColumnSet()); | |
998 next_column_ = 0; | |
999 rows_.push_back(row); | |
1000 current_row_col_set_ = row->column_set(); | |
1001 SkipPaddingColumns(); | |
1002 } | |
1003 | |
1004 void GridLayout::UpdateRemainingHeightFromRows(ViewState* view_state) { | |
1005 for (int i = 0, start_row = view_state->start_row; | |
1006 i < view_state->row_span; ++i) { | |
1007 view_state->remaining_height -= rows_[i + start_row]->Size(); | |
1008 } | |
1009 } | |
1010 | |
1011 void GridLayout::DistributeRemainingHeight(ViewState* view_state) { | |
1012 int height = view_state->remaining_height; | |
1013 if (height <= 0) | |
1014 return; | |
1015 | |
1016 // Determine the number of resizable rows the view touches. | |
1017 int resizable_rows = 0; | |
1018 int start_row = view_state->start_row; | |
1019 int max_row = view_state->start_row + view_state->row_span; | |
1020 for (int i = start_row; i < max_row; ++i) { | |
1021 if (rows_[i]->IsResizable()) { | |
1022 resizable_rows++; | |
1023 } | |
1024 } | |
1025 | |
1026 if (resizable_rows > 0) { | |
1027 // There are resizable rows, give the remaining height to them. | |
1028 int to_distribute = height / resizable_rows; | |
1029 for (int i = start_row; i < max_row; ++i) { | |
1030 if (rows_[i]->IsResizable()) { | |
1031 height -= to_distribute; | |
1032 if (height < to_distribute) { | |
1033 // Give all slop to the last column. | |
1034 to_distribute += height; | |
1035 } | |
1036 rows_[i]->SetSize(rows_[i]->Size() + to_distribute); | |
1037 } | |
1038 } | |
1039 } else { | |
1040 // None of the rows are resizable, divy the remaining height up equally | |
1041 // among all rows the view touches. | |
1042 int each_row_height = height / view_state->row_span; | |
1043 for (int i = start_row; i < max_row; ++i) { | |
1044 height -= each_row_height; | |
1045 if (height < each_row_height) | |
1046 each_row_height += height; | |
1047 rows_[i]->SetSize(rows_[i]->Size() + each_row_height); | |
1048 } | |
1049 view_state->remaining_height = 0; | |
1050 } | |
1051 } | |
1052 | |
1053 void GridLayout::SkipPaddingColumns() { | |
1054 if (!current_row_col_set_) | |
1055 return; | |
1056 while (next_column_ < current_row_col_set_->num_columns() && | |
1057 current_row_col_set_->columns_[next_column_]->is_padding_) { | |
1058 next_column_++; | |
1059 } | |
1060 } | |
1061 | |
1062 ColumnSet* GridLayout::GetLastValidColumnSet() { | |
1063 for (int i = current_row_ - 1; i >= 0; --i) { | |
1064 if (rows_[i]->column_set()) | |
1065 return rows_[i]->column_set(); | |
1066 } | |
1067 return NULL; | |
1068 } | |
1069 | |
1070 } // namespace views | |
OLD | NEW |