1// Copyright (c) 2013 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 "chrome/browser/ui/cocoa/autofill/simple_grid_layout.h" 6 7#include <algorithm> 8 9#include "base/logging.h" 10#include "base/stl_util.h" 11 12namespace { 13const int kAutoColumnIdStart = 1000000; // Starting ID for autogeneration. 14} 15 16// Encapsulates state for a single NSView in the layout 17class ViewState { 18 public: 19 ViewState(NSView* view, ColumnSet* column_set, int row, int column); 20 21 // Gets the current width of the column associated with this view. 22 float GetColumnWidth(); 23 24 // Get the preferred height for specified width. 25 float GetHeightForWidth(float with); 26 27 Column* GetColumn() const { return column_set_->GetColumn(column_); } 28 29 int row_index() { return row_; } 30 NSView* view() { return view_; } 31 float preferred_height() { return pref_height_; } 32 void set_preferred_height(float height) { pref_height_ = height; } 33 34 private: 35 NSView* view_; 36 ColumnSet* column_set_; 37 int row_; 38 int column_; 39 float pref_height_; 40}; 41 42class LayoutElement { 43 public: 44 LayoutElement(float resize_percent, int fixed_width); 45 virtual ~LayoutElement() {} 46 47 template <class T> 48 static void ResetSizes(ScopedVector<T>* elements) { 49 // Reset the layout width of each column. 50 for (typename std::vector<T*>::iterator i = elements->begin(); 51 i != elements->end(); ++i) { 52 (*i)->ResetSize(); 53 } 54 } 55 56 template <class T> 57 static void CalculateLocationsFromSize(ScopedVector<T>* elements) { 58 // Reset the layout width of each column. 59 int location = 0; 60 for (typename std::vector<T*>::iterator i = elements->begin(); 61 i != elements->end(); ++i) { 62 (*i)->SetLocation(location); 63 location += (*i)->Size(); 64 } 65 } 66 67 float Size() { return size_; } 68 69 void ResetSize() { 70 size_ = fixed_width_; 71 } 72 73 void SetSize(float size) { 74 size_ = size; 75 } 76 77 float Location() const { 78 return location_; 79 } 80 81 // Adjusts the size of this LayoutElement to be the max of the current size 82 // and the specified size. 83 virtual void AdjustSize(float size) { 84 size_ = std::max(size_, size); 85 } 86 87 void SetLocation(float location) { 88 location_ = location; 89 } 90 91 bool IsResizable() { 92 return resize_percent_ > 0.0f; 93 } 94 95 float ResizePercent() { 96 return resize_percent_; 97 } 98 99 private: 100 float resize_percent_; 101 int fixed_width_; 102 float size_; 103 float location_; 104}; 105 106LayoutElement::LayoutElement(float resize_percent, int fixed_width) 107 : resize_percent_(resize_percent), 108 fixed_width_(fixed_width), 109 size_(0), 110 location_(0) { 111} 112 113class Column : public LayoutElement { 114 public: 115 Column(float resize_percent, int fixed_width, bool is_padding); 116 117 bool is_padding() { return is_padding_; } 118 119 private: 120 const bool is_padding_; 121}; 122 123Column::Column(float resize_percent, int fixed_width, bool is_padding) 124 : LayoutElement(resize_percent, fixed_width), 125 is_padding_(is_padding) { 126} 127 128class Row : public LayoutElement { 129 public: 130 Row(float resize_percent, int fixed_height, ColumnSet* column_set); 131 132 ColumnSet* column_set() { return column_set_; } 133 134 private: 135 ColumnSet* column_set_; 136}; 137 138Row::Row(float resize_percent, int fixed_height, ColumnSet* column_set) 139 : LayoutElement(resize_percent, fixed_height), 140 column_set_(column_set) { 141} 142 143ViewState::ViewState(NSView* view, ColumnSet* column_set, int row, int column) 144 : view_(view), 145 column_set_(column_set), 146 row_(row), 147 column_(column) {} 148 149float ViewState::GetColumnWidth() { 150 return column_set_->GetColumnWidth(column_); 151} 152 153float ViewState::GetHeightForWidth(float width) { 154 // NSView doesn't have any way to make height fit size, get frame height. 155 return NSHeight([view_ frame]); 156} 157 158ColumnSet::ColumnSet(int id) : id_(id) { 159} 160 161ColumnSet::~ColumnSet() { 162} 163 164void ColumnSet::AddPaddingColumn(int fixed_width) { 165 columns_.push_back(new Column(0.0f, fixed_width, true)); 166} 167 168void ColumnSet::AddColumn(float resize_percent) { 169 columns_.push_back(new Column(resize_percent, 0, false)); 170} 171 172void ColumnSet::CalculateSize(float width) { 173 // Reset column widths 174 LayoutElement::ResetSizes(&columns_); 175 width = CalculateRemainingWidth(width); 176 DistributeRemainingWidth(width); 177} 178 179void ColumnSet::ResetColumnXCoordinates() { 180 LayoutElement::CalculateLocationsFromSize(&columns_); 181} 182 183float ColumnSet::CalculateRemainingWidth(float width) { 184 for (size_t i = 0; i < columns_.size(); ++i) 185 width -= columns_[i]->Size(); 186 187 return width; 188} 189 190void ColumnSet::DistributeRemainingWidth(float width) { 191 float total_resize = 0.0f; 192 int resizable_columns = 0.0; 193 194 for (size_t i = 0; i < columns_.size(); ++i) { 195 if (columns_[i]->IsResizable()) { 196 total_resize += columns_[i]->ResizePercent(); 197 resizable_columns++; 198 } 199 } 200 201 float remaining_width = width; 202 for (size_t i = 0; i < columns_.size(); ++i) { 203 if (columns_[i]->IsResizable()) { 204 float delta = (resizable_columns == 0) ? remaining_width : 205 (width * columns_[i]->ResizePercent() / total_resize); 206 remaining_width -= delta; 207 columns_[i]->SetSize(columns_[i]->Size() + delta); 208 resizable_columns--; 209 } 210 } 211} 212 213float ColumnSet::GetColumnWidth(int column_index) { 214 if (column_index < 0 || column_index >= num_columns()) 215 return 0.0; 216 return columns_[column_index]->Size(); 217} 218 219float ColumnSet::ColumnLocation(int column_index) { 220 if (column_index < 0 || column_index >= num_columns()) 221 return 0.0; 222 return columns_[column_index]->Location(); 223} 224 225SimpleGridLayout::SimpleGridLayout(NSView* host) 226 : next_column_(0), 227 current_auto_id_(kAutoColumnIdStart), 228 host_(host) { 229 [host_ frame]; 230} 231 232SimpleGridLayout::~SimpleGridLayout() { 233} 234 235ColumnSet* SimpleGridLayout::AddColumnSet(int id) { 236 DCHECK(GetColumnSet(id) == NULL); 237 ColumnSet* column_set = new ColumnSet(id); 238 column_sets_.push_back(column_set); 239 return column_set; 240} 241 242ColumnSet* SimpleGridLayout::GetColumnSet(int id) { 243 for (ScopedVector<ColumnSet>::const_iterator i = column_sets_.begin(); 244 i != column_sets_.end(); ++i) { 245 if ((*i)->id() == id) { 246 return *i; 247 } 248 } 249 return NULL; 250} 251 252void SimpleGridLayout::AddPaddingRow(int fixed_height) { 253 AddRow(new Row(0.0f, fixed_height, NULL)); 254} 255 256void SimpleGridLayout::StartRow(float vertical_resize, int column_set_id) { 257 ColumnSet* column_set = GetColumnSet(column_set_id); 258 DCHECK(column_set); 259 AddRow(new Row(vertical_resize, 0, column_set)); 260} 261 262ColumnSet* SimpleGridLayout::AddRow() { 263 AddRow(new Row(0, 0, AddColumnSet(current_auto_id_++))); 264 return column_sets_.back(); 265} 266 267void SimpleGridLayout::SkipColumns(int col_count) { 268 DCHECK(col_count > 0); 269 next_column_ += col_count; 270 ColumnSet* current_row_col_set_ = GetLastValidColumnSet(); 271 DCHECK(current_row_col_set_ && 272 next_column_ <= current_row_col_set_->num_columns()); 273 SkipPaddingColumns(); 274} 275 276void SimpleGridLayout::AddView(NSView* view) { 277 [host_ addSubview:view]; 278 DCHECK(next_column_ < GetLastValidColumnSet()->num_columns()); 279 view_states_.push_back( 280 new ViewState(view, 281 GetLastValidColumnSet(), 282 rows_.size() - 1, 283 next_column_++)); 284 SkipPaddingColumns(); 285} 286 287// Sizes elements to fit into the superViews bounds, according to constraints. 288void SimpleGridLayout::Layout(NSView* superView) { 289 SizeRowsAndColumns(NSWidth([superView bounds])); 290 for (std::vector<ViewState*>::iterator i = view_states_.begin(); 291 i != view_states_.end(); ++i) { 292 ViewState* view_state = *i; 293 NSView* view = view_state->view(); 294 NSRect frame = NSMakeRect(view_state->GetColumn()->Location(), 295 rows_[view_state->row_index()]->Location(), 296 view_state->GetColumn()->Size(), 297 rows_[view_state->row_index()]->Size()); 298 [view setFrame:NSIntegralRect(frame)]; 299 } 300} 301 302void SimpleGridLayout::SizeRowsAndColumns(float width) { 303 // Size all columns first. 304 for (ScopedVector<ColumnSet>::iterator i = column_sets_.begin(); 305 i != column_sets_.end(); ++i) { 306 (*i)->CalculateSize(width); 307 (*i)->ResetColumnXCoordinates(); 308 } 309 310 // Reset the height of each row. 311 LayoutElement::ResetSizes(&rows_); 312 313 // For each ViewState, obtain the preferred height 314 for (std::vector<ViewState*>::iterator i= view_states_.begin(); 315 i != view_states_.end() ; ++i) { 316 ViewState* view_state = *i; 317 318 // The view is resizable. As the pref height may vary with the width, 319 // ask for the pref again. 320 int actual_width = view_state->GetColumnWidth(); 321 322 // The width this view will get differs from its preferred. Some Views 323 // pref height varies with its width; ask for the preferred again. 324 view_state->set_preferred_height( 325 view_state->GetHeightForWidth(actual_width)); 326 } 327 328 329 // Make sure each row can accommodate all contained ViewStates. 330 std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin(); 331 for (; view_states_iterator != view_states_.end(); ++view_states_iterator) { 332 ViewState* view_state = *view_states_iterator; 333 Row* row = rows_[view_state->row_index()]; 334 row->AdjustSize(view_state->preferred_height()); 335 } 336 337 // Update the location of each of the rows. 338 LayoutElement::CalculateLocationsFromSize(&rows_); 339} 340 341void SimpleGridLayout::SkipPaddingColumns() { 342 ColumnSet* current_row_col_set_ = GetLastValidColumnSet(); 343 while (next_column_ < current_row_col_set_->num_columns() && 344 current_row_col_set_->GetColumn(next_column_)->is_padding()) { 345 next_column_++; 346 } 347} 348 349ColumnSet* SimpleGridLayout::GetLastValidColumnSet() { 350 for (int i = num_rows() - 1; i >= 0; --i) { 351 if (rows_[i]->column_set()) 352 return rows_[i]->column_set(); 353 } 354 return NULL; 355} 356 357float SimpleGridLayout::GetRowHeight(int row_index) { 358 if (row_index < 0 || row_index >= num_rows()) 359 return 0.0; 360 return rows_[row_index]->Size(); 361} 362 363float SimpleGridLayout::GetRowLocation(int row_index) const { 364 if (row_index < 0 || row_index >= num_rows()) 365 return 0.0; 366 return rows_[row_index]->Location(); 367} 368 369float SimpleGridLayout::GetPreferredHeightForWidth(float width) { 370 if (rows_.empty()) 371 return 0.0f; 372 373 SizeRowsAndColumns(width); 374 return rows_.back()->Location() + rows_.back()->Size(); 375} 376 377void SimpleGridLayout::AddRow(Row* row) { 378 next_column_ = 0; 379 rows_.push_back(row); 380} 381 382