summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-10 21:31:32 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-10 21:31:32 +0000
commit9aefecc973faa549046389937bd526f00fb26b5f (patch)
treeb7e6d42698e2366d118d727f87f160c6c4ca1638
parenta3b8c532a83a41f3a21907973b54570989afcde9 (diff)
downloadchromium_src-9aefecc973faa549046389937bd526f00fb26b5f.zip
chromium_src-9aefecc973faa549046389937bd526f00fb26b5f.tar.gz
chromium_src-9aefecc973faa549046389937bd526f00fb26b5f.tar.bz2
Refactors tab overview and gets the rest of the chunks working. I've
refactored it so that there is a clear object responsible for dragging and the controller now controls it all. There is some common code between TabOverviewDragController and DraggedTabController. I will try and share some code between these two later. BUG=none TEST=none Review URL: http://codereview.chromium.org/118513 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18089 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/browser.cc2
-rw-r--r--chrome/browser/gtk/browser_window_gtk.cc6
-rw-r--r--chrome/browser/gtk/browser_window_gtk.h7
-rw-r--r--chrome/browser/views/tabs/grid.cc78
-rw-r--r--chrome/browser/views/tabs/grid.h19
-rw-r--r--chrome/browser/views/tabs/tab_overview_cell.cc3
-rw-r--r--chrome/browser/views/tabs/tab_overview_cell.h10
-rw-r--r--chrome/browser/views/tabs/tab_overview_controller.cc164
-rw-r--r--chrome/browser/views/tabs/tab_overview_controller.h84
-rw-r--r--chrome/browser/views/tabs/tab_overview_drag_controller.cc399
-rw-r--r--chrome/browser/views/tabs/tab_overview_drag_controller.h157
-rw-r--r--chrome/browser/views/tabs/tab_overview_grid.cc195
-rw-r--r--chrome/browser/views/tabs/tab_overview_grid.h102
-rw-r--r--chrome/chrome.gyp14
14 files changed, 926 insertions, 314 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 514fba8..3f6b024 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -1504,7 +1504,7 @@ void Browser::DuplicateContentsAt(int index) {
}
void Browser::CloseFrameAfterDragSession() {
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_LINUX)
// This is scheduled to run after we return to the message loop because
// otherwise the frame will think the drag session is still active and ignore
// the request.
diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc
index 51823f6c1..076e84c 100644
--- a/chrome/browser/gtk/browser_window_gtk.cc
+++ b/chrome/browser/gtk/browser_window_gtk.cc
@@ -283,7 +283,8 @@ std::map<XID, GtkWindow*> BrowserWindowGtk::xid_map_;
// readability.
BrowserWindowGtk::BrowserWindowGtk(Browser* browser)
: browser_(browser),
- full_screen_(false) {
+ full_screen_(false),
+ drag_active_(false) {
use_custom_frame_.Init(prefs::kUseCustomChromeFrame,
browser_->profile()->GetPrefs(), this);
window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
@@ -738,6 +739,9 @@ void BrowserWindowGtk::OnStateChanged(GdkWindowState state) {
}
bool BrowserWindowGtk::CanClose() const {
+ if (drag_active_)
+ return false;
+
// TODO(tc): We don't have tab dragging yet.
// You cannot close a frame for which there is an active originating drag
// session.
diff --git a/chrome/browser/gtk/browser_window_gtk.h b/chrome/browser/gtk/browser_window_gtk.h
index 8cb6a3e..73285f2 100644
--- a/chrome/browser/gtk/browser_window_gtk.h
+++ b/chrome/browser/gtk/browser_window_gtk.h
@@ -119,6 +119,10 @@ class BrowserWindowGtk : public BrowserWindow,
// Add the find bar widget to the window hierarchy.
void AddFindBar(FindBarGtk* findbar);
+ // Sets whether a drag is active. If a drag is active the window will not
+ // close.
+ void set_drag_active(bool drag_active) { drag_active_ = drag_active; }
+
// Returns the BrowserWindowGtk registered with |window|.
static BrowserWindowGtk* GetBrowserWindowForNativeWindow(
gfx::NativeWindow window);
@@ -231,6 +235,9 @@ class BrowserWindowGtk : public BrowserWindow,
// decorations.
BooleanPrefMember use_custom_frame_;
+ // True if a drag is active. See description above setter for details.
+ bool drag_active_;
+
// A map which translates an X Window ID into its respective GtkWindow.
static std::map<XID, GtkWindow*> xid_map_;
diff --git a/chrome/browser/views/tabs/grid.cc b/chrome/browser/views/tabs/grid.cc
index ef2654c..ae17bf9 100644
--- a/chrome/browser/views/tabs/grid.cc
+++ b/chrome/browser/views/tabs/grid.cc
@@ -22,7 +22,8 @@ Grid::Grid()
cell_width_(0),
cell_height_(0),
columns_(0),
- rows_(0) {
+ rows_(0),
+ floating_index_(-1) {
}
void Grid::MoveCell(int old_index, int new_index) {
@@ -44,8 +45,10 @@ void Grid::InsertCell(int index, View* cell) {
// 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]);
+ if (index != floating_index_) {
+ start_bounds_[index] = target_bounds_[index];
+ cell->SetBounds(target_bounds_[index]);
+ }
}
void Grid::RemoveCell(int index) {
@@ -56,6 +59,10 @@ void Grid::RemoveCell(int index) {
CalculateTargetBoundsAndStartAnimation();
}
+void Grid::AnimateToTargetBounds() {
+ CalculateTargetBoundsAndStartAnimation();
+}
+
void Grid::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
if (modifying_children_ || parent != this)
return;
@@ -66,6 +73,14 @@ void Grid::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
needs_layout_ = true;
}
+gfx::Rect Grid::CellBounds(int index) {
+ int row = index / columns_;
+ int col = index % columns_;
+ return gfx::Rect(col * cell_width_ + std::max(0, col * kCellXPadding),
+ row * cell_height_ + std::max(0, row * kCellYPadding),
+ cell_width_, cell_height_);
+}
+
gfx::Size Grid::GetPreferredSize() {
if (needs_layout_)
Layout();
@@ -81,8 +96,29 @@ void Grid::Layout() {
animation_.Stop();
target_bounds_.clear();
CalculateCellBounds(&target_bounds_);
- for (size_t i = 0; i < target_bounds_.size(); ++i)
- GetChildViewAt(i)->SetBounds(target_bounds_[i]);
+ for (size_t i = 0; i < target_bounds_.size(); ++i) {
+ if (static_cast<int>(i) != floating_index_)
+ GetChildViewAt(i)->SetBounds(target_bounds_[i]);
+ }
+}
+
+void Grid::PaintChildren(gfx::Canvas* canvas) {
+ int i, c;
+ for (i = 0, c = GetChildViewCount(); i < c; ++i) {
+ if (i != floating_index_) {
+ View* child = GetChildViewAt(i);
+ if (!child) {
+ NOTREACHED();
+ continue;
+ }
+ child->ProcessPaint(canvas);
+ }
+ }
+
+ // Paint the floating view last. This way it floats on top of all other
+ // views.
+ if (floating_index_ != -1 && floating_index_ < GetChildViewCount())
+ GetChildViewAt(floating_index_)->ProcessPaint(canvas);
}
void Grid::AnimationEnded(const Animation* animation) {
@@ -95,13 +131,15 @@ void Grid::AnimationProgressed(const Animation* animation) {
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())));
+ if (static_cast<int>(i) != floating_index_) {
+ 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();
}
@@ -170,14 +208,8 @@ void Grid::CalculateCellBounds(std::vector<gfx::Rect>* bounds) {
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_));
- }
+ for (int i = 0; i < cell_count; ++i)
+ bounds->push_back(CellBounds(i));
}
void Grid::CalculateTargetBoundsAndStartAnimation() {
@@ -203,8 +235,10 @@ void Grid::CalculateTargetBoundsAndStartAnimation() {
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]);
+ for (size_t i = 0; i < target_bounds_.size(); ++i) {
+ if (static_cast<int>(i) != floating_index_)
+ GetChildViewAt(i)->SetBounds(target_bounds_[i]);
+ }
}
int Grid::AnimationPosition(int start, int target) {
diff --git a/chrome/browser/views/tabs/grid.h b/chrome/browser/views/tabs/grid.h
index 491c1f3..ba86cbd 100644
--- a/chrome/browser/views/tabs/grid.h
+++ b/chrome/browser/views/tabs/grid.h
@@ -38,6 +38,18 @@ class Grid : public views::View, public AnimationDelegate {
// WARNING: this does NOT delete the view, it's up to the caller to do that.
void RemoveCell(int index);
+ // Calculates the target bounds of each cell and starts the animation timer
+ // (assuming it isn't already running). This is invoked for you, but may
+ // be invoked to retrigger animation, perhaps after changing the floating
+ // index.
+ void AnimateToTargetBounds();
+
+ // Sets the index of the floating cell. The floating cells bounds are NOT
+ // updated along with the rest of the cells, and the floating cell is painted
+ // after all other cells. This is typically used during drag and drop when
+ // the user is dragging a cell around.
+ void set_floating_index(int index) { floating_index_ = index; }
+
// Returns the number of columns.
int columns() const { return columns_; }
@@ -50,12 +62,16 @@ class Grid : public views::View, public AnimationDelegate {
// Returns the height of a cell.
int cell_height() const { return cell_height_; }
+ // Returns the bounds of the specified cell.
+ gfx::Rect CellBounds(int index);
+
// View overrides.
virtual void ViewHierarchyChanged(bool is_add,
views::View* parent,
views::View* child);
virtual gfx::Size GetPreferredSize();
virtual void Layout();
+ void PaintChildren(gfx::Canvas* canvas);
// AnimationDelegate overrides.
virtual void AnimationEnded(const Animation* animation);
@@ -108,6 +124,9 @@ class Grid : public views::View, public AnimationDelegate {
int columns_;
int rows_;
+ // See description above setter.
+ int floating_index_;
+
// Used during animation, gives the initial bounds of the views.
std::vector<gfx::Rect> start_bounds_;
diff --git a/chrome/browser/views/tabs/tab_overview_cell.cc b/chrome/browser/views/tabs/tab_overview_cell.cc
index 5d2b47f..c05aae3 100644
--- a/chrome/browser/views/tabs/tab_overview_cell.cc
+++ b/chrome/browser/views/tabs/tab_overview_cell.cc
@@ -80,6 +80,9 @@ bool TabOverviewCell::IsPointInThumbnail(const gfx::Point& point) {
}
gfx::Size TabOverviewCell::GetPreferredSize() {
+ if (!preferred_size_.IsEmpty())
+ return preferred_size_;
+
// Force the preferred width to that of the thumbnail.
gfx::Size thumbnail_pref = thumbnail_view_->GetPreferredSize();
gfx::Size pref = View::GetPreferredSize();
diff --git a/chrome/browser/views/tabs/tab_overview_cell.h b/chrome/browser/views/tabs/tab_overview_cell.h
index b492972..9411cec 100644
--- a/chrome/browser/views/tabs/tab_overview_cell.h
+++ b/chrome/browser/views/tabs/tab_overview_cell.h
@@ -24,6 +24,13 @@ class TabOverviewCell : public views::View {
void SetTitle(const string16& title);
void SetFavIcon(const SkBitmap& favicon);
+ // Sets the preferred size. Normally the preferred size is calculate from
+ // the content, but this can be used to fix it at a particular value. Use an
+ // empty size to get the default preferred size.
+ void set_preferred_size(const gfx::Size& preferred_size) {
+ preferred_size_ = preferred_size;
+ }
+
// Returns true if the specified point, in the bounds of the cell, is over
// the thumbnail.
bool IsPointInThumbnail(const gfx::Point& point);
@@ -36,6 +43,9 @@ class TabOverviewCell : public views::View {
views::ImageView* thumbnail_view_;
views::ImageView* fav_icon_view_;
+ // Specific preferred size. See set_preferred_size() for details.
+ gfx::Size preferred_size_;
+
DISALLOW_COPY_AND_ASSIGN(TabOverviewCell);
};
diff --git a/chrome/browser/views/tabs/tab_overview_controller.cc b/chrome/browser/views/tabs/tab_overview_controller.cc
index 1d35fb3..f85b2eb 100644
--- a/chrome/browser/views/tabs/tab_overview_controller.cc
+++ b/chrome/browser/views/tabs/tab_overview_controller.cc
@@ -4,7 +4,11 @@
#include "chrome/browser/views/tabs/tab_overview_controller.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/gtk/browser_window_gtk.h"
+#include "chrome/browser/views/tabs/tab_overview_cell.h"
#include "chrome/browser/views/tabs/tab_overview_container.h"
+#include "chrome/browser/views/tabs/tab_overview_grid.h"
#include "chrome/browser/window_sizer.h"
#include "views/fill_layout.h"
#include "views/widget/root_view.h"
@@ -15,19 +19,24 @@ static int kInteriorPadding = 20;
TabOverviewController::TabOverviewController(
const gfx::Point& monitor_origin)
- : model_(NULL) {
- view_ = new TabOverviewGrid();
- view_->set_host(this);
+ : host_(NULL),
+ container_(NULL),
+ grid_(NULL),
+ browser_(NULL),
+ drag_browser_(NULL),
+ moved_offscreen_(false),
+ shown_(false) {
+ grid_ = new TabOverviewGrid(this);
// Create the host.
views::WidgetGtk* host = new views::WidgetGtk(views::WidgetGtk::TYPE_POPUP);
host->set_delete_on_destroy(false);
host->MakeTransparent();
host->Init(NULL, gfx::Rect(), true);
- host_.reset(host);
+ host_ = host;
container_ = new TabOverviewContainer();
- container_->AddChildView(view_);
+ container_->AddChildView(grid_);
host->GetRootView()->SetLayoutManager(new views::FillLayout());
host->GetRootView()->AddChildView(container_);
@@ -37,40 +46,161 @@ TabOverviewController::TabOverviewController(
monitor_bounds_ = provider->GetMonitorWorkAreaMatching(
gfx::Rect(monitor_origin.x(), monitor_origin.y(), 1, 1));
int max_width = monitor_bounds_.width() - kMonitorPadding * 2 -
- kInteriorPadding * 2;
+ kInteriorPadding * 2;
int max_height = monitor_bounds_.height() / 2;
container_->SetMaxSize(gfx::Size(max_width, max_height));
}
TabOverviewController::~TabOverviewController() {
+ if (browser_)
+ model()->RemoveObserver(this);
+ host_->Close();
+ // The drag controller may call back to us from it's destructor. Make sure
+ // it's destroyed before us.
+ grid()->CancelDrag();
}
-void TabOverviewController::SetTabStripModel(TabStripModel* tab_strip_model) {
- model_ = tab_strip_model;
- view_->SetTabStripModel(tab_strip_model);
+void TabOverviewController::SetBrowser(Browser* browser) {
+ if (browser_)
+ model()->RemoveObserver(this);
+ browser_ = browser;
+ if (browser_)
+ model()->AddObserver(this);
+ moved_offscreen_ = false;
+ RecreateCells();
+}
+
+TabStripModel* TabOverviewController::model() const {
+ return browser_ ? browser_->tabstrip_model() : NULL;
}
void TabOverviewController::Show() {
if (host_->IsVisible())
return;
- DCHECK(view_->model()); // The model needs to be set before showing.
+ shown_ = true;
+ DCHECK(model()); // The model needs to be set before showing.
host_->Show();
}
-void TabOverviewController::Hide() {
- host_->Hide();
+void TabOverviewController::ConfigureCell(TabOverviewCell* cell,
+ TabContents* contents) {
+ // TODO: need to set thumbnail here.
+ if (contents) {
+ cell->SetTitle(contents->GetTitle());
+ cell->SetFavIcon(contents->GetFavIcon());
+ cell->SchedulePaint();
+ } else {
+ // Need to figure out under what circumstances this is null and deal.
+ NOTIMPLEMENTED();
+ }
+}
+
+void TabOverviewController::DragStarted() {
+ DCHECK(!drag_browser_);
+ drag_browser_ = browser_;
+ static_cast<BrowserWindowGtk*>(browser_->window())->set_drag_active(true);
+}
+
+void TabOverviewController::DragEnded() {
+ static_cast<BrowserWindowGtk*>(drag_browser_->window())->
+ set_drag_active(false);
+ if (drag_browser_->tabstrip_model()->count() == 0)
+ drag_browser_->tabstrip_model()->delegate()->CloseFrameAfterDragSession();
+ drag_browser_ = NULL;
+}
+
+void TabOverviewController::MoveOffscreen() {
+ gfx::Rect bounds;
+ moved_offscreen_ = true;
+ host_->GetBounds(&bounds, true);
+ host_->SetBounds(gfx::Rect(-10000, -10000, bounds.width(), bounds.height()));
+}
+
+void TabOverviewController::SelectTabContents(TabContents* contents) {
+ NOTIMPLEMENTED();
+}
+
+void TabOverviewController::TabInsertedAt(TabContents* contents,
+ int index,
+ bool foreground) {
+ if (!grid_->modifying_model())
+ grid_->CancelDrag();
+
+ TabOverviewCell* child = new TabOverviewCell();
+ ConfigureCell(child, index);
+ grid_->InsertCell(index, child);
+
+ TabCountChanged();
+}
+
+void TabOverviewController::TabClosingAt(TabContents* contents, int index) {
+ // Nothing to do, we only care when the tab is actually detached.
+}
+
+void TabOverviewController::TabDetachedAt(TabContents* contents, int index) {
+ if (!grid_->modifying_model())
+ grid_->CancelDrag();
+
+ scoped_ptr<TabOverviewCell> child(grid_->GetTabOverviewCellAt(index));
+ grid_->RemoveCell(index);
+
+ TabCountChanged();
+}
+
+void TabOverviewController::TabMoved(TabContents* contents,
+ int from_index,
+ int to_index) {
+ if (!grid_->modifying_model())
+ grid_->CancelDrag();
+
+ grid_->MoveCell(from_index, to_index);
+}
+
+void TabOverviewController::TabChangedAt(TabContents* contents, int index,
+ bool loading_only) {
+ ConfigureCell(grid_->GetTabOverviewCellAt(index), index);
+}
+
+void TabOverviewController::TabStripEmpty() {
+ if (!grid_->modifying_model()) {
+ grid_->CancelDrag();
+ // The tab strip is empty, hide the grid.
+ host_->Hide();
+ }
+}
+
+void TabOverviewController::ConfigureCell(TabOverviewCell* cell, int index) {
+ ConfigureCell(cell, model()->GetTabContentsAt(index));
}
-void TabOverviewController::TabOverviewGridPreferredSizeChanged() {
+void TabOverviewController::RecreateCells() {
+ grid_->RemoveAllChildViews(true);
+
+ if (model()) {
+ for (int i = 0; i < model()->count(); ++i) {
+ TabOverviewCell* child = new TabOverviewCell();
+ ConfigureCell(child, i);
+ grid_->AddChildView(child);
+ }
+ }
+ TabCountChanged();
+}
+
+void TabOverviewController::TabCountChanged() {
+ if (moved_offscreen_)
+ return;
+
gfx::Size pref = container_->GetPreferredSize();
int x = monitor_bounds_.x() + (monitor_bounds_.width() - pref.width()) / 2;
int y = monitor_bounds_.y() + monitor_bounds_.height() / 2 - pref.height();
host_->SetBounds(gfx::Rect(x, y, pref.width(), pref.height()));
container_->UpdateWidgetShape(pref.width(), pref.height());
-}
-
-void TabOverviewController::SelectTabContents(TabContents* contents) {
- NOTIMPLEMENTED();
+ if (grid()->GetChildViewCount() > 0) {
+ if (shown_)
+ host_->Show();
+ } else {
+ host_->Hide();
+ }
}
diff --git a/chrome/browser/views/tabs/tab_overview_controller.h b/chrome/browser/views/tabs/tab_overview_controller.h
index 911b5cf..01600e2 100644
--- a/chrome/browser/views/tabs/tab_overview_controller.h
+++ b/chrome/browser/views/tabs/tab_overview_controller.h
@@ -5,41 +5,82 @@
#ifndef CHROME_BROWSER_VIEWS_TABS_TAB_OVERVIEW_CONTROLLER_H_
#define CHROME_BROWSER_VIEWS_TABS_TAB_OVERVIEW_CONTROLLER_H_
+#include "base/gfx/rect.h"
#include "base/scoped_ptr.h"
-#include "chrome/browser/views/tabs/tab_overview_grid.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+class Browser;
+class TabOverviewCell;
class TabOverviewContainer;
-class TabStripModel;
+class TabOverviewGrid;
namespace views {
class Widget;
}
-namespace gfx {
-class Point;
-}
-// TabOverviewController is responsible for showing a TabOverviewGrid.
-class TabOverviewController : public TabOverviewGrid::Host {
+// TabOverviewController is responsible for showing a TabOverviewGrid and
+// keeping it in sync with the TabStripModel of a browser.
+class TabOverviewController : public TabStripModelObserver {
public:
// Creates a TabOverviewController that will be shown on the monitor
// containing |monitor_origin|.
explicit TabOverviewController(const gfx::Point& monitor_origin);
~TabOverviewController();
- // Sets the tarb strip to show.
- void SetTabStripModel(TabStripModel* tab_strip_model);
+ // Sets the browser we're showing the tab strip for.
+ void SetBrowser(Browser* browser);
+ Browser* browser() const { return browser_; }
+ TabOverviewGrid* grid() const { return grid_; }
+ TabStripModel* model() const;
+
+ // Returns true if the grid has been moved off screen. The grid is moved
+ // offscren if the user detaches the last tab in the tab strip.
+ bool moved_offscreen() const { return moved_offscreen_; }
- // Shows/hides the grid.
+ // Shows the grid.
void Show();
- void Hide();
- // TabOverviewGrid::Host overrides.
- virtual void TabOverviewGridPreferredSizeChanged();
+ // Configures a cell from the model.
+ void ConfigureCell(TabOverviewCell* cell, TabContents* contents);
+
+ // Invoked from TabOverviewDragController.
+ virtual void DragStarted();
+ virtual void DragEnded();
+ virtual void MoveOffscreen();
virtual void SelectTabContents(TabContents* contents);
+ // TabStripModelObserver overrides.
+ virtual void TabInsertedAt(TabContents* contents,
+ int index,
+ bool foreground);
+ virtual void TabClosingAt(TabContents* contents, int index);
+ virtual void TabDetachedAt(TabContents* contents, int index);
+ virtual void TabMoved(TabContents* contents,
+ int from_index,
+ int to_index);
+ virtual void TabChangedAt(TabContents* contents, int index,
+ bool loading_only);
+ virtual void TabStripEmpty();
+ // Currently don't care about these as we're not rendering the selection.
+ virtual void TabDeselectedAt(TabContents* contents, int index) { }
+ virtual void TabSelectedAt(TabContents* old_contents,
+ TabContents* new_contents,
+ int index,
+ bool user_gesture) { }
+
private:
+ // Configures a cell from the model.
+ void ConfigureCell(TabOverviewCell* cell, int index);
+
+ // Removes all the cells in the grid and populates it from the model.
+ void RecreateCells();
+
+ // Invoked when the count of the model changes. Notifies the host the pref
+ // size changed.
+ void TabCountChanged();
+
// The widget showing the view.
- scoped_ptr<views::Widget> host_;
+ views::Widget* host_;
// Bounds of the monitor we're being displayed on. This is used to position
// the widget.
@@ -49,10 +90,19 @@ class TabOverviewController : public TabOverviewGrid::Host {
TabOverviewContainer* container_;
// The view. This is owned by host.
- TabOverviewGrid* view_;
+ TabOverviewGrid* grid_;
+
+ // The browser, not owned by us.
+ Browser* browser_;
+
+ // The browser a drag was started on.
+ Browser* drag_browser_;
+
+ // True if the host has been moved offscreen.
+ bool moved_offscreen_;
- // The model, not owned by us.
- TabStripModel* model_;
+ // Has Show been invoked?
+ bool shown_;
DISALLOW_COPY_AND_ASSIGN(TabOverviewController);
};
diff --git a/chrome/browser/views/tabs/tab_overview_drag_controller.cc b/chrome/browser/views/tabs/tab_overview_drag_controller.cc
new file mode 100644
index 0000000..fee8b16
--- /dev/null
+++ b/chrome/browser/views/tabs/tab_overview_drag_controller.cc
@@ -0,0 +1,399 @@
+// 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/tab_overview_drag_controller.h"
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/dock_info.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/views/tabs/tab_overview_cell.h"
+#include "chrome/browser/views/tabs/tab_overview_controller.h"
+#include "chrome/browser/views/tabs/tab_overview_grid.h"
+#include "chrome/common/notification_service.h"
+#include "views/fill_layout.h"
+#include "views/view.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget_gtk.h"
+
+TabOverviewDragController::TabOverviewDragController(
+ TabOverviewController* controller)
+ : controller_(controller),
+ original_model_(controller->model()),
+ current_index_(-1),
+ original_index_(-1),
+ detached_tab_(NULL),
+ original_delegate_(NULL),
+ x_offset_(0),
+ y_offset_(0),
+ dragging_(false),
+ modifying_model_(false),
+ detached_window_(NULL) {
+}
+
+TabOverviewDragController::~TabOverviewDragController() {
+ controller_->DragEnded();
+ if (original_index_ != -1)
+ RevertDrag();
+}
+
+bool TabOverviewDragController::Configure(const gfx::Point& location) {
+ // Find the cell the user clicked on.
+ TabOverviewCell* cell = NULL;
+ int index = -1;
+ for (int i = 0; i < grid()->GetChildViewCount(); ++i) {
+ views::View* child = grid()->GetChildViewAt(i);
+ if (child->bounds().Contains(location)) {
+ index = i;
+ cell = static_cast<TabOverviewCell*>(child);
+ break;
+ }
+ }
+ if (!cell)
+ return false; // User didn't click on a cell.
+
+ // Only start a drag if the user clicks on the thumbnail.
+ gfx::Point cell_point(location);
+ grid()->ConvertPointToView(grid(), cell, &cell_point);
+ if (!cell->IsPointInThumbnail(cell_point))
+ return false;
+
+ current_index_ = original_index_ = index;
+ origin_ = location;
+ x_offset_ = location.x() - cell->bounds().x();
+ y_offset_ = location.y() - cell->bounds().y();
+ return true;
+}
+
+void TabOverviewDragController::Drag(const gfx::Point& location) {
+ if (original_index_ == -1)
+ return;
+
+ if (!dragging_ &&
+ views::View::ExceededDragThreshold(location.x() - origin_.x(),
+ location.y() - origin_.y())) {
+ // Start dragging.
+ dragging_ = true;
+ controller_->DragStarted();
+ grid()->set_floating_index(current_index_);
+ }
+ if (dragging_)
+ DragCell(location);
+}
+
+void TabOverviewDragController::CommitDrag(const gfx::Point& location) {
+ if (original_index_ == -1)
+ return;
+
+ Drag(location);
+ if (detached_tab_) {
+ DropTab(location);
+ } else if (!dragging_ ) {
+ // We haven't started dragging. Tell the controller to select the tab.
+ controller_->SelectTabContents(model()->GetTabContentsAt(original_index_));
+ } else {
+ // The tab is already in position, nothing to do but animate the change.
+ grid()->set_floating_index(-1);
+ grid()->AnimateToTargetBounds();
+ }
+
+ // Set the index to -1 so we know not to do any cleanup.
+ original_index_ = -1;
+}
+
+void TabOverviewDragController::RevertDrag() {
+ if (original_index_ == -1)
+ return;
+
+ modifying_model_ = true;
+ if (detached_tab_) {
+ // Tab is currently detached, add it back to the original tab strip.
+ original_model_->InsertTabContentsAt(original_index_,
+ detached_tab_, true, false);
+ SetDetachedContents(NULL);
+ detached_window_->Close();
+ detached_window_ = NULL;
+ } else if (original_model_ != model()) {
+ // The tab was added to a different tab strip. Move it back to the
+ // original.
+ TabContents* contents = model()->DetachTabContentsAt(current_index_);
+ original_model_->InsertTabContentsAt(original_index_, contents, true,
+ false);
+ } else if (current_index_ != original_index_) {
+ original_model_->MoveTabContentsAt(current_index_, original_index_, true);
+ }
+ modifying_model_ = false;
+
+ // Set the index to -1 so we know not to do any cleanup.
+ original_index_ = -1;
+}
+
+TabOverviewGrid* TabOverviewDragController::grid() const {
+ return controller_->grid();
+}
+
+TabStripModel* TabOverviewDragController::model() const {
+ return controller_->model();
+}
+
+void TabOverviewDragController::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED);
+ DCHECK(Source<TabContents>(source).ptr() == detached_tab_);
+ RevertDrag();
+}
+
+void TabOverviewDragController::OpenURLFromTab(
+ TabContents* source,
+ const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ if (original_delegate_) {
+ if (disposition == CURRENT_TAB)
+ disposition = NEW_WINDOW;
+
+ original_delegate_->OpenURLFromTab(source, url, referrer,
+ disposition, transition);
+ }
+}
+
+void TabOverviewDragController::NavigationStateChanged(
+ const TabContents* source,
+ unsigned changed_flags) {
+}
+
+void TabOverviewDragController::AddNewContents(
+ TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {
+ DCHECK(disposition != CURRENT_TAB);
+
+ // Theoretically could be called while dragging if the page tries to
+ // spawn a window. Route this message back to the browser in most cases.
+ if (original_delegate_) {
+ original_delegate_->AddNewContents(source, new_contents, disposition,
+ initial_pos, user_gesture);
+ }
+}
+
+void TabOverviewDragController::ActivateContents(TabContents* contents) {
+ // Ignored.
+}
+
+void TabOverviewDragController::LoadingStateChanged(TabContents* source) {
+}
+
+void TabOverviewDragController::CloseContents(TabContents* source) {
+ // Theoretically could be called by a window. Should be ignored
+ // because window.close() is ignored (usually, even though this
+ // method gets called.)
+}
+
+void TabOverviewDragController::MoveContents(TabContents* source,
+ const gfx::Rect& pos) {
+ // Theoretically could be called by a web page trying to move its
+ // own window. Should be ignored since we're moving the window...
+}
+
+bool TabOverviewDragController::IsPopup(TabContents* source) {
+ return false;
+}
+
+void TabOverviewDragController::ToolbarSizeChanged(TabContents* source,
+ bool finished) {
+ // Dragged tabs don't care about this.
+}
+
+void TabOverviewDragController::URLStarredChanged(TabContents* source,
+ bool starred) {
+ // Ignored.
+}
+
+void TabOverviewDragController::UpdateTargetURL(TabContents* source,
+ const GURL& url) {
+ // Ignored.
+}
+
+void TabOverviewDragController::DragCell(const gfx::Point& location) {
+ if (controller_->moved_offscreen()) {
+ MoveDetachedWindow(location);
+ return;
+ }
+
+ int col = (location.x() - x_offset_ + grid()->cell_width() / 2) /
+ (grid()->cell_width() + TabOverviewGrid::kCellXPadding);
+ int row = (location.y() - y_offset_ + grid()->cell_height() / 2) /
+ (grid()->cell_height() + TabOverviewGrid::kCellYPadding);
+ gfx::Rect local_bounds = grid()->GetLocalBounds(true);
+ if (!local_bounds.Contains(location)) {
+ // Local bounds doesn't contain the point, allow dragging to the left/right
+ // of us.
+ views::RootView* root = grid()->GetRootView();
+ gfx::Rect allowed_bounds = local_bounds;
+ gfx::Point root_offset;
+ views::View::ConvertPointToView(grid(), root, &root_offset);
+ allowed_bounds.Offset(-root_offset.x(), 0);
+ allowed_bounds.set_width(root->width());
+ if (!allowed_bounds.Contains(location)) {
+ // The user dragged outside the grid.
+ if (detached_tab_) {
+ // We've already created the detached window, move it.
+ MoveDetachedWindow(location);
+ } else {
+ // Detach the cell.
+ Detach(location);
+ }
+ return;
+ }
+ if (location.x() < 0) {
+ col = 0;
+ } else if (location.x() >= grid()->width()) {
+ col = grid()->columns();
+ } else {
+ col = (location.x() + grid()->cell_width() / 2) /
+ (grid()->cell_width() + TabOverviewGrid::kCellXPadding);
+ }
+ }
+ int new_index = std::min(model()->count() - 1,
+ row * grid()->columns() + col);
+ if (detached_tab_ ) {
+ // The user dragged a detached tab back over the grid, reattach it.
+ Attach(new_index);
+ } else if (new_index != current_index_) {
+ grid()->set_floating_index(new_index);
+ modifying_model_ = true;
+ model()->MoveTabContentsAt(current_index_, new_index, false);
+ modifying_model_ = false;
+ current_index_ = new_index;
+ }
+ gfx::Rect target_bounds = grid()->CellBounds(current_index_);
+ target_bounds.Offset(location.x() - target_bounds.x() - x_offset_,
+ location.y() - target_bounds.y() - y_offset_);
+ target_bounds.set_y(row * (grid()->cell_height() +
+ TabOverviewGrid::kCellYPadding));
+ target_bounds = target_bounds.AdjustToFit(grid()->GetLocalBounds(true));
+ views::View* cell = grid()->GetChildViewAt(new_index);
+ gfx::Rect cell_bounds = cell->bounds();
+ if (target_bounds.origin() != cell_bounds.origin()) {
+ grid()->SchedulePaint(cell_bounds, false);
+ grid()->SchedulePaint(target_bounds, false);
+ cell->SetBounds(target_bounds);
+ }
+}
+
+void TabOverviewDragController::Attach(int index) {
+ DCHECK(detached_tab_);
+ DCHECK(model());
+ current_index_ = index;
+ modifying_model_ = true;
+ model()->InsertTabContentsAt(index, detached_tab_, true, false);
+ modifying_model_ = false;
+ grid()->set_floating_index(index);
+ SetDetachedContents(NULL);
+
+ detached_window_->Close();
+ detached_window_ = NULL;
+}
+
+void TabOverviewDragController::Detach(const gfx::Point& location) {
+ if (detached_tab_) {
+ // Already detached.
+ return;
+ }
+
+ detached_window_ = CreateDetachedWindow(
+ location, model()->GetTabContentsAt(current_index_));
+ detached_window_->Show();
+
+ grid()->set_floating_index(-1);
+ SetDetachedContents(model()->GetTabContentsAt(current_index_));
+ if (model()->count() == 1) {
+ // The model is going to be empty. Tell the host to move us offscreen.
+ // NOTE: it would be nice to hide and destroy the window here but this
+ // causes two problems: we'll stop getting events, and we don't want
+ // to empty out the tabstrip as otherwise they may trigger Chrome to
+ // exit.
+ controller_->MoveOffscreen();
+ }
+ modifying_model_ = true;
+ model()->DetachTabContentsAt(current_index_);
+ modifying_model_ = false;
+}
+
+void TabOverviewDragController::DropTab(const gfx::Point& location) {
+ TabContents* contents = detached_tab_;
+ SetDetachedContents(NULL);
+
+ gfx::Rect browser_rect = controller_->browser()->window()->GetNormalBounds();
+ gfx::Point screen_loc(location);
+ grid()->ConvertPointToScreen(grid(), &screen_loc);
+ gfx::Rect window_bounds(
+ screen_loc, gfx::Size(browser_rect.width(), browser_rect.height()));
+ Browser* new_browser = model()->delegate()->CreateNewStripWithContents(
+ contents, window_bounds, DockInfo());
+ new_browser->window()->Show();
+
+ detached_window_->Close();
+ detached_window_ = NULL;
+}
+
+void TabOverviewDragController::MoveDetachedWindow(
+ const gfx::Point& location) {
+ gfx::Point screen_loc = location;
+ screen_loc.Offset(-x_offset_, -y_offset_);
+ grid()->ConvertPointToScreen(grid(), &screen_loc);
+ detached_window_->SetBounds(
+ gfx::Rect(screen_loc,
+ detached_window_->GetRootView()->GetPreferredSize()));
+}
+
+views::Widget* TabOverviewDragController::CreateDetachedWindow(
+ const gfx::Point& location,
+ TabContents* tab_contents) {
+ // TODO: wrap the cell in another view that provides a background.
+ views::WidgetGtk* widget =
+ new views::WidgetGtk(views::WidgetGtk::TYPE_POPUP);
+ widget->MakeTransparent();
+ gfx::Point screen_loc = location;
+ screen_loc.Offset(-x_offset_, -y_offset_);
+ grid()->ConvertPointToScreen(grid(), &screen_loc);
+ TabOverviewCell* cell = new TabOverviewCell();
+ cell->set_preferred_size(
+ gfx::Size(grid()->cell_width(), grid()->cell_height()));
+ controller_->ConfigureCell(cell, tab_contents);
+ widget->Init(NULL, gfx::Rect(screen_loc, cell->GetPreferredSize()), true);
+ widget->GetRootView()->SetLayoutManager(new views::FillLayout());
+ widget->GetRootView()->AddChildView(cell);
+ return widget;
+}
+
+void TabOverviewDragController::SetDetachedContents(TabContents* tab) {
+ if (detached_tab_) {
+ registrar_.Remove(this,
+ NotificationType::TAB_CONTENTS_DESTROYED,
+ Source<TabContents>(detached_tab_));
+ if (detached_tab_->delegate() == this)
+ detached_tab_->set_delegate(original_delegate_);
+ else
+ DLOG(WARNING) << " delegate changed";
+ }
+ original_delegate_ = NULL;
+ detached_tab_ = tab;
+ if (tab) {
+ registrar_.Add(this,
+ NotificationType::TAB_CONTENTS_DESTROYED,
+ Source<TabContents>(tab));
+
+ // We need to be the delegate so we receive messages about stuff,
+ // otherwise our dragged contents may be replaced and subsequently
+ // collected/destroyed while the drag is in process, leading to
+ // nasty crashes.
+ original_delegate_ = tab->delegate();
+ tab->set_delegate(this);
+ }
+}
diff --git a/chrome/browser/views/tabs/tab_overview_drag_controller.h b/chrome/browser/views/tabs/tab_overview_drag_controller.h
new file mode 100644
index 0000000..7bf5298
--- /dev/null
+++ b/chrome/browser/views/tabs/tab_overview_drag_controller.h
@@ -0,0 +1,157 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_TABS_TAB_OVERVIEW_DRAG_CONTROLLER_H_
+#define CHROME_BROWSER_VIEWS_TABS_TAB_OVERVIEW_DRAG_CONTROLLER_H_
+
+#include "base/gfx/point.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+#include "chrome/common/notification_registrar.h"
+
+class TabOverviewController;
+class TabOverviewGrid;
+class TabStripModel;
+
+namespace views {
+class Widget;
+}
+
+// TabOverviewDragController handles dragging cells in a TabOverviewGrid.
+// There are a couple of interesting states TabOverviewDragController may
+// be in:
+// . original_index_ == -1: indicates the drag wasn't valid (not over a cell),
+// or the drag is done (either committed or reverted).
+// . detached_tab_ != NULL: the user has dragged a tab outside the grid such
+// that a windows was created with the contents of
+// the tab.
+// . detached_tab_ == NULL: the user is dragging a cell around within the grid.
+//
+// TabOverviewGrid invokes Configure to prepare the controller. If this returns
+// true, then Drag is repeatedly invoked as the user drags the mouse around.
+// Finally CommitDrag is invoked if the user releases the mouse, or RevertDrag
+// if the drag is canceled some how.
+//
+// NOTE: all coordinates passed in are relative to the TabOverviewGrid.
+class TabOverviewDragController : public TabContentsDelegate,
+ public NotificationObserver {
+ public:
+ explicit TabOverviewDragController(TabOverviewController* controller);
+ ~TabOverviewDragController();
+
+ // Invoked to prepare the TabOverviewDragController for a drag. Returns true
+ // if over a cell, false if the mouse isn't over a valid location.
+ bool Configure(const gfx::Point& location);
+
+ // Invoked as the user drags the mouse.
+ void Drag(const gfx::Point& location);
+
+ // Invoked to commit the drag, typically when the user pressed enter.
+ void CommitDrag(const gfx::Point& location);
+
+ // Invoked to rever the drag.
+ void RevertDrag();
+
+ bool modifying_model() const { return modifying_model_; }
+ TabOverviewGrid* grid() const;
+ TabStripModel* model() const;
+
+ // Overridden from NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Overridden from TabContentsDelegate:
+ virtual void OpenURLFromTab(TabContents* source,
+ const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition);
+ virtual void NavigationStateChanged(const TabContents* source,
+ unsigned changed_flags);
+ virtual void AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture);
+ virtual void ActivateContents(TabContents* contents);
+ virtual void LoadingStateChanged(TabContents* source);
+ virtual void CloseContents(TabContents* source);
+ virtual void MoveContents(TabContents* source, const gfx::Rect& pos);
+ virtual bool IsPopup(TabContents* source);
+ virtual void ToolbarSizeChanged(TabContents* source, bool is_animating);
+ virtual void URLStarredChanged(TabContents* source, bool starred);
+ virtual void UpdateTargetURL(TabContents* source, const GURL& url);
+
+ private:
+ // Invoked from Drag if the mouse has moved enough to trigger dragging.
+ void DragCell(const gfx::Point& location);
+
+ // Reattaches the detatched tab. |index| is the index into |model()| as to
+ // where the tab should be attached.
+ void Attach(int index);
+
+ // Detaches the tab at |current_index_|.
+ void Detach(const gfx::Point& location);
+
+ // Drops the detached tab. This is invoked from |CommitDrag|.
+ void DropTab(const gfx::Point& location);
+
+ // Moves the detached window.
+ void MoveDetachedWindow(const gfx::Point& location);
+
+ // Creates and returns the detached window.
+ views::Widget* CreateDetachedWindow(const gfx::Point& location,
+ TabContents* contents);
+
+ // Sets the detaches contents, installed/uninstalling listeners.
+ void SetDetachedContents(TabContents* tab);
+
+ TabOverviewController* controller_;
+
+ // The model the drag started from. This needs to be cached as the grid may
+ // end up showing a different model if the user drags over another window.
+ TabStripModel* original_model_;
+
+ // The index the tab has been dragged to. This is initially the index the
+ // user pressed the mouse at, but changes as the user drags the tab around.
+ int current_index_;
+
+ // The original index the tab was at. If -1 it means the drag is invalid or
+ // done. See description above class for more details.
+ int original_index_;
+
+ // The tab being dragged. This is only non-NULL if the tab has been detached.
+ TabContents* detached_tab_;
+
+ // If detached_tab_ is non-null, this is it's delegate before we set
+ // ourselves as the delegate.
+ TabContentsDelegate* original_delegate_;
+
+ // The origin of the click.
+ gfx::Point origin_;
+
+ // Offset of the initial mouse location relative to the cell at
+ // original_index_.
+ int x_offset_;
+ int y_offset_;
+
+ // Has the user started dragging?
+ bool dragging_;
+
+ // If true, we're modifying the model. This is used to avoid cancelling the
+ // drag when the model changes.
+ bool modifying_model_;
+
+ // Handles registering for notifications.
+ NotificationRegistrar registrar_;
+
+ // Once a tab is detached a window is created containing a cell and moved
+ // around; this is that window.
+ views::Widget* detached_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabOverviewDragController);
+};
+
+#endif // CHROME_BROWSER_VIEWS_TABS_TAB_OVERVIEW_DRAG_CONTROLLER_H_
diff --git a/chrome/browser/views/tabs/tab_overview_grid.cc b/chrome/browser/views/tabs/tab_overview_grid.cc
index e04f1a7..e14da4a 100644
--- a/chrome/browser/views/tabs/tab_overview_grid.cc
+++ b/chrome/browser/views/tabs/tab_overview_grid.cc
@@ -4,197 +4,60 @@
#include "chrome/browser/views/tabs/tab_overview_grid.h"
-#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/views/tabs/tab_overview_cell.h"
-#include "views/widget/root_view.h"
+#include "chrome/browser/views/tabs/tab_overview_controller.h"
+#include "chrome/browser/views/tabs/tab_overview_drag_controller.h"
-TabOverviewGrid::TabOverviewGrid()
- : model_(NULL),
- host_(NULL) {
+TabOverviewGrid::TabOverviewGrid(TabOverviewController* controller)
+ : controller_(controller) {
}
TabOverviewGrid::~TabOverviewGrid() {
- RemoveListeners();
- model_ = NULL;
}
-void TabOverviewGrid::SetTabStripModel(TabStripModel* tab_strip_model) {
- if (tab_strip_model == model_)
- return;
-
- RemoveListeners();
- model_ = tab_strip_model;
- AddListeners();
- Recreate();
-}
-
-void TabOverviewGrid::TabInsertedAt(TabContents* contents,
- int index,
- bool foreground) {
- drag_info_ = DragInfo();
-
- TabOverviewCell* child = new TabOverviewCell();
- ConfigureChild(child, index);
- InsertCell(index, child);
-
- TabCountChanged();
-}
-
-void TabOverviewGrid::TabClosingAt(TabContents* contents, int index) {
- // Nothing to do, we only care when the tab is actually detached.
-}
-
-void TabOverviewGrid::TabDetachedAt(TabContents* contents, int index) {
- drag_info_ = DragInfo();
-
- scoped_ptr<TabOverviewCell> child(GetTabOverviewCellAt(index));
- RemoveCell(index);
-
- TabCountChanged();
+bool TabOverviewGrid::modifying_model() const {
+ return drag_controller_.get() && drag_controller_->modifying_model();
}
-void TabOverviewGrid::TabMoved(TabContents* contents,
- int from_index,
- int to_index) {
- if (!drag_info_.moving_tab)
- drag_info_ = DragInfo();
-
- MoveCell(from_index, to_index);
-}
-
-void TabOverviewGrid::TabChangedAt(TabContents* contents, int index,
- bool loading_only) {
- ConfigureChild(GetTabOverviewCellAt(index), index);
+TabOverviewCell* TabOverviewGrid::GetTabOverviewCellAt(int index) {
+ return static_cast<TabOverviewCell*>(GetChildViewAt(index));
}
-void TabOverviewGrid::TabStripEmpty() {
- SetTabStripModel(NULL);
+void TabOverviewGrid::CancelDrag() {
+ drag_controller_.reset(NULL);
}
bool TabOverviewGrid::OnMousePressed(const views::MouseEvent& event) {
- drag_info_ = DragInfo();
- TabOverviewCell* cell = NULL;
- for (int i = 0; i < GetChildViewCount(); ++i) {
- View* child = GetChildViewAt(i);
- if (child->bounds().Contains(event.location())) {
- drag_info_.index = i;
- cell = static_cast<TabOverviewCell*>(child);
- break;
- }
- }
- if (!cell)
- return false;
- gfx::Point cell_point(event.location());
- ConvertPointToView(this, cell, &cell_point);
- if (!cell->IsPointInThumbnail(cell_point))
+ if (drag_controller_.get())
+ return true;
+
+ if (!event.IsLeftMouseButton())
return false;
- drag_info_.origin = event.location();
+ drag_controller_.reset(new TabOverviewDragController(controller_));
+ if (!drag_controller_->Configure(event.location())) {
+ drag_controller_.reset(NULL);
+ return false;
+ }
return true;
}
bool TabOverviewGrid::OnMouseDragged(const views::MouseEvent& event) {
- if (drag_info_.index >= 0 && !drag_info_.dragging &&
- ExceededDragThreshold(event.location().x() - drag_info_.origin.x(),
- event.location().y() - drag_info_.origin.y())) {
- // Start dragging.
- drag_info_.dragging = true;
- }
- if (drag_info_.dragging)
- UpdateDrag(event.location());
+ if (!drag_controller_.get())
+ return false;
+
+ drag_controller_->Drag(event.location());
return true;
}
void TabOverviewGrid::OnMouseReleased(const views::MouseEvent& event,
bool canceled) {
- if (host_ && !drag_info_.dragging && drag_info_.index != -1)
- host_->SelectTabContents(model_->GetTabContentsAt(drag_info_.index));
-}
-
-void TabOverviewGrid::AddListeners() {
- if (model_)
- model_->AddObserver(this);
-}
-
-void TabOverviewGrid::RemoveListeners() {
- if (model_)
- model_->RemoveObserver(this);
-}
-
-void TabOverviewGrid::Recreate() {
- RemoveAllChildViews(true);
-
- if (model_) {
- for (int i = 0; i < model_->count(); ++i) {
- TabOverviewCell* child = new TabOverviewCell();
- ConfigureChild(child, i);
- AddChildView(child);
- }
- }
- TabCountChanged();
-}
-
-TabOverviewCell* TabOverviewGrid::GetTabOverviewCellAt(int index) {
- return static_cast<TabOverviewCell*>(GetChildViewAt(index));
-}
-
-void TabOverviewGrid::ConfigureChild(TabOverviewCell* child, int index) {
- // TODO: need to set thumbnail here.
- TabContents* tab_contents = model_->GetTabContentsAt(index);
- if (tab_contents) {
- child->SetTitle(tab_contents->GetTitle());
- child->SetFavIcon(tab_contents->GetFavIcon());
- child->SchedulePaint();
- } else {
- // Need to figure out under what circumstances this is null and deal.
- NOTIMPLEMENTED();
- }
-}
-
-void TabOverviewGrid::TabCountChanged() {
- if (host_)
- host_->TabOverviewGridPreferredSizeChanged();
-}
-
-void TabOverviewGrid::UpdateDrag(const gfx::Point& location) {
- int row = 0;
- int col = 0;
- gfx::Rect local_bounds = GetLocalBounds(true);
- if (!local_bounds.Contains(location)) {
- // Local bounds doesn't contain the point, allow dragging to the left/right
- // of us to equal to before/after last cell.
- views::RootView* root = GetRootView();
- gfx::Point root_point = location;
- views::View::ConvertPointToView(this, root, &root_point);
- gfx::Rect root_bounds = root->GetLocalBounds(true);
- if (!root->bounds().Contains(root_point)) {
- // The user dragged outside the grid, remove the cell.
- return;
- }
- if (location.x() < 0)
- col = 0;
- else if (location.x() >= width())
- col = columns();
- else
- col = (location.x() + cell_width() / 2) / (cell_width() + kCellXPadding);
- if (location.y() < 0)
- row = 0;
- else if (location.y() >= height())
- row = rows();
- else
- row = location.y() / (cell_height() + kCellYPadding);
- } else {
- // We contain the point in our bounds.
- col = (location.x() + cell_width() / 2) / (cell_width() + kCellXPadding);
- row = location.y() / (cell_height() + kCellYPadding);
- }
- int new_index = std::min(model_->count() - 1, row * columns() + col);
- if (new_index == drag_info_.index)
+ if (!drag_controller_.get())
return;
- drag_info_.moving_tab = true;
- model_->MoveTabContentsAt(drag_info_.index, new_index, false);
- drag_info_.moving_tab = false;
- drag_info_.index = new_index;
- // TODO: need to handle dragging outside of window.
+ if (canceled)
+ drag_controller_->RevertDrag();
+ else
+ drag_controller_->CommitDrag(event.location());
+ drag_controller_.reset(NULL);
}
diff --git a/chrome/browser/views/tabs/tab_overview_grid.h b/chrome/browser/views/tabs/tab_overview_grid.h
index a7a8ec6..bedf4ce 100644
--- a/chrome/browser/views/tabs/tab_overview_grid.h
+++ b/chrome/browser/views/tabs/tab_overview_grid.h
@@ -5,59 +5,30 @@
#ifndef CHROME_BROWSER_VIEWS_TABS_TAB_OVERVIEW_GRID_H_
#define CHROME_BROWSER_VIEWS_TABS_TAB_OVERVIEW_GRID_H_
-#include "chrome/browser/tabs/tab_strip_model.h"
+#include "base/scoped_ptr.h"
#include "chrome/browser/views/tabs/grid.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "views/view.h"
class TabOverviewCell;
+class TabOverviewController;
+class TabOverviewDragController;
// TabOverviewGrid is used to provide a grid view of the contents of a tab
-// strip model. Each cell of the grid is a TabOverviewCell.
-class TabOverviewGrid : public Grid, public TabStripModelObserver {
+// strip model. Each cell of the grid is a TabOverviewCell. TabOverviewGrids
+// primary responsibility is to forward events to TabOverviewDragController.
+class TabOverviewGrid : public Grid {
public:
-
- class Host {
- public:
- // Invoked when the preferred size of the TabOverviewGrid changes. The
- // preferred size changes any the contents of the tab strip changes.
- virtual void TabOverviewGridPreferredSizeChanged() = 0;
-
- // Invoked when the user selects a cell in the grid.
- virtual void SelectTabContents(TabContents* contents) = 0;
-
- protected:
- ~Host() {}
- };
-
- TabOverviewGrid();
+ explicit TabOverviewGrid(TabOverviewController* controller);
virtual ~TabOverviewGrid();
- // Sets the host.
- void set_host(Host* host) { host_ = host; }
+ // Returns true if a drag is underway and the drag is in the process of
+ // modifying the tab strip model.
+ bool modifying_model() const;
- // The contents of the TabOverviewGrid are driven by that of the model.
- void SetTabStripModel(TabStripModel* tab_strip_model);
- TabStripModel* model() const { return model_; }
+ // Returns the TabOverviewCell at the specified index.
+ TabOverviewCell* GetTabOverviewCellAt(int index);
- // TabStripModelObserver overrides.
- virtual void TabInsertedAt(TabContents* contents,
- int index,
- bool foreground);
- virtual void TabClosingAt(TabContents* contents, int index);
- virtual void TabDetachedAt(TabContents* contents, int index);
- virtual void TabMoved(TabContents* contents,
- int from_index,
- int to_index);
- virtual void TabChangedAt(TabContents* contents, int index,
- bool loading_only);
- virtual void TabStripEmpty();
- // Currently don't care about these as we're not rendering the selection.
- virtual void TabDeselectedAt(TabContents* contents, int index) { }
- virtual void TabSelectedAt(TabContents* old_contents,
- TabContents* new_contents,
- int index,
- bool user_gesture) { }
+ // Cancels the drag. Does nothing if a drag is not underway.
+ void CancelDrag();
// View overrides.
virtual bool OnMousePressed(const views::MouseEvent& event);
@@ -65,50 +36,9 @@ class TabOverviewGrid : public Grid, public TabStripModelObserver {
virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled);
private:
- // DragInfo is used when the user presses the mouse on the grid. It indicates
- // where the press occurred and whether the user is dragging the mouse.
- struct DragInfo {
- DragInfo() : index(-1), dragging(false) {}
-
- // The index the user pressed that mouse at. If -1, the user didn't press
- // on a valid location.
- int index;
-
- // Has the user started dragging?
- bool dragging;
-
- // The origin of the click.
- gfx::Point origin;
-
- // If true, we're moving the tab in the model. This is used to avoid
- // resetting DragInfo when the model changes.
- bool moving_tab;
- };
-
- void AddListeners();
- void RemoveListeners();
-
- // Recreates the contents of the grid from that of the model.
- void Recreate();
-
- // Returns the TabOverviewCell at the specified index.
- TabOverviewCell* GetTabOverviewCellAt(int index);
-
- // Configures a cell from the model.
- void ConfigureChild(TabOverviewCell* child, int index);
-
- // Invoked when the count of the model changes. Notifies the host the pref
- // size changed.
- void TabCountChanged();
-
- // Updates |drag_info_| based on |location|.
- void UpdateDrag(const gfx::Point& location);
-
- TabStripModel* model_;
-
- Host* host_;
+ TabOverviewController* controller_;
- DragInfo drag_info_;
+ scoped_ptr<TabOverviewDragController> drag_controller_;
DISALLOW_COPY_AND_ASSIGN(TabOverviewGrid);
};
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index fd0a17c..a6953cd 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1575,10 +1575,12 @@
'browser/views/tabs/tab_overview_cell.h',
'browser/views/tabs/tab_overview_container.cc',
'browser/views/tabs/tab_overview_container.h',
- 'browser/views/tabs/tab_overview_grid.cc',
- 'browser/views/tabs/tab_overview_grid.h',
'browser/views/tabs/tab_overview_controller.cc',
'browser/views/tabs/tab_overview_controller.h',
+ 'browser/views/tabs/tab_overview_drag_controller.cc',
+ 'browser/views/tabs/tab_overview_drag_controller.h',
+ 'browser/views/tabs/tab_overview_grid.cc',
+ 'browser/views/tabs/tab_overview_grid.h',
'browser/views/tabs/tab_renderer.cc',
'browser/views/tabs/tab_renderer.h',
'browser/views/tabs/tab_strip.cc',
@@ -1744,12 +1746,14 @@
'browser/password_manager/password_store_kwallet.cc',
'browser/views/tabs/grid.cc',
'browser/views/tabs/grid.h',
- 'browser/views/tabs/tab_overview_child.cc',
- 'browser/views/tabs/tab_overview_child.h',
+ 'browser/views/tabs/tab_overview_cell.cc',
+ 'browser/views/tabs/tab_overview_cell.h',
'browser/views/tabs/tab_overview_container.cc',
'browser/views/tabs/tab_overview_container.h',
'browser/views/tabs/tab_overview_controller.cc',
'browser/views/tabs/tab_overview_controller.h',
+ 'browser/views/tabs/tab_overview_drag_controller.cc',
+ 'browser/views/tabs/tab_overview_drag_controller.h',
'browser/views/tabs/tab_overview_grid.cc',
'browser/views/tabs/tab_overview_grid.h',
],
@@ -1883,6 +1887,8 @@
['include', 'browser/views/tabs/tab_overview_container.h'],
['include', 'browser/views/tabs/tab_overview_controller.cc'],
['include', 'browser/views/tabs/tab_overview_controller.h'],
+ ['include', 'browser/views/tabs/tab_overview_drag_controller.cc'],
+ ['include', 'browser/views/tabs/tab_overview_drag_controller.h'],
['include', 'browser/views/tabs/tab_overview_grid.cc'],
['include', 'browser/views/tabs/tab_overview_grid.h'],
],