[email protected] | e1791eb5 | 2010-05-21 03:10:04 | [diff] [blame] | 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 4 | |
[email protected] | 2362e4f | 2009-05-08 00:34:05 | [diff] [blame] | 5 | #include "views/grid_layout.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 6 | |
| 7 | #include <algorithm> |
| 8 | |
| 9 | #include "base/logging.h" |
[email protected] | e1791eb5 | 2010-05-21 03:10:04 | [diff] [blame] | 10 | #include "base/stl_util-inl.h" |
[email protected] | 5c7293a | 2010-03-17 06:40:57 | [diff] [blame] | 11 | #include "gfx/insets.h" |
[email protected] | 5ba55842 | 2009-05-29 22:40:33 | [diff] [blame] | 12 | #include "views/standard_layout.h" |
[email protected] | 2362e4f | 2009-05-08 00:34:05 | [diff] [blame] | 13 | #include "views/view.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 14 | |
[email protected] | c2dacc9 | 2008-10-16 23:51:38 | [diff] [blame] | 15 | namespace views { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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. |
[email protected] | 8c11771 | 2009-01-13 12:26:46 | [diff] [blame] | 27 | for (typename std::vector<T*>::iterator i = elements->begin(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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; |
[email protected] | 8c11771 | 2009-01-13 12:26:46 | [diff] [blame] | 39 | for (typename std::vector<T*>::iterator i = elements->begin(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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; |
[email protected] | 8c11771 | 2009-01-13 12:26:46 | [diff] [blame] | 56 | for (typename std::vector<T*>::iterator i = elements->begin(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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; |
[email protected] | 8c11771 | 2009-01-13 12:26:46 | [diff] [blame] | 67 | for (typename std::vector<T*>::iterator i = elements->begin(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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 | |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 148 | DISALLOW_COPY_AND_ASSIGN(LayoutElement); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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 | |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 218 | DISALLOW_COPY_AND_ASSIGN(Column); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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), |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 270 | column_set_(column_set), |
| 271 | max_ascent_(0), |
| 272 | max_descent_(0) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | virtual ~Row() {} |
| 276 | |
| 277 | virtual void ResetSize() { |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 278 | max_ascent_ = max_descent_ = 0; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 279 | SetSize(height_); |
| 280 | } |
| 281 | |
| 282 | ColumnSet* column_set() { |
| 283 | return column_set_; |
| 284 | } |
| 285 | |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 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 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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 | |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 307 | int max_ascent_; |
| 308 | int max_descent_; |
| 309 | |
| 310 | DISALLOW_COPY_AND_ASSIGN(Row); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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), |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 334 | remaining_height(0), |
| 335 | baseline(-1) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 345 | const int col_span; |
[email protected] | 8c11771 | 2009-01-13 12:26:46 | [diff] [blame] | 346 | const int row_span; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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; |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 363 | |
| 364 | // The baseline. Only used if the view is vertically aligned along the |
| 365 | // baseline. |
| 366 | int baseline; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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() { |
[email protected] | e1791eb5 | 2010-05-21 03:10:04 | [diff] [blame] | 383 | STLDeleteElements(&columns_); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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; |
[email protected] | a665d47 | 2010-02-24 22:17:03 | [diff] [blame] | 546 | float total_resize = 0; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 547 | for (int i = start_col; i < max_col; ++i) { |
| 548 | if (columns_[i]->IsResizable()) { |
[email protected] | a665d47 | 2010-02-24 22:17:03 | [diff] [blame] | 549 | total_resize += columns_[i]->ResizePercent(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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) { |
[email protected] | a665d47 | 2010-02-24 22:17:03 | [diff] [blame] | 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) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 561 | if (columns_[i]->IsResizable()) { |
[email protected] | a665d47 | 2010-02-24 22:17:03 | [diff] [blame] | 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); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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() { |
[email protected] | 154f8bc | 2008-10-15 18:02:30 | [diff] [blame] | 603 | gfx::Size pref; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 608 | if (!view_state->pref_width_fixed || !view_state->pref_height_fixed) { |
[email protected] | 154f8bc | 2008-10-15 18:02:30 | [diff] [blame] | 609 | pref = view_state->view->GetPreferredSize(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 610 | if (!view_state->pref_width_fixed) |
[email protected] | 154f8bc | 2008-10-15 18:02:30 | [diff] [blame] | 611 | view_state->pref_width = pref.width(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 612 | if (!view_state->pref_height_fixed) |
[email protected] | 154f8bc | 2008-10-15 18:02:30 | [diff] [blame] | 613 | view_state->pref_height = pref.height(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 614 | } |
[email protected] | 154f8bc | 2008-10-15 18:02:30 | [diff] [blame] | 615 | view_state->remaining_width = pref.width(); |
| 616 | view_state->remaining_height = pref.height(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 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() { |
[email protected] | e1791eb5 | 2010-05-21 03:10:04 | [diff] [blame] | 674 | STLDeleteElements(&column_sets_); |
| 675 | STLDeleteElements(&view_states_); |
| 676 | STLDeleteElements(&rows_); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 677 | } |
| 678 | |
| 679 | void 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] | 3a48345 | 2009-12-23 08:56:20 | [diff] [blame] | 686 | void GridLayout::SetInsets(const gfx::Insets& insets) { |
| 687 | SetInsets(insets.top(), insets.left(), insets.bottom(), insets.right()); |
| 688 | } |
| 689 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 690 | ColumnSet* 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 | |
| 697 | void 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 | |
| 703 | void 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 | |
| 709 | void GridLayout::AddPaddingRow(float vertical_resize, int pixel_count) { |
| 710 | AddRow(new Row(true, pixel_count, vertical_resize, NULL)); |
| 711 | } |
| 712 | |
| 713 | void 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 | |
| 721 | void GridLayout::AddView(View* view) { |
| 722 | AddView(view, 1, 1); |
| 723 | } |
| 724 | |
| 725 | void 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 | |
| 732 | void 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 | |
| 737 | void 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] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 742 | // 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 745 | 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 | |
| 752 | static 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] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 761 | 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 764 | 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 | |
| 776 | void GridLayout::Installed(View* host) { |
| 777 | DCHECK(host_ == host); |
| 778 | } |
| 779 | |
| 780 | void GridLayout::Uninstalled(View* host) { |
| 781 | DCHECK(host_ == host); |
| 782 | } |
| 783 | |
| 784 | void GridLayout::ViewAdded(View* host, View* view) { |
| 785 | DCHECK(host_ == host && adding_view_); |
| 786 | } |
| 787 | |
| 788 | void GridLayout::ViewRemoved(View* host, View* view) { |
| 789 | DCHECK(host_ == host); |
| 790 | } |
| 791 | |
| 792 | void 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] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 796 | gfx::Size pref; |
[email protected] | 6f3bb6c | 2008-09-17 22:25:33 | [diff] [blame] | 797 | SizeRowsAndColumns(true, host_->width(), host_->height(), &pref); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 798 | |
| 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] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 815 | 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 821 | view->SetBounds(x, y, width, height); |
| 822 | } |
| 823 | } |
| 824 | |
[email protected] | 154f8bc | 2008-10-15 18:02:30 | [diff] [blame] | 825 | gfx::Size GridLayout::GetPreferredSize(View* host) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 826 | DCHECK(host_ == host); |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 827 | gfx::Size out; |
[email protected] | 154f8bc | 2008-10-15 18:02:30 | [diff] [blame] | 828 | SizeRowsAndColumns(false, 0, 0, &out); |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 829 | return out; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 830 | } |
| 831 | |
| 832 | int GridLayout::GetPreferredHeightForWidth(View* host, int width) { |
| 833 | DCHECK(host_ == host); |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 834 | gfx::Size pref; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 835 | SizeRowsAndColumns(false, width, 0, &pref); |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 836 | return pref.height(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 837 | } |
| 838 | |
| 839 | void GridLayout::SizeRowsAndColumns(bool layout, int width, int height, |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 840 | gfx::Size* pref) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 841 | // Make sure the master columns have been calculated. |
| 842 | CalculateMasterColumnsIfNecessary(); |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 843 | pref->SetSize(0, 0); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 844 | if (rows_.empty()) |
| 845 | return; |
| 846 | |
[email protected] | f861719 | 2010-07-05 07:57:10 | [diff] [blame] | 847 | // 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 850 | for (std::vector<ColumnSet*>::iterator i = column_sets_.begin(); |
| 851 | i != column_sets_.end(); ++i) { |
| 852 | (*i)->CalculateSize(); |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 853 | pref->set_width(std::max(pref->width(), (*i)->LayoutWidth())); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 854 | } |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 855 | pref->set_width(pref->width() + left_inset_ + right_inset_); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 856 | |
[email protected] | f861719 | 2010-07-05 07:57:10 | [diff] [blame] | 857 | // 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 867 | // Reset the height of each row. |
| 868 | LayoutElement::ResetSizes(&rows_); |
| 869 | |
[email protected] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 870 | // 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 873 | // . 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] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 880 | |
| 881 | if (view_state->v_align == BASELINE) |
| 882 | view_state->baseline = view_state->view->GetBaseline(); |
| 883 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 884 | 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] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 901 | // Update the height/ascent/descent of each row from the views. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 902 | 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] | a136016 | 2009-11-30 21:19:07 | [diff] [blame] | 908 | 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.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 913 | 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] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 931 | pref->set_height(rows_[rows_.size() - 1]->Location() + |
| 932 | rows_[rows_.size() - 1]->Size() + top_inset_ + bottom_inset_); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 933 | |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 934 | if (layout && height != pref->height()) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 935 | // We're doing a layout, and the height differs from the preferred height, |
| 936 | // divy up the extra space. |
[email protected] | 11700330 | 2008-12-16 19:38:18 | [diff] [blame] | 937 | LayoutElement::DistributeDelta(height - pref->height(), &rows_); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 938 | |
| 939 | // Reset y locations. |
| 940 | LayoutElement::CalculateLocationsFromSize(&rows_); |
| 941 | } |
| 942 | } |
| 943 | |
| 944 | void 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 | |
| 954 | void 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 | |
| 974 | ColumnSet* 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 | |
| 984 | void 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 | |
| 996 | void 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 | |
| 1003 | void 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 | |
| 1045 | void 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 | |
| 1054 | ColumnSet* 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] | c2dacc9 | 2008-10-16 23:51:38 | [diff] [blame] | 1062 | } // namespace views |
[email protected] | 5ba55842 | 2009-05-29 22:40:33 | [diff] [blame] | 1063 | |
| 1064 | views::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 | } |