Chromium Code Reviews| Index: chrome/browser/ui/cocoa/simple_grid_layout.mm |
| diff --git a/chrome/browser/ui/cocoa/simple_grid_layout.mm b/chrome/browser/ui/cocoa/simple_grid_layout.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..485aa4773f645df12066a7485791333d59e95d66 |
| --- /dev/null |
| +++ b/chrome/browser/ui/cocoa/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/simple_grid_layout.h" |
| + |
| +#include <algorithm> |
| + |
| +#include "base/logging.h" |
| +#include "base/stl_util.h" |
| + |
| +namespace { |
| + const int kAutoColumnIdStart = 1000000; // Starting ID for autogeneration. |
|
Robert Sesek
2013/05/02 20:16:46
nit: no indention
groby-ooo-7-16
2013/05/03 04:31:46
Done.
|
| +} |
| + |
| +// 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_; } |
|
Robert Sesek
2013/05/02 20:16:46
nit: blank line after
groby-ooo-7-16
2013/05/03 04:31:46
Done.
|
| + 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); |
|
Robert Sesek
2013/05/02 20:16:46
nit: blank line after
groby-ooo-7-16
2013/05/03 04:31:46
Done.
|
| + ColumnSet* column_set() { return column_set_; } |
| + |
| + private: |
| + ColumnSet* column_set_; |
| +}; |
| + |
| +Row::Row(float resize_percent, int fixed_height, ColumnSet* column_set) |
|
Robert Sesek
2013/05/02 20:16:46
nit: extra space after ,
groby-ooo-7-16
2013/05/03 04:31:46
Done.
|
| + : 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); |
| +} |
| + |