| Index: chrome/browser/ui/cocoa/autofill/simple_grid_layout.mm
|
| diff --git a/chrome/browser/ui/cocoa/autofill/simple_grid_layout.mm b/chrome/browser/ui/cocoa/autofill/simple_grid_layout.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..751fa73d9f04ac26bfc6f48713e90a8c9952810b
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/cocoa/autofill/simple_grid_layout.mm
|
| @@ -0,0 +1,379 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/ui/cocoa/autofill/simple_grid_layout.h"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/stl_util.h"
|
| +
|
| +namespace {
|
| + const int kAutoColumnIdStart = 1000000; // Starting ID for autogeneration.
|
| +}
|
| +
|
| +// Encapsulates state for a single NSView in the layout
|
| +class ViewState {
|
| + public:
|
| + ViewState(NSView* view, ColumnSet* column_set, int row, int column);
|
| +
|
| + // Gets the current width of the column associated with this view.
|
| + float GetColumnWidth();
|
| +
|
| + // Get the preferred height for specified width.
|
| + float GetHeightForWidth(float with);
|
| +
|
| + Column* GetColumn() const { return column_set_->GetColumn(column_); }
|
| +
|
| + int row_index() { return row_; }
|
| + NSView* view() { return view_; }
|
| + float preferred_height() { return pref_height_; }
|
| + void set_preferred_height(float height) { pref_height_ = height; }
|
| +
|
| + private:
|
| + NSView* view_;
|
| + ColumnSet* column_set_;
|
| + int row_;
|
| + int column_;
|
| + float pref_height_;
|
| +};
|
| +
|
| +class LayoutElement {
|
| + public:
|
| + LayoutElement(float resize_percent, int fixed_width);
|
| + virtual ~LayoutElement() {}
|
| +
|
| + template <class T>
|
| + static void ResetSizes(ScopedVector<T>* elements) {
|
| + // Reset the layout width of each column.
|
| + for (typename std::vector<T*>::iterator i = elements->begin();
|
| + i != elements->end(); ++i) {
|
| + (*i)->ResetSize();
|
| + }
|
| + }
|
| +
|
| + template <class T>
|
| + static void CalculateLocationsFromSize(ScopedVector<T>* elements) {
|
| + // Reset the layout width of each column.
|
| + int location = 0;
|
| + for (typename std::vector<T*>::iterator i = elements->begin();
|
| + i != elements->end(); ++i) {
|
| + (*i)->SetLocation(location);
|
| + location += (*i)->Size();
|
| + }
|
| + }
|
| +
|
| + float Size() { return size_; }
|
| +
|
| + void ResetSize() {
|
| + size_ = fixed_width_;
|
| + }
|
| +
|
| + void SetSize(float size) {
|
| + size_ = size;
|
| + }
|
| +
|
| + float Location() const {
|
| + return location_;
|
| + }
|
| +
|
| + // Adjusts the size of this LayoutElement to be the max of the current size
|
| + // and the specified size.
|
| + virtual void AdjustSize(float size) {
|
| + size_ = std::max(size_, size);
|
| + }
|
| +
|
| + void SetLocation(float location) {
|
| + location_ = location;
|
| + }
|
| +
|
| + bool IsResizable() {
|
| + return resize_percent_ > 0.0f;
|
| + }
|
| +
|
| + float ResizePercent() {
|
| + return resize_percent_;
|
| + }
|
| +
|
| + private:
|
| + float resize_percent_;
|
| + int fixed_width_;
|
| + float size_;
|
| + float location_;
|
| +};
|
| +
|
| +LayoutElement::LayoutElement(float resize_percent, int fixed_width)
|
| + : resize_percent_(resize_percent),
|
| + fixed_width_(fixed_width),
|
| + size_(0),
|
| + location_(0) {
|
| +}
|
| +
|
| +class Column : public LayoutElement {
|
| + public:
|
| + Column(float resize_percent, int fixed_width, bool is_padding);
|
| +
|
| + bool is_padding() { return is_padding_; }
|
| + private:
|
| + const bool is_padding_;
|
| +};
|
| +
|
| +Column::Column(float resize_percent, int fixed_width, bool is_padding)
|
| + : LayoutElement(resize_percent, fixed_width),
|
| + is_padding_(is_padding) {
|
| +}
|
| +
|
| +class Row : public LayoutElement {
|
| + public:
|
| + Row(float resize_percent, int fixed_height, ColumnSet* column_set);
|
| + ColumnSet* column_set() { return column_set_; }
|
| +
|
| + private:
|
| + ColumnSet* column_set_;
|
| +};
|
| +
|
| +Row::Row(float resize_percent, int fixed_height, ColumnSet* column_set)
|
| + : LayoutElement(resize_percent, fixed_height),
|
| + column_set_(column_set) {
|
| +}
|
| +
|
| +ViewState::ViewState(NSView* view, ColumnSet* column_set, int row, int column)
|
| + : view_(view),
|
| + column_set_(column_set),
|
| + row_(row),
|
| + column_(column) {}
|
| +
|
| +float ViewState::GetColumnWidth() {
|
| + return column_set_->GetColumnWidth(column_);
|
| +}
|
| +
|
| +float ViewState::GetHeightForWidth(float width) {
|
| + // NSView doesn't have any way to make height fit size, get frame height.
|
| + return NSHeight([view_ frame]);
|
| +}
|
| +
|
| +ColumnSet::ColumnSet(int id) : id_(id) {
|
| +}
|
| +
|
| +ColumnSet::~ColumnSet() {
|
| +}
|
| +
|
| +void ColumnSet::AddPaddingColumn(int fixed_width) {
|
| + columns_.push_back(new Column(0.0f, fixed_width, true));
|
| +}
|
| +
|
| +void ColumnSet::AddColumn(float resize_percent) {
|
| + columns_.push_back(new Column(resize_percent, 0, false));
|
| +}
|
| +
|
| +void ColumnSet::CalculateSize(float width) {
|
| + // Reset column widths
|
| + LayoutElement::ResetSizes(&columns_);
|
| + width = CalculateRemainingWidth(width);
|
| + DistributeRemainingWidth(width);
|
| +}
|
| +
|
| +void ColumnSet::ResetColumnXCoordinates() {
|
| + LayoutElement::CalculateLocationsFromSize(&columns_);
|
| +}
|
| +
|
| +float ColumnSet::CalculateRemainingWidth(float width) {
|
| + for (size_t i = 0; i < columns_.size(); ++i)
|
| + width -= columns_[i]->Size();
|
| +
|
| + return width;
|
| +}
|
| +
|
| +void ColumnSet::DistributeRemainingWidth(float width) {
|
| + float total_resize = 0.0f;
|
| + int resizable_columns = 0.0;
|
| +
|
| + for (size_t i = 0; i < columns_.size(); ++i) {
|
| + if (columns_[i]->IsResizable()) {
|
| + total_resize += columns_[i]->ResizePercent();
|
| + resizable_columns++;
|
| + }
|
| + }
|
| +
|
| + float remaining_width = width;
|
| + for (size_t i = 0; i < columns_.size(); ++i) {
|
| + if (columns_[i]->IsResizable()) {
|
| + float delta = (resizable_columns == 0) ? remaining_width :
|
| + (width * columns_[i]->ResizePercent() / total_resize);
|
| + remaining_width -= delta;
|
| + columns_[i]->SetSize(columns_[i]->Size() + delta);
|
| + resizable_columns--;
|
| + }
|
| + }
|
| +}
|
| +
|
| +float ColumnSet::GetColumnWidth(int column_index) {
|
| + if (column_index < 0 || column_index >= num_columns())
|
| + return 0.0;
|
| + return columns_[column_index]->Size();
|
| +}
|
| +
|
| +float ColumnSet::ColumnLocation(int column_index) {
|
| + if (column_index < 0 || column_index >= num_columns())
|
| + return 0.0;
|
| + return columns_[column_index]->Location();
|
| +}
|
| +
|
| +SimpleGridLayout::SimpleGridLayout(NSView* host)
|
| + : next_column_(0),
|
| + current_auto_id_(kAutoColumnIdStart),
|
| + host_(host) {
|
| + [host_ frame];
|
| +}
|
| +
|
| +SimpleGridLayout::~SimpleGridLayout() {
|
| +}
|
| +
|
| +ColumnSet* SimpleGridLayout::AddColumnSet(int id) {
|
| + DCHECK(GetColumnSet(id) == NULL);
|
| + ColumnSet* column_set = new ColumnSet(id);
|
| + column_sets_.push_back(column_set);
|
| + return column_set;
|
| +}
|
| +
|
| +ColumnSet* SimpleGridLayout::GetColumnSet(int id) {
|
| + for (ScopedVector<ColumnSet>::const_iterator i = column_sets_.begin();
|
| + i != column_sets_.end(); ++i) {
|
| + if ((*i)->id() == id) {
|
| + return *i;
|
| + }
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +void SimpleGridLayout::AddPaddingRow(int fixed_height) {
|
| + AddRow(new Row(0.0f, fixed_height, NULL));
|
| +}
|
| +
|
| +void SimpleGridLayout::StartRow(float vertical_resize, int column_set_id) {
|
| + ColumnSet* column_set = GetColumnSet(column_set_id);
|
| + DCHECK(column_set);
|
| + AddRow(new Row(vertical_resize, 0, column_set));
|
| +}
|
| +
|
| +ColumnSet* SimpleGridLayout::AddRow() {
|
| + AddRow(new Row(0, 0, AddColumnSet(current_auto_id_++)));
|
| + return column_sets_.back();
|
| +}
|
| +
|
| +void SimpleGridLayout::SkipColumns(int col_count) {
|
| + DCHECK(col_count > 0);
|
| + next_column_ += col_count;
|
| + ColumnSet* current_row_col_set_ = GetLastValidColumnSet();
|
| + DCHECK(current_row_col_set_ &&
|
| + next_column_ <= current_row_col_set_->num_columns());
|
| + SkipPaddingColumns();
|
| +}
|
| +
|
| +void SimpleGridLayout::AddView(NSView* view) {
|
| + [host_ addSubview:view];
|
| + DCHECK(next_column_ < GetLastValidColumnSet()->num_columns());
|
| + view_states_.push_back(
|
| + new ViewState(view,
|
| + GetLastValidColumnSet(),
|
| + rows_.size() - 1,
|
| + next_column_++));
|
| + SkipPaddingColumns();
|
| +}
|
| +
|
| +// Sizes elements to fit into the superViews bounds, according to constraints.
|
| +void SimpleGridLayout::Layout(NSView* superView) {
|
| + SizeRowsAndColumns(NSWidth([superView bounds]));
|
| + for (std::vector<ViewState*>::iterator i = view_states_.begin();
|
| + i != view_states_.end(); ++i) {
|
| + ViewState* view_state = *i;
|
| + NSView* view = view_state->view();
|
| + NSRect frame = NSMakeRect(view_state->GetColumn()->Location(),
|
| + rows_[view_state->row_index()]->Location(),
|
| + view_state->GetColumn()->Size(),
|
| + rows_[view_state->row_index()]->Size());
|
| + [view setFrame:frame];
|
| + }
|
| +}
|
| +
|
| +void SimpleGridLayout::SizeRowsAndColumns(float width) {
|
| + // Size all columns first.
|
| + for (ScopedVector<ColumnSet>::iterator i = column_sets_.begin();
|
| + i != column_sets_.end(); ++i) {
|
| + (*i)->CalculateSize(width);
|
| + (*i)->ResetColumnXCoordinates();
|
| + }
|
| +
|
| + // Reset the height of each row.
|
| + LayoutElement::ResetSizes(&rows_);
|
| +
|
| + // For each ViewState, obtain the preferred height
|
| + for (std::vector<ViewState*>::iterator i= view_states_.begin();
|
| + i != view_states_.end() ; ++i) {
|
| + ViewState* view_state = *i;
|
| +
|
| + // The view is resizable. As the pref height may vary with the width,
|
| + // ask for the pref again.
|
| + int actual_width = view_state->GetColumnWidth();
|
| +
|
| + // The width this view will get differs from its preferred. Some Views
|
| + // pref height varies with its width; ask for the preferred again.
|
| + view_state->set_preferred_height(
|
| + view_state->GetHeightForWidth(actual_width));
|
| + }
|
| +
|
| +
|
| + // Make sure each row can accommodate all contained ViewStates.
|
| + std::vector<ViewState*>::iterator view_states_iterator = view_states_.begin();
|
| + for (; view_states_iterator != view_states_.end(); ++view_states_iterator) {
|
| + ViewState* view_state = *view_states_iterator;
|
| + Row* row = rows_[view_state->row_index()];
|
| + row->AdjustSize(view_state->preferred_height());
|
| + }
|
| +
|
| + // Update the location of each of the rows.
|
| + LayoutElement::CalculateLocationsFromSize(&rows_);
|
| +}
|
| +
|
| +void SimpleGridLayout::SkipPaddingColumns() {
|
| + ColumnSet* current_row_col_set_ = GetLastValidColumnSet();
|
| + while (next_column_ < current_row_col_set_->num_columns() &&
|
| + current_row_col_set_->GetColumn(next_column_)->is_padding()) {
|
| + next_column_++;
|
| + }
|
| +}
|
| +
|
| +ColumnSet* SimpleGridLayout::GetLastValidColumnSet() {
|
| + for (int i = num_rows() - 1; i >= 0; --i) {
|
| + if (rows_[i]->column_set())
|
| + return rows_[i]->column_set();
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +float SimpleGridLayout::GetRowHeight(int row_index) {
|
| + if (row_index < 0 || row_index >= num_rows())
|
| + return 0.0;
|
| + return rows_[row_index]->Size();
|
| +}
|
| +
|
| +float SimpleGridLayout::GetRowLocation(int row_index) const {
|
| + if (row_index < 0 || row_index >= num_rows())
|
| + return 0.0;
|
| + return rows_[row_index]->Location();
|
| +}
|
| +
|
| +float SimpleGridLayout::GetPreferredHeightForWidth(float width) {
|
| + if (rows_.empty())
|
| + return 0.0f;
|
| +
|
| + SizeRowsAndColumns(width);
|
| + return rows_.back()->Location() + rows_.back()->Size();
|
| +}
|
| +
|
| +void SimpleGridLayout::AddRow(Row* row) {
|
| + next_column_ = 0;
|
| + rows_.push_back(row);
|
| +}
|
| +
|
|
|