BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / autofill / simple_grid_layout.mm
blob2694877527a445f28c864ff66e5c19942eab7f86
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"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "base/stl_util.h"
12 namespace {
13 const int kAutoColumnIdStart = 1000000;  // Starting ID for autogeneration.
16 // Encapsulates state for a single NSView in the layout
17 class ViewState {
18  public:
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; }
34  private:
35   NSView* view_;
36   ColumnSet* column_set_;
37   int row_;
38   int column_;
39   float pref_height_;
42 class LayoutElement {
43  public:
44   LayoutElement(float resize_percent, int fixed_width);
45   virtual ~LayoutElement() {}
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   }
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   }
67   float Size() { return size_; }
69   void ResetSize() {
70       size_ = fixed_width_;
71   }
73   void SetSize(float size) {
74     size_ = size;
75   }
77   float Location() const {
78     return location_;
79   }
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   }
87   void SetLocation(float location) {
88     location_ = location;
89   }
91   bool IsResizable() {
92     return resize_percent_ > 0.0f;
93   }
95   float ResizePercent() {
96     return resize_percent_;
97   }
99  private:
100   float resize_percent_;
101   int fixed_width_;
102   float size_;
103   float location_;
106 LayoutElement::LayoutElement(float resize_percent, int fixed_width)
107     : resize_percent_(resize_percent),
108       fixed_width_(fixed_width),
109       size_(0),
110       location_(0) {
113 class Column : public LayoutElement {
114  public:
115    Column(float resize_percent, int fixed_width, bool is_padding);
117    bool is_padding() { return is_padding_; }
119   private:
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 {
129  public:
130   Row(float resize_percent, int fixed_height, ColumnSet* column_set);
132   ColumnSet* column_set() { return column_set_; }
134  private:
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)
144     : view_(view),
145       column_set_(column_set),
146       row_(row),
147       column_(column) {}
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();
187   return width;
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();
197       resizable_columns++;
198     }
199   }
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   }
213 float ColumnSet::GetColumnWidth(int column_index) {
214   if (column_index < 0 || column_index >= num_columns())
215     return 0.0;
216   return columns_[column_index]->Size();
219 float ColumnSet::ColumnLocation(int column_index) {
220   if (column_index < 0 || column_index >= num_columns())
221     return 0.0;
222   return columns_[column_index]->Location();
225 SimpleGridLayout::SimpleGridLayout(NSView* host)
226     : next_column_(0),
227       current_auto_id_(kAutoColumnIdStart),
228       host_(host) {
229   [host_ frame];
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);
239   return 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) {
246       return *i;
247     }
248   }
249   return NULL;
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);
258   DCHECK(column_set);
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(
280       new ViewState(view,
281                     GetLastValidColumnSet(),
282                     rows_.size() - 1,
283                     next_column_++));
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)];
299   }
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();
308   }
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));
326   }
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   }
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()) {
345     next_column_++;
346   }
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();
353   }
354   return NULL;
357 float SimpleGridLayout::GetRowHeight(int row_index) {
358   if (row_index < 0 || row_index >= num_rows())
359     return 0.0;
360   return rows_[row_index]->Size();
363 float 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();
369 float SimpleGridLayout::GetPreferredHeightForWidth(float width) {
370   if (rows_.empty())
371     return 0.0f;
373   SizeRowsAndColumns(width);
374   return rows_.back()->Location() + rows_.back()->Size();
377 void SimpleGridLayout::AddRow(Row* row) {
378   next_column_ = 0;
379   rows_.push_back(row);