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.
5 #include "chrome/browser/ui/cocoa/autofill/simple_grid_layout.h"
9 #include "base/logging.h"
10 #include "base/stl_util.h"
13 const int kAutoColumnIdStart = 1000000; // Starting ID for autogeneration.
16 // Encapsulates state for a single NSView in the layout
19 ViewState(NSView* view, ColumnSet* column_set, int row, int column);
21 // Gets the current width of the column associated with this view.
22 float GetColumnWidth();
24 // Get the preferred height for specified width.
25 float GetHeightForWidth(float with);
27 Column* GetColumn() const { return column_set_->GetColumn(column_); }
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; }
36 ColumnSet* column_set_;
44 LayoutElement(float resize_percent, int fixed_width);
45 virtual ~LayoutElement() {}
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) {
57 static void CalculateLocationsFromSize(ScopedVector<T>* elements) {
58 // Reset the layout width of each column.
60 for (typename std::vector<T*>::iterator i = elements->begin();
61 i != elements->end(); ++i) {
62 (*i)->SetLocation(location);
63 location += (*i)->Size();
67 float Size() { return size_; }
73 void SetSize(float size) {
77 float Location() const {
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);
87 void SetLocation(float location) {
92 return resize_percent_ > 0.0f;
95 float ResizePercent() {
96 return resize_percent_;
100 float resize_percent_;
106 LayoutElement::LayoutElement(float resize_percent, int fixed_width)
107 : resize_percent_(resize_percent),
108 fixed_width_(fixed_width),
113 class Column : public LayoutElement {
115 Column(float resize_percent, int fixed_width, bool is_padding);
117 bool is_padding() { return is_padding_; }
120 const bool is_padding_;
123 Column::Column(float resize_percent, int fixed_width, bool is_padding)
124 : LayoutElement(resize_percent, fixed_width),
125 is_padding_(is_padding) {
128 class Row : public LayoutElement {
130 Row(float resize_percent, int fixed_height, ColumnSet* column_set);
132 ColumnSet* column_set() { return column_set_; }
135 ColumnSet* column_set_;
138 Row::Row(float resize_percent, int fixed_height, ColumnSet* column_set)
139 : LayoutElement(resize_percent, fixed_height),
140 column_set_(column_set) {
143 ViewState::ViewState(NSView* view, ColumnSet* column_set, int row, int column)
145 column_set_(column_set),
149 float ViewState::GetColumnWidth() {
150 return column_set_->GetColumnWidth(column_);
153 float ViewState::GetHeightForWidth(float width) {
154 // NSView doesn't have any way to make height fit size, get frame height.
155 return NSHeight([view_ frame]);
158 ColumnSet::ColumnSet(int id) : id_(id) {
161 ColumnSet::~ColumnSet() {
164 void ColumnSet::AddPaddingColumn(int fixed_width) {
165 columns_.push_back(new Column(0.0f, fixed_width, true));
168 void ColumnSet::AddColumn(float resize_percent) {
169 columns_.push_back(new Column(resize_percent, 0, false));
172 void ColumnSet::CalculateSize(float width) {
173 // Reset column widths
174 LayoutElement::ResetSizes(&columns_);
175 width = CalculateRemainingWidth(width);
176 DistributeRemainingWidth(width);
179 void ColumnSet::ResetColumnXCoordinates() {
180 LayoutElement::CalculateLocationsFromSize(&columns_);
183 float ColumnSet::CalculateRemainingWidth(float width) {
184 for (size_t i = 0; i < columns_.size(); ++i)
185 width -= columns_[i]->Size();
190 void ColumnSet::DistributeRemainingWidth(float width) {
191 float total_resize = 0.0f;
192 int resizable_columns = 0.0;
194 for (size_t i = 0; i < columns_.size(); ++i) {
195 if (columns_[i]->IsResizable()) {
196 total_resize += columns_[i]->ResizePercent();
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);
213 float ColumnSet::GetColumnWidth(int column_index) {
214 if (column_index < 0 || column_index >= num_columns())
216 return columns_[column_index]->Size();
219 float ColumnSet::ColumnLocation(int column_index) {
220 if (column_index < 0 || column_index >= num_columns())
222 return columns_[column_index]->Location();
225 SimpleGridLayout::SimpleGridLayout(NSView* host)
227 current_auto_id_(kAutoColumnIdStart),
232 SimpleGridLayout::~SimpleGridLayout() {
235 ColumnSet* SimpleGridLayout::AddColumnSet(int id) {
236 DCHECK(GetColumnSet(id) == NULL);
237 ColumnSet* column_set = new ColumnSet(id);
238 column_sets_.push_back(column_set);
242 ColumnSet* 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) {
252 void SimpleGridLayout::AddPaddingRow(int fixed_height) {
253 AddRow(new Row(0.0f, fixed_height, NULL));
256 void SimpleGridLayout::StartRow(float vertical_resize, int column_set_id) {
257 ColumnSet* column_set = GetColumnSet(column_set_id);
259 AddRow(new Row(vertical_resize, 0, column_set));
262 ColumnSet* SimpleGridLayout::AddRow() {
263 AddRow(new Row(0, 0, AddColumnSet(current_auto_id_++)));
264 return column_sets_.back();
267 void 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();
276 void SimpleGridLayout::AddView(NSView* view) {
277 [host_ addSubview:view];
278 DCHECK(next_column_ < GetLastValidColumnSet()->num_columns());
279 view_states_.push_back(
281 GetLastValidColumnSet(),
284 SkipPaddingColumns();
287 // Sizes elements to fit into the superViews bounds, according to constraints.
288 void 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)];
302 void 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();
310 // Reset the height of each row.
311 LayoutElement::ResetSizes(&rows_);
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;
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();
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));
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());
337 // Update the location of each of the rows.
338 LayoutElement::CalculateLocationsFromSize(&rows_);
341 void 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()) {
349 ColumnSet* SimpleGridLayout::GetLastValidColumnSet() {
350 for (int i = num_rows() - 1; i >= 0; --i) {
351 if (rows_[i]->column_set())
352 return rows_[i]->column_set();
357 float SimpleGridLayout::GetRowHeight(int row_index) {
358 if (row_index < 0 || row_index >= num_rows())
360 return rows_[row_index]->Size();
363 float SimpleGridLayout::GetRowLocation(int row_index) const {
364 if (row_index < 0 || row_index >= num_rows())
366 return rows_[row_index]->Location();
369 float SimpleGridLayout::GetPreferredHeightForWidth(float width) {
373 SizeRowsAndColumns(width);
374 return rows_.back()->Location() + rows_.back()->Size();
377 void SimpleGridLayout::AddRow(Row* row) {
379 rows_.push_back(row);