Home | History | Annotate | Download | only in autofill
      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 
     12 namespace {
     13 const int kAutoColumnIdStart = 1000000;  // Starting ID for autogeneration.
     14 }
     15 
     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);
     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 
     42 class 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 
    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) {
    111 }
    112 
    113 class 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 
    123 Column::Column(float resize_percent, int fixed_width, bool is_padding)
    124     : LayoutElement(resize_percent, fixed_width),
    125       is_padding_(is_padding) {
    126 }
    127 
    128 class 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 
    138 Row::Row(float resize_percent, int fixed_height, ColumnSet* column_set)
    139     : LayoutElement(resize_percent, fixed_height),
    140       column_set_(column_set) {
    141 }
    142 
    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) {}
    148 
    149 float ViewState::GetColumnWidth() {
    150   return column_set_->GetColumnWidth(column_);
    151 }
    152 
    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]);
    156 }
    157 
    158 ColumnSet::ColumnSet(int id) : id_(id) {
    159 }
    160 
    161 ColumnSet::~ColumnSet() {
    162 }
    163 
    164 void ColumnSet::AddPaddingColumn(int fixed_width) {
    165   columns_.push_back(new Column(0.0f, fixed_width, true));
    166 }
    167 
    168 void ColumnSet::AddColumn(float resize_percent) {
    169   columns_.push_back(new Column(resize_percent, 0, false));
    170 }
    171 
    172 void ColumnSet::CalculateSize(float width) {
    173   // Reset column widths
    174   LayoutElement::ResetSizes(&columns_);
    175   width = CalculateRemainingWidth(width);
    176   DistributeRemainingWidth(width);
    177 }
    178 
    179 void ColumnSet::ResetColumnXCoordinates() {
    180   LayoutElement::CalculateLocationsFromSize(&columns_);
    181 }
    182 
    183 float 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 
    190 void 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 
    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();
    217 }
    218 
    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();
    223 }
    224 
    225 SimpleGridLayout::SimpleGridLayout(NSView* host)
    226     : next_column_(0),
    227       current_auto_id_(kAutoColumnIdStart),
    228       host_(host) {
    229   [host_ frame];
    230 }
    231 
    232 SimpleGridLayout::~SimpleGridLayout() {
    233 }
    234 
    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;
    240 }
    241 
    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;
    250 }
    251 
    252 void SimpleGridLayout::AddPaddingRow(int fixed_height) {
    253   AddRow(new Row(0.0f, fixed_height, NULL));
    254 }
    255 
    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));
    260 }
    261 
    262 ColumnSet* SimpleGridLayout::AddRow() {
    263   AddRow(new Row(0, 0, AddColumnSet(current_auto_id_++)));
    264   return column_sets_.back();
    265 }
    266 
    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();
    274 }
    275 
    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();
    285 }
    286 
    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   }
    300 }
    301 
    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   }
    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 
    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   }
    347 }
    348 
    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;
    355 }
    356 
    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();
    361 }
    362 
    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();
    367 }
    368 
    369 float 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 
    377 void SimpleGridLayout::AddRow(Row* row) {
    378   next_column_ = 0;
    379   rows_.push_back(row);
    380 }
    381 
    382