diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-10 21:31:32 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-10 21:31:32 +0000 |
commit | 9aefecc973faa549046389937bd526f00fb26b5f (patch) | |
tree | b7e6d42698e2366d118d727f87f160c6c4ca1638 | |
parent | a3b8c532a83a41f3a21907973b54570989afcde9 (diff) | |
download | chromium_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.cc | 2 | ||||
-rw-r--r-- | chrome/browser/gtk/browser_window_gtk.cc | 6 | ||||
-rw-r--r-- | chrome/browser/gtk/browser_window_gtk.h | 7 | ||||
-rw-r--r-- | chrome/browser/views/tabs/grid.cc | 78 | ||||
-rw-r--r-- | chrome/browser/views/tabs/grid.h | 19 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_overview_cell.cc | 3 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_overview_cell.h | 10 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_overview_controller.cc | 164 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_overview_controller.h | 84 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_overview_drag_controller.cc | 399 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_overview_drag_controller.h | 157 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_overview_grid.cc | 195 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_overview_grid.h | 102 | ||||
-rw-r--r-- | chrome/chrome.gyp | 14 |
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'], ], |