| 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 |