// Copyright (c) 2009 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/views/tabs/grid.h" #include "base/compiler_specific.h" using views::View; //static const int Grid::kCellXPadding = 15; // static const int Grid::kCellYPadding = 15; Grid::Grid() : ALLOW_THIS_IN_INITIALIZER_LIST(animation_(this)), modifying_children_(false), needs_layout_(false), pref_width_(0), pref_height_(0), cell_width_(0), cell_height_(0), columns_(0), rows_(0) { } void Grid::MoveCell(int old_index, int new_index) { View* cell = GetChildViewAt(old_index); modifying_children_ = true; RemoveChildView(cell); AddChildView(new_index, cell); modifying_children_ = false; CalculateTargetBoundsAndStartAnimation(); } void Grid::InsertCell(int index, View* cell) { modifying_children_ = true; AddChildView(index, cell); modifying_children_ = false; CalculateTargetBoundsAndStartAnimation(); // Set the bounds of the cell to it's target bounds. This way it won't appear // to animate. start_bounds_[index] = target_bounds_[index]; cell->SetBounds(target_bounds_[index]); } void Grid::RemoveCell(int index) { modifying_children_ = true; RemoveChildView(GetChildViewAt(index)); modifying_children_ = false; CalculateTargetBoundsAndStartAnimation(); } void Grid::ViewHierarchyChanged(bool is_add, View* parent, View* child) { if (modifying_children_ || parent != this) return; // Our child views changed without us knowing it. Stop the animation and mark // us as dirty (needs_layout_ = true). animation_.Stop(); needs_layout_ = true; } gfx::Size Grid::GetPreferredSize() { if (needs_layout_) Layout(); return gfx::Size(pref_width_, pref_height_); } void Grid::Layout() { if (!needs_layout_) return; needs_layout_ = false; animation_.Stop(); target_bounds_.clear(); CalculateCellBounds(&target_bounds_); for (size_t i = 0; i < target_bounds_.size(); ++i) GetChildViewAt(i)->SetBounds(target_bounds_[i]); } void Grid::AnimationEnded(const Animation* animation) { SetViewBoundsToTarget(); } void Grid::AnimationProgressed(const Animation* animation) { DCHECK(GetChildViewCount() == static_cast(target_bounds_.size())); for (size_t i = 0; i < target_bounds_.size(); ++i) { View* view = GetChildViewAt(i); gfx::Rect start_bounds = start_bounds_[i]; gfx::Rect target_bounds = target_bounds_[i]; view->SetBounds( gfx::Rect(AnimationPosition(start_bounds.x(), target_bounds.x()), AnimationPosition(start_bounds.y(), target_bounds.y()), AnimationPosition(start_bounds.width(), target_bounds.width()), AnimationPosition(start_bounds.height(), target_bounds.height()))); } SchedulePaint(); } void Grid::AnimationCanceled(const Animation* animation) { // Don't do anything when the animation is canceled. Presumably Layout will // be invoked, and all children will get set to their appropriate position. } void Grid::CalculateCellBounds(std::vector* bounds) { DCHECK(max_size_.width() > 0 && max_size_.height() > 0); int cell_count = GetChildViewCount(); if (cell_count == 0) { pref_width_ = pref_height_ = 0; return; } gfx::Size cell_pref = GetChildViewAt(0)->GetPreferredSize(); int col_count, row_count; // Assume we get the ideal cell size. int cell_width = cell_pref.width(); int cell_height = cell_pref.height(); int max_columns = std::max(1, (max_size_.width() + kCellXPadding) / (cell_width + kCellXPadding)); if (cell_count <= max_columns) { // All the cells fit in a single row. row_count = 1; col_count = cell_count; } else { // Need more than one row to display all. int max_rows = std::max(1, (max_size_.height() + kCellYPadding) / (cell_height + kCellYPadding)); col_count = max_columns; row_count = cell_count / max_columns; if (cell_count % col_count != 0) row_count++; if (cell_count > max_columns * max_rows) { // We don't have enough space for the cells at their ideal size. Keep // adding columns (and shrinking down cell sizes) until we fit // everything. float ratio = static_cast(cell_width) / static_cast(cell_height); do { col_count++; cell_width = static_cast(max_size_.width() - ((col_count - 1) * kCellXPadding)) / static_cast(col_count); cell_height = static_cast(cell_width) / ratio; row_count = std::max(1, (max_size_.height() + kCellYPadding) / (cell_height + kCellYPadding)); } while (row_count * col_count < cell_count); row_count = cell_count / col_count; if (cell_count % col_count != 0) row_count++; } } cell_width_ = cell_width; cell_height_ = cell_height; columns_ = col_count; rows_ = row_count; pref_width_ = std::max(0, col_count * (cell_width + kCellXPadding) - kCellXPadding); pref_height_ = std::max(0, row_count * (cell_height + kCellYPadding) - kCellYPadding); for (int i = 0; i < cell_count; ++i) { int row = i / columns_; int col = i % columns_; bounds->push_back( gfx::Rect(col * cell_width_ + std::max(0, col * kCellXPadding), row * cell_height_ + std::max(0, row * kCellYPadding), cell_width_, cell_height_)); } } void Grid::CalculateTargetBoundsAndStartAnimation() { if (needs_layout_) Layout(); // Determine the current bounds. start_bounds_.clear(); start_bounds_.resize(GetChildViewCount()); for (int i = 0; i < GetChildViewCount(); ++i) start_bounds_[i] = GetChildViewAt(i)->bounds(); // Then the target bounds. target_bounds_.clear(); CalculateCellBounds(&target_bounds_); // And make sure the animation is running. if (!animation_.IsAnimating()) { animation_.Reset(); animation_.Show(); } } void Grid::SetViewBoundsToTarget() { DCHECK(GetChildViewCount() == static_cast(target_bounds_.size())); for (size_t i = 0; i < target_bounds_.size(); ++i) GetChildViewAt(i)->SetBounds(target_bounds_[i]); } int Grid::AnimationPosition(int start, int target) { return start + static_cast( static_cast(target - start) * animation_.GetCurrentValue()); }