summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/tabs/grid.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views/tabs/grid.cc')
-rw-r--r--chrome/browser/views/tabs/grid.cc213
1 files changed, 213 insertions, 0 deletions
diff --git a/chrome/browser/views/tabs/grid.cc b/chrome/browser/views/tabs/grid.cc
new file mode 100644
index 0000000..ef2654c
--- /dev/null
+++ b/chrome/browser/views/tabs/grid.cc
@@ -0,0 +1,213 @@
+// 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<int>(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<gfx::Rect>* 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<float>(cell_width) /
+ static_cast<float>(cell_height);
+ do {
+ col_count++;
+ cell_width =
+ static_cast<float>(max_size_.width() -
+ ((col_count - 1) * kCellXPadding)) /
+ static_cast<float>(col_count);
+ cell_height = static_cast<float>(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<int>(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<int>(
+ static_cast<double>(target - start) * animation_.GetCurrentValue());
+}