summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-07 18:44:31 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-07 18:44:31 +0000
commit7c0560f904487819f2c408c87899d1ccfa91c1fe (patch)
treeda949466cd6dd09f5ce575b206a65b6ba0e1a2f9
parent93a2c7241cddc33af51cdc3daea03e414f544ec8 (diff)
downloadchromium_src-7c0560f904487819f2c408c87899d1ccfa91c1fe.zip
chromium_src-7c0560f904487819f2c408c87899d1ccfa91c1fe.tar.gz
chromium_src-7c0560f904487819f2c408c87899d1ccfa91c1fe.tar.bz2
Basics of a new TabStrip.It's very, very rough, but I wanted to check it in so I don't have to keep typing svn pset as I pass patches back and forth between machines.Behind a command line flag --enable-tabtastic2.I'm trying to split the TabContents specific stuff off of the TabStrip so it's more generic (and more easily mocked for unit testing of various layout conditions). Hence TabStrip vs. BrowserTabStrip. TabStrip may move into views/ once this process is complete.Animator is a utility that can be associated with a View that (at this point) animates that View's bounds from wherever it is now to somewhere else. The TabStrip uses this to do animations for individual Tabs that are independent of each other - a limitation of the old TabStrip is that only one animation is ever active at a time so its animations are a little jumpy compared to other products.Also, detached tab dragging shows the live contents, with all animations/video/etc.Like I said, this is really rough, but I didn't want it to grow any bigger. I will write up a design doc later.http://crbug.com/9032TEST=TBD... will finally be doing some for TabStrip layout!
Review URL: http://codereview.chromium.org/42490 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20053 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/browser.cc12
-rw-r--r--chrome/browser/browser.h3
-rw-r--r--chrome/browser/browser_window.h7
-rw-r--r--chrome/browser/tabs/tab_strip_model.h10
-rw-r--r--chrome/browser/tabs/tab_strip_model_unittest.cc4
-rw-r--r--chrome/browser/views/frame/browser_frame.h3
-rw-r--r--chrome/browser/views/frame/browser_frame_win.cc85
-rw-r--r--chrome/browser/views/frame/browser_frame_win.h22
-rw-r--r--chrome/browser/views/frame/browser_view.cc86
-rw-r--r--chrome/browser/views/frame/browser_view.h6
-rw-r--r--chrome/browser/views/tabs/browser_tab_strip.cc148
-rw-r--r--chrome/browser/views/tabs/browser_tab_strip.h58
-rw-r--r--chrome/browser/views/tabs/tab_2.cc119
-rw-r--r--chrome/browser/views/tabs/tab_2.h114
-rw-r--r--chrome/browser/views/tabs/tab_strip_2.cc406
-rw-r--r--chrome/browser/views/tabs/tab_strip_2.h163
-rw-r--r--chrome/chrome.gyp6
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h2
-rw-r--r--views/animator.cc151
-rw-r--r--views/animator.h110
-rw-r--r--views/views.gyp2
-rw-r--r--views/widget/widget.h6
-rw-r--r--views/widget/widget_gtk.cc5
-rw-r--r--views/widget/widget_gtk.h2
-rw-r--r--views/widget/widget_win.cc8
-rw-r--r--views/widget/widget_win.h6
27 files changed, 1520 insertions, 27 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index f2fe26e..5d08f29 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -1433,6 +1433,18 @@ Browser* Browser::CreateNewStripWithContents(TabContents* detached_contents,
return browser;
}
+void Browser::ContinueDraggingDetachedTab(TabContents* contents,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& tab_bounds) {
+ Browser* browser = new Browser(TYPE_NORMAL, profile_);
+ browser->set_override_bounds(window_bounds);
+ browser->CreateBrowserWindow();
+ browser->tabstrip_model()->AppendTabContents(contents, true);
+ browser->LoadingStateChanged(contents);
+ browser->window()->Show();
+ browser->window()->ContinueDraggingDetachedTab(tab_bounds);
+}
+
int Browser::GetDragActions() const {
return TAB_TEAROFF_ACTION | (tab_count() > 1 ? TAB_MOVE_ACTION : 0);
}
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index c3b6538..819b551 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -417,6 +417,9 @@ class Browser : public TabStripModelDelegate,
virtual Browser* CreateNewStripWithContents(TabContents* detached_contents,
const gfx::Rect& window_bounds,
const DockInfo& dock_info);
+ virtual void ContinueDraggingDetachedTab(TabContents* contents,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& tab_bounds);
virtual int GetDragActions() const;
// Construct a TabContents for a given URL, profile and transition type.
// If instance is not null, its process will be used to render the tab.
diff --git a/chrome/browser/browser_window.h b/chrome/browser/browser_window.h
index 4ff81bb..dbffe09 100644
--- a/chrome/browser/browser_window.h
+++ b/chrome/browser/browser_window.h
@@ -195,6 +195,13 @@ class BrowserWindow {
virtual void ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
gfx::NativeWindow parent_window) = 0;
+ // Asks the window to continue a drag operation begun in a different browser
+ // window. |tab_bounds| are the bounds of the Tab view that was dragged from
+ // the source window, in screen coordinates. The corresponding Tab view in
+ // this new window will be positioned at these bounds for a seamless
+ // appearance.
+ virtual void ContinueDraggingDetachedTab(const gfx::Rect& tab_bounds) {}
+
// BrowserThemeProvider calls this when a user has changed his or her theme,
// indicating that it's time to redraw everything.
virtual void UserChangedTheme() = 0;
diff --git a/chrome/browser/tabs/tab_strip_model.h b/chrome/browser/tabs/tab_strip_model.h
index 2431883..fdbe77e 100644
--- a/chrome/browser/tabs/tab_strip_model.h
+++ b/chrome/browser/tabs/tab_strip_model.h
@@ -130,6 +130,16 @@ class TabStripModelDelegate {
const gfx::Rect& window_bounds,
const DockInfo& dock_info) = 0;
+ // Creates a new Browser object and window containing the specified
+ // |contents|, and continues a drag operation that began within the source
+ // window's tab strip. |window_bounds| are the bounds of the source window in
+ // screen coordinates, used to place the new window, and |tab_bounds| are the
+ // bounds of the dragged Tab view in the source window, in screen coordinates,
+ // used to place the new Tab in the new window.
+ virtual void ContinueDraggingDetachedTab(TabContents* contents,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& tab_bounds) = 0;
+
enum {
TAB_MOVE_ACTION = 1,
TAB_TEAROFF_ACTION = 2
diff --git a/chrome/browser/tabs/tab_strip_model_unittest.cc b/chrome/browser/tabs/tab_strip_model_unittest.cc
index 0f74cc0..19991a3 100644
--- a/chrome/browser/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/tabs/tab_strip_model_unittest.cc
@@ -36,6 +36,10 @@ class TabStripDummyDelegate : public TabStripModelDelegate {
const DockInfo& dock_info) {
return NULL;
}
+ virtual void ContinueDraggingDetachedTab(TabContents* contents,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& tab_bounds) {
+ }
virtual int GetDragActions() const { return 0; }
virtual TabContents* CreateTabContentsForURL(
const GURL& url,
diff --git a/chrome/browser/views/frame/browser_frame.h b/chrome/browser/views/frame/browser_frame.h
index e0539e9..4e09619 100644
--- a/chrome/browser/views/frame/browser_frame.h
+++ b/chrome/browser/views/frame/browser_frame.h
@@ -53,6 +53,9 @@ class BrowserFrame {
// Tells the frame to update the throbber.
virtual void UpdateThrobber(bool running) = 0;
+ // Tells the frame to continue a drag detached tab operation.
+ virtual void ContinueDraggingDetachedTab() = 0;
+
// Returns the theme provider for this frame.
virtual ThemeProvider* GetThemeProviderForFrame() const = 0;
};
diff --git a/chrome/browser/views/frame/browser_frame_win.cc b/chrome/browser/views/frame/browser_frame_win.cc
index 1d39820..2f00e20 100644
--- a/chrome/browser/views/frame/browser_frame_win.cc
+++ b/chrome/browser/views/frame/browser_frame_win.cc
@@ -11,16 +11,20 @@
#include "app/win_util.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/browser_list.h"
+#include "chrome/browser/dock_info.h"
#include "chrome/browser/views/frame/browser_non_client_frame_view.h"
#include "chrome/browser/views/frame/browser_root_view.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/frame/glass_browser_frame_view.h"
#include "chrome/browser/views/frame/opaque_browser_frame_view.h"
+#include "chrome/browser/views/tabs/browser_tab_strip.h"
#include "grit/theme_resources.h"
+#include "views/screen.h"
#include "views/window/window_delegate.h"
// static
static const int kClientEdgeThickness = 3;
+static const int kTabDragWindowAlpha = 200;
// static (Factory method.)
BrowserFrame* BrowserFrame::Create(BrowserView* browser_view,
@@ -36,6 +40,10 @@ BrowserFrame* BrowserFrame::Create(BrowserView* browser_view,
BrowserFrameWin::BrowserFrameWin(BrowserView* browser_view, Profile* profile)
: WindowWin(browser_view),
browser_view_(browser_view),
+ saved_window_style_(0),
+ saved_window_ex_style_(0),
+ detached_drag_mode_(false),
+ drop_tabstrip_(NULL),
root_view_(NULL),
frame_initialized_(false),
profile_(profile) {
@@ -80,6 +88,16 @@ void BrowserFrameWin::UpdateThrobber(bool running) {
browser_frame_view_->UpdateThrobber(running);
}
+void BrowserFrameWin::ContinueDraggingDetachedTab() {
+ detached_drag_mode_ = true;
+
+ // Set the frame to partially transparent.
+ UpdateWindowAlphaForTabDragging(detached_drag_mode_);
+
+ // Send the message directly, so that the window is positioned appropriately.
+ SendMessage(GetNativeWindow(), WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(0, 0));
+}
+
ThemeProvider* BrowserFrameWin::GetThemeProviderForFrame() const {
// This is implemented for a different interface than GetThemeProvider is,
// but they mean the same things.
@@ -111,9 +129,28 @@ void BrowserFrameWin::OnEndSession(BOOL ending, UINT logoff) {
}
void BrowserFrameWin::OnEnterSizeMove() {
+ drop_tabstrip_ = NULL;
browser_view_->WindowMoveOrResizeStarted();
}
+void BrowserFrameWin::OnExitSizeMove() {
+ if (TabStrip2::Enabled()) {
+ if (detached_drag_mode_) {
+ detached_drag_mode_ = false;
+ if (drop_tabstrip_) {
+ gfx::Point screen_point = views::Screen::GetCursorScreenPoint();
+ BrowserTabStrip* tabstrip = browser_view_->bts();
+ gfx::Rect tsb = tabstrip->GetDraggedTabScreenBounds(screen_point);
+ drop_tabstrip_->AttachTab(tabstrip->DetachTab(0), screen_point, tsb);
+ } else {
+ UpdateWindowAlphaForTabDragging(detached_drag_mode_);
+ browser_view_->bts()->SendDraggedTabHome();
+ }
+ }
+ }
+ WidgetWin::OnExitSizeMove();
+}
+
void BrowserFrameWin::OnInitMenuPopup(HMENU menu, UINT position,
BOOL is_system_menu) {
browser_view_->PrepareToRunSystemMenu(menu);
@@ -228,6 +265,30 @@ LRESULT BrowserFrameWin::OnNCHitTest(const CPoint& pt) {
}
void BrowserFrameWin::OnWindowPosChanged(WINDOWPOS* window_pos) {
+ if (TabStrip2::Enabled()) {
+ if (detached_drag_mode_) {
+ // TODO(beng): move all to BrowserTabStrip...
+
+ // We check to see if the mouse cursor is in the magnetism zone of another
+ // visible TabStrip. If so, we should dock to it.
+ std::set<HWND> ignore_windows;
+ ignore_windows.insert(GetNativeWindow());
+
+ gfx::Point screen_point = views::Screen::GetCursorScreenPoint();
+ HWND local_window =
+ DockInfo::GetLocalProcessWindowAtPoint(screen_point, ignore_windows);
+ if (local_window) {
+ drop_tabstrip_ =
+ BrowserView::GetBrowserViewForNativeWindow(local_window)->bts();
+ if (TabStrip2::IsDragRearrange(drop_tabstrip_, screen_point)) {
+ ReleaseCapture();
+ return;
+ }
+ }
+ drop_tabstrip_ = NULL;
+ }
+ }
+
// Windows lies to us about the position of the minimize button before a
// window is visible. We use the position of the minimize button to place the
// distributor logo in official builds. When the window is shown, we need to
@@ -242,6 +303,8 @@ void BrowserFrameWin::OnWindowPosChanged(WINDOWPOS* window_pos) {
GetNonClientView()->Layout();
GetNonClientView()->SchedulePaint();
}
+
+ // Let the default window procedure handle - IMPORTANT!
WindowWin::OnWindowPosChanged(window_pos);
}
@@ -290,9 +353,29 @@ void BrowserFrameWin::UpdateDWMFrame() {
}
// In maximized mode, we only have a titlebar strip of glass, no side/bottom
// borders.
- if (!browser_view_->IsFullscreen()) {
+ if (!browser_view_->IsFullscreen() && !TabStrip2::Enabled()) {
margins.cyTopHeight =
GetBoundsForTabStrip(browser_view_->tabstrip()).bottom();
}
DwmExtendFrameIntoClientArea(GetNativeView(), &margins);
}
+
+void BrowserFrameWin::UpdateWindowAlphaForTabDragging(bool dragging) {
+ HWND frame_hwnd = GetNativeWindow();
+ if (dragging) {
+ // Make the frame slightly transparent during the drag operation.
+ saved_window_style_ = ::GetWindowLong(frame_hwnd, GWL_STYLE);
+ saved_window_ex_style_ = ::GetWindowLong(frame_hwnd, GWL_EXSTYLE);
+ ::SetWindowLong(frame_hwnd, GWL_EXSTYLE,
+ saved_window_ex_style_ | WS_EX_LAYERED);
+ // Remove the captions tyle so the window doesn't have window controls for a
+ // more "transparent" look.
+ ::SetWindowLong(frame_hwnd, GWL_STYLE,
+ saved_window_style_ & ~WS_CAPTION);
+ SetLayeredWindowAttributes(frame_hwnd, RGB(0xFF, 0xFF, 0xFF),
+ kTabDragWindowAlpha, LWA_ALPHA);
+ } else {
+ ::SetWindowLong(frame_hwnd, GWL_STYLE, saved_window_style_);
+ ::SetWindowLong(frame_hwnd, GWL_EXSTYLE, saved_window_ex_style_);
+ }
+}
diff --git a/chrome/browser/views/frame/browser_frame_win.h b/chrome/browser/views/frame/browser_frame_win.h
index 47b0afa..a355019 100644
--- a/chrome/browser/views/frame/browser_frame_win.h
+++ b/chrome/browser/views/frame/browser_frame_win.h
@@ -12,6 +12,7 @@
class AeroGlassNonClientView;
class BrowserNonClientFrameView;
class BrowserRootView;
+class BrowserTabStrip;
class BrowserView;
class NonClientFrameView;
class Profile;
@@ -40,6 +41,7 @@ class BrowserFrameWin : public BrowserFrame, public views::WindowWin {
virtual int GetMinimizeButtonOffset() const;
virtual gfx::Rect GetBoundsForTabStrip(TabStrip* tabstrip) const;
virtual void UpdateThrobber(bool running);
+ virtual void ContinueDraggingDetachedTab();
virtual ThemeProvider* GetThemeProviderForFrame() const;
// Overridden from views::Widget.
@@ -52,8 +54,9 @@ class BrowserFrameWin : public BrowserFrame, public views::WindowWin {
// Overridden from views::WidgetWin:
virtual bool AcceleratorPressed(views::Accelerator* accelerator);
virtual bool GetAccelerator(int cmd_id, views::Accelerator* accelerator);
- virtual void OnEnterSizeMove();
virtual void OnEndSession(BOOL ending, UINT logoff);
+ virtual void OnEnterSizeMove();
+ virtual void OnExitSizeMove();
virtual void OnInitMenuPopup(HMENU menu, UINT position, BOOL is_system_menu);
virtual LRESULT OnMouseActivate(HWND window,
UINT hittest_code,
@@ -76,6 +79,10 @@ class BrowserFrameWin : public BrowserFrame, public views::WindowWin {
// Updates the DWM with the frame bounds.
void UpdateDWMFrame();
+ // Update the window's pacity when entering and exiting detached dragging
+ // mode.
+ void UpdateWindowAlphaForTabDragging(bool dragging);
+
// The BrowserView is our ClientView. This is a pointer to it.
BrowserView* browser_view_;
@@ -91,6 +98,19 @@ class BrowserFrameWin : public BrowserFrame, public views::WindowWin {
Profile* profile_;
+ // The window styles before we modified them for a tab dragging operation.
+ DWORD saved_window_style_;
+ DWORD saved_window_ex_style_;
+
+ // True if the window is currently being moved in a detached tab drag
+ // operation.
+ bool detached_drag_mode_;
+
+ // When this frame represents a detached tab being dragged, this is a TabStrip
+ // in another window that the tab being dragged would be docked to if the
+ // mouse were released, or NULL if there is no suitable TabStrip.
+ BrowserTabStrip* drop_tabstrip_;
+
DISALLOW_COPY_AND_ASSIGN(BrowserFrameWin);
};
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index 2cafb1a..e83a46b 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -39,6 +39,7 @@
#include "chrome/browser/views/infobars/infobar_container.h"
#include "chrome/browser/views/status_bubble_views.h"
#include "chrome/browser/views/tab_contents/tab_contents_container.h"
+#include "chrome/browser/views/tabs/browser_tab_strip.h"
#include "chrome/browser/views/tabs/tab_strip.h"
#include "chrome/browser/views/toolbar_star_toggle.h"
#include "chrome/browser/views/toolbar_view.h"
@@ -430,7 +431,10 @@ int BrowserView::GetTabStripHeight() const {
// We want to return tabstrip_->height(), but we might be called in the midst
// of layout, when that hasn't yet been updated to reflect the current state.
// So return what the tabstrip height _ought_ to be right now.
- return IsTabStripVisible() ? tabstrip_->GetPreferredSize().height() : 0;
+ views::View* tabstrip =
+ TabStrip2::Enabled() ? static_cast<views::View*>(bts_)
+ : static_cast<views::View*>(tabstrip_);
+ return IsTabStripVisible() ? tabstrip->GetPreferredSize().height() : 0;
}
gfx::Rect BrowserView::GetTabStripBounds() const {
@@ -940,6 +944,12 @@ void BrowserView::ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
browser::ShowHtmlDialogView(parent, browser_.get(), delegate);
}
+void BrowserView::ContinueDraggingDetachedTab(const gfx::Rect& tab_bounds) {
+ DCHECK(TabStrip2::Enabled());
+ bts_->SetDraggedTabBounds(0, tab_bounds);
+ frame_->ContinueDraggingDetachedTab();
+}
+
void BrowserView::UserChangedTheme() {
frame_->GetWindow()->FrameTypeChanged();
GetRootView()->ThemeChanged();
@@ -1218,7 +1228,7 @@ views::ClientView* BrowserView::CreateClientView(views::Window* window) {
bool BrowserView::CanClose() const {
// You cannot close a frame for which there is an active originating drag
// session.
- if (tabstrip_->IsDragSessionActive())
+ if (!TabStrip2::Enabled() && tabstrip_->IsDragSessionActive())
return false;
// Give beforeunload handlers the chance to cancel the close before we hide
@@ -1276,18 +1286,20 @@ int BrowserView::NonClientHitTest(const gfx::Point& point) {
View::ConvertPointToView(GetParent(), this, &point_in_view_coords);
// See if the mouse pointer is within the bounds of the TabStrip.
- gfx::Point point_in_tabstrip_coords(point);
- View::ConvertPointToView(GetParent(), tabstrip_, &point_in_tabstrip_coords);
- if (tabstrip_->HitTest(point_in_tabstrip_coords)) {
- if (tabstrip_->PointIsWithinWindowCaption(point_in_tabstrip_coords))
- return HTCAPTION;
- return HTCLIENT;
+ if (!TabStrip2::Enabled()) {
+ gfx::Point point_in_tabstrip_coords(point);
+ View::ConvertPointToView(GetParent(), tabstrip_, &point_in_tabstrip_coords);
+ if (tabstrip_->HitTest(point_in_tabstrip_coords)) {
+ if (tabstrip_->PointIsWithinWindowCaption(point_in_tabstrip_coords))
+ return HTCAPTION;
+ return HTCLIENT;
+ }
}
// The top few pixels of the TabStrip are a drop-shadow - as we're pretty
// starved of dragable area, let's give it to window dragging (this also
// makes sense visually).
- if (!IsMaximized() &&
+ if (!TabStrip2::Enabled() && !IsMaximized() &&
(point_in_view_coords.y() < tabstrip_->y() + kTabShadowSize)) {
// We return HTNOWHERE as this is a signal to our containing
// NonClientView that it should figure out what the correct hit-test
@@ -1300,8 +1312,13 @@ int BrowserView::NonClientHitTest(const gfx::Point& point) {
// within the bounds of this view, the point is considered to be within the
// client area.
gfx::Rect bv_bounds = bounds();
- bv_bounds.Offset(0, toolbar_->y());
- bv_bounds.set_height(bv_bounds.height() - toolbar_->y());
+ if (TabStrip2::Enabled()) {
+ bv_bounds.Offset(0, bts_->y());
+ bv_bounds.set_height(bv_bounds.height() - bts_->y());
+ } else {
+ bv_bounds.Offset(0, toolbar_->y());
+ bv_bounds.set_height(bv_bounds.height() - toolbar_->y());
+ }
if (bv_bounds.Contains(point))
return HTCLIENT;
@@ -1325,11 +1342,14 @@ int BrowserView::NonClientHitTest(const gfx::Point& point) {
}
gfx::Size BrowserView::GetMinimumSize() {
+ views::View* tabstrip =
+ TabStrip2::Enabled() ? static_cast<views::View*>(bts_)
+ : static_cast<views::View*>(tabstrip_);
// TODO(noname): In theory the tabstrip width should probably be
// (OTR + tabstrip + caption buttons) width.
gfx::Size tabstrip_size(
browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) ?
- tabstrip_->GetMinimumSize() : gfx::Size());
+ tabstrip->GetMinimumSize() : gfx::Size());
gfx::Size toolbar_size(
(browser_->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) ||
browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR)) ?
@@ -1417,11 +1437,16 @@ void BrowserView::Init() {
LoadAccelerators();
SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME));
- tabstrip_ = new TabStrip(browser_->tabstrip_model());
- tabstrip_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TABSTRIP));
- AddChildView(tabstrip_);
- tabstrip_->InitTabStripButtons();
- frame_->TabStripCreated(tabstrip_);
+ if (TabStrip2::Enabled()) {
+ bts_ = new BrowserTabStrip(browser_->tabstrip_model());
+ AddChildView(bts_);
+ } else {
+ tabstrip_ = new TabStrip(browser_->tabstrip_model());
+ tabstrip_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TABSTRIP));
+ AddChildView(tabstrip_);
+ tabstrip_->InitTabStripButtons();
+ frame_->TabStripCreated(tabstrip_);
+ }
toolbar_ = new ToolbarView(browser_.get());
AddChildView(toolbar_);
@@ -1469,18 +1494,30 @@ void BrowserView::InitSystemMenu() {
#endif
int BrowserView::LayoutTabStrip() {
- gfx::Rect tabstrip_bounds = frame_->GetBoundsForTabStrip(tabstrip_);
- tabstrip_->SetBackgroundOffset(
- gfx::Point(tabstrip_bounds.x(), tabstrip_bounds.y()));
+ gfx::Rect tabstrip_bounds;
+ if (TabStrip2::Enabled()) {
+ tabstrip_bounds = gfx::Rect(0, 0, width(),
+ bts_->GetPreferredSize().height());
+ } else {
+ tabstrip_bounds = frame_->GetBoundsForTabStrip(tabstrip_);
+ tabstrip_->SetBackgroundOffset(
+ gfx::Point(tabstrip_bounds.x(), tabstrip_bounds.y()));
+ }
gfx::Point tabstrip_origin = tabstrip_bounds.origin();
ConvertPointToView(GetParent(), this, &tabstrip_origin);
tabstrip_bounds.set_origin(tabstrip_origin);
bool visible = IsTabStripVisible();
int y = visible ? tabstrip_bounds.y() : 0;
int height = visible ? tabstrip_bounds.height() : 0;
- tabstrip_->SetVisible(visible);
- tabstrip_->SetBounds(tabstrip_bounds.x(), y, tabstrip_bounds.width(), height);
- return y + height;
+ int bottom = y + height;
+ if (TabStrip2::Enabled()) {
+ gfx::Size btsps = bts_->GetPreferredSize();
+ bts_->SetBounds(tabstrip_bounds.x(), y, width(), btsps.height());
+ } else {
+ tabstrip_->SetVisible(visible);
+ tabstrip_->SetBounds(tabstrip_bounds.x(), y, tabstrip_bounds.width(), height);
+ }
+ return bottom;
}
int BrowserView::LayoutToolbar(int top) {
@@ -1796,7 +1833,8 @@ void BrowserView::LoadingAnimationCallback() {
// will return false for fullscreen windows, but we still need to update
// their animations (so that when they come out of fullscreen mode they'll
// be correct).
- tabstrip_->UpdateLoadingAnimations();
+ if (!TabStrip2::Enabled())
+ tabstrip_->UpdateLoadingAnimations();
} else if (ShouldShowWindowIcon()) {
// ... or in the window icon area for popups and app windows.
TabContents* tab_contents = browser_->GetSelectedTabContents();
diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h
index 3af778f..c3b48a9 100644
--- a/chrome/browser/views/frame/browser_view.h
+++ b/chrome/browser/views/frame/browser_view.h
@@ -31,6 +31,7 @@
class BookmarkBarView;
class Browser;
class BrowserBubble;
+class BrowserTabStrip;
class DownloadShelfView;
class EncodingMenuModel;
class ExtensionShelf;
@@ -121,6 +122,7 @@ class BrowserView : public BrowserWindow,
// Accessor for the TabStrip.
TabStrip* tabstrip() const { return tabstrip_; }
+ BrowserTabStrip* bts() const { return bts_; }
// Accessor for the ExtensionShelf.
ExtensionShelf* extension_shelf() const { return extension_shelf_; }
@@ -238,6 +240,7 @@ class BrowserView : public BrowserWindow,
virtual void ConfirmBrowserCloseWithPendingDownloads();
virtual void ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
gfx::NativeWindow parent_window);
+ virtual void ContinueDraggingDetachedTab(const gfx::Rect& tab_bounds);
virtual void UserChangedTheme();
virtual int GetExtraRenderViewHeight() const;
virtual void TabContentsFocused(TabContents* source);
@@ -395,6 +398,9 @@ class BrowserView : public BrowserWindow,
// The TabStrip.
TabStrip* tabstrip_;
+ // The BrowserTabStrip.
+ BrowserTabStrip* bts_;
+
// The Toolbar containing the navigation buttons, menus and the address bar.
ToolbarView* toolbar_;
diff --git a/chrome/browser/views/tabs/browser_tab_strip.cc b/chrome/browser/views/tabs/browser_tab_strip.cc
new file mode 100644
index 0000000..4a3be6b
--- /dev/null
+++ b/chrome/browser/views/tabs/browser_tab_strip.cc
@@ -0,0 +1,148 @@
+// 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/browser_tab_strip.h"
+
+#include "chrome/browser/tab_contents/tab_contents.h"
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+// RemovingTabModel
+// After a tab is removed from the TabStrip2's model, the model can no longer
+// provide information about the tab needed to update the display, however the
+// TabStrip2 continues to display the tab until it animates out of existence.
+// During this period, this model serves as a dummy. It is created and assigned
+// to the tab when the tab is removed from the model and owned by the Tab2.
+
+class RemovingTabModel : public Tab2Model {
+ public:
+ explicit RemovingTabModel(TabContents* source)
+ : title_(source->GetTitle()) {
+ }
+
+ // Overridden from Tab2Model:
+ virtual string16 GetTitle(Tab2* tab) const { return title_; }
+ virtual bool IsSelected(Tab2* tab) const { return false; }
+ virtual void SelectTab(Tab2* tab) { NOTREACHED(); }
+ virtual void CaptureDragInfo(Tab2* tab,
+ const views::MouseEvent& drag_event) {
+ NOTREACHED();
+ }
+ virtual bool DragTab(Tab2* tab, const views::MouseEvent& drag_event) {
+ NOTREACHED();
+ return false;
+ }
+ virtual void DragEnded(Tab2* tab) {
+ NOTREACHED();
+ }
+ virtual views::AnimatorDelegate* AsAnimatorDelegate() {
+ NOTREACHED();
+ return NULL;
+ }
+
+ private:
+ string16 title_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemovingTabModel);
+};
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// BrowserTabStrip, public:
+
+BrowserTabStrip::BrowserTabStrip(TabStripModel* model)
+ : TabStrip2(this),
+ model_(model) {
+ model_->AddObserver(this);
+}
+
+BrowserTabStrip::~BrowserTabStrip() {
+ model_->RemoveObserver(this);
+}
+
+TabContents* BrowserTabStrip::DetachTab(int index) {
+ TabContents* contents = model_->GetTabContentsAt(index);
+ model_->DetachTabContentsAt(index);
+ return contents;
+}
+
+void BrowserTabStrip::AttachTab(TabContents* contents,
+ const gfx::Point& screen_point,
+ const gfx::Rect& tab_screen_bounds) {
+ gfx::Point tabstrip_point(screen_point);
+
+ gfx::Point screen_origin;
+ View::ConvertPointToScreen(this, &screen_origin);
+ tabstrip_point.Offset(-screen_origin.x(), -screen_origin.y());
+
+ int index = GetInsertionIndexForPoint(tabstrip_point);
+ model_->InsertTabContentsAt(index, contents, true, false);
+
+ gfx::Point origin(tab_screen_bounds.origin());
+ View::ConvertPointToView(NULL, this, &origin);
+ ResumeDraggingTab(index, gfx::Rect(origin, tab_screen_bounds.size()));
+ // TODO(beng): post task to continue dragging now.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// BrowserTabStrip, TabStripModelObserver overrides:
+
+void BrowserTabStrip::TabInsertedAt(TabContents* contents,
+ int index,
+ bool foreground) {
+ AddTabAt(index);
+}
+
+void BrowserTabStrip::TabDetachedAt(TabContents* contents, int index) {
+ RemoveTabAt(index, new RemovingTabModel(contents));
+}
+
+void BrowserTabStrip::TabSelectedAt(TabContents* old_contents,
+ TabContents* contents,
+ int index,
+ bool user_gesture) {
+ TabStrip2::SelectTabAt(index);
+}
+
+void BrowserTabStrip::TabMoved(TabContents* contents,
+ int from_index,
+ int to_index) {
+ TabStrip2::MoveTabAt(from_index, to_index);
+}
+
+void BrowserTabStrip::TabChangedAt(TabContents* contents, int index) {
+ // TODO
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// BrowserTabStrip, TabStrip2Model overrides:
+
+string16 BrowserTabStrip::GetTitle(int index) const {
+ return model_->GetTabContentsAt(index)->GetTitle();
+}
+
+bool BrowserTabStrip::IsSelected(int index) const {
+ return model_->selected_index() == index;
+}
+
+void BrowserTabStrip::SelectTabAt(int index) {
+ model_->SelectTabContentsAt(index, true);
+}
+
+bool BrowserTabStrip::CanDragTabs() const {
+ return model_->delegate()->GetDragActions() != 0;
+}
+
+void BrowserTabStrip::MoveTabAt(int index, int to_index) {
+ model_->MoveTabContentsAt(index, to_index, true);
+}
+
+void BrowserTabStrip::DetachTabAt(int index, const gfx::Rect& window_bounds,
+ const gfx::Rect& tab_bounds) {
+ TabContents* contents = DetachTab(index);
+ model_->delegate()->ContinueDraggingDetachedTab(contents, window_bounds,
+ tab_bounds);
+}
diff --git a/chrome/browser/views/tabs/browser_tab_strip.h b/chrome/browser/views/tabs/browser_tab_strip.h
new file mode 100644
index 0000000..f88de2d
--- /dev/null
+++ b/chrome/browser/views/tabs/browser_tab_strip.h
@@ -0,0 +1,58 @@
+// 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_BROWSER_TAB_STRIP_H_
+#define CHROME_BROWSER_VIEWS_TABS_BROWSER_TAB_STRIP_H_
+
+#include "chrome/browser/views/tabs/tab_strip_2.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+
+// A specialization fo TabStrip2 for the browser window.
+//
+// TODO(beng): This shouldn't be a subclass of TabStrip2, rather it should own
+// one.
+class BrowserTabStrip : public TabStrip2,
+ public TabStrip2Model,
+ public TabStripModelObserver {
+ public:
+ explicit BrowserTabStrip(TabStripModel* model);
+ virtual ~BrowserTabStrip();
+
+ // Detaches the tab at the specified index.
+ TabContents* DetachTab(int index);
+
+ // Attaches the specified TabContents at the appropriate position given the
+ // mouse cursor at the specified screen position.
+ void AttachTab(TabContents* contents, const gfx::Point& screen_point,
+ const gfx::Rect& tab_screen_bounds);
+
+ // Overridden from TabStripModelObserver:
+ virtual void TabInsertedAt(TabContents* contents,
+ int index,
+ bool foreground);
+ virtual void TabDetachedAt(TabContents* contents, int index);
+ virtual void TabSelectedAt(TabContents* old_contents,
+ TabContents* contents,
+ int index,
+ bool user_gesture);
+ virtual void TabMoved(TabContents* contents, int from_index, int to_index);
+ virtual void TabChangedAt(TabContents* contents, int index);
+
+ // Overridden from TabStrip2Model:
+ virtual string16 GetTitle(int index) const;
+ virtual bool IsSelected(int index) const;
+ virtual void SelectTabAt(int index);
+ virtual bool CanDragTabs() const;
+ virtual void MoveTabAt(int index, int to_index);
+ virtual void DetachTabAt(int index,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& tab_bounds);
+
+ private:
+ TabStripModel* model_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserTabStrip);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_TABS_BROWSER_TAB_STRIP_H_
diff --git a/chrome/browser/views/tabs/tab_2.cc b/chrome/browser/views/tabs/tab_2.cc
new file mode 100644
index 0000000..a541f3a
--- /dev/null
+++ b/chrome/browser/views/tabs/tab_2.cc
@@ -0,0 +1,119 @@
+// 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_2.h"
+
+#include "app/gfx/canvas.h"
+#include "app/gfx/font.h"
+#include "app/gfx/path.h"
+#include "views/animator.h"
+
+static const SkScalar kTabCapWidth = 15;
+static const SkScalar kTabTopCurveWidth = 4;
+static const SkScalar kTabBottomCurveWidth = 3;
+
+////////////////////////////////////////////////////////////////////////////////
+// Tab2, public:
+
+Tab2::Tab2(Tab2Model* model)
+ : model_(model),
+ dragging_(false),
+ removing_(false) {
+}
+
+Tab2::~Tab2() {
+}
+
+void Tab2::SetRemovingModel(Tab2Model* model) {
+ removing_model_.reset(model);
+ model_ = removing_model_.get();
+}
+
+bool Tab2::IsAnimating() const {
+ return animator_.get() && animator_->IsAnimating();
+}
+
+views::Animator* Tab2::GetAnimator() {
+ if (!animator_.get())
+ animator_.reset(new views::Animator(this, model_->AsAnimatorDelegate()));
+ return animator_.get();
+}
+
+// static
+gfx::Size Tab2::GetStandardSize() {
+ return gfx::Size(140, 27);
+}
+
+void Tab2::AddTabShapeToPath(gfx::Path* path) const {
+ SkScalar h = SkIntToScalar(height());
+ SkScalar w = SkIntToScalar(width());
+
+ path->moveTo(0, h);
+
+ // Left end cap.
+ path->lineTo(kTabBottomCurveWidth, h - kTabBottomCurveWidth);
+ path->lineTo(kTabCapWidth - kTabTopCurveWidth, kTabTopCurveWidth);
+ path->lineTo(kTabCapWidth, 0);
+
+ // Connect to the right cap.
+ path->lineTo(w - kTabCapWidth, 0);
+
+ // Right end cap.
+ path->lineTo(w - kTabCapWidth + kTabTopCurveWidth, kTabTopCurveWidth);
+ path->lineTo(w - kTabBottomCurveWidth, h - kTabBottomCurveWidth);
+ path->lineTo(w, h);
+
+ // Close out the path.
+ path->lineTo(0, h);
+ path->close();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tab2, views::View overrides:
+
+gfx::Size Tab2::GetPreferredSize() {
+ return gfx::Size();
+}
+
+void Tab2::Layout() {
+}
+
+void Tab2::Paint(gfx::Canvas* canvas) {
+ SkColor color = model_->IsSelected(this) ? SK_ColorRED : SK_ColorGREEN;
+
+ gfx::Path path;
+ AddTabShapeToPath(&path);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(color);
+ canvas->drawPath(path, paint);
+
+ // TODO(beng): less ad-hoc
+ canvas->DrawStringInt(model_->GetTitle(this), gfx::Font(), SK_ColorBLACK,
+ 5, 3, 100, 20);
+}
+
+bool Tab2::OnMousePressed(const views::MouseEvent& event) {
+ if (event.IsLeftMouseButton()) {
+ model_->SelectTab(this);
+ model_->CaptureDragInfo(this, event);
+ }
+ return true;
+}
+
+bool Tab2::OnMouseDragged(const views::MouseEvent& event) {
+ dragging_ = true;
+ return model_->DragTab(this, event);
+}
+
+void Tab2::OnMouseReleased(const views::MouseEvent& event, bool canceled) {
+ if (dragging_) {
+ dragging_ = false;
+ model_->DragEnded(this);
+ }
+}
+
+void Tab2::DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current) {
+}
diff --git a/chrome/browser/views/tabs/tab_2.h b/chrome/browser/views/tabs/tab_2.h
new file mode 100644
index 0000000..f5ae969
--- /dev/null
+++ b/chrome/browser/views/tabs/tab_2.h
@@ -0,0 +1,114 @@
+// 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_2_H_
+#define CHROME_BROWSER_VIEWS_TABS_TAB_2_H_
+
+#include "base/string16.h"
+#include "views/view.h"
+
+class Tab2;
+
+namespace gfx {
+class Canvas;
+class Path;
+};
+
+namespace views {
+class AnimationContext;
+class Animator;
+class AnimatorDelegate;
+}
+
+// An interface implemented by an object that provides data to the Tab2.
+// The Tab2 sometimes owns the Tab2Model. See |removing_model_| in Tab2.
+class Tab2Model {
+ public:
+ virtual ~Tab2Model() {}
+
+ // Tab2 presentation state.
+ virtual string16 GetTitle(Tab2* tab) const = 0;
+ virtual bool IsSelected(Tab2* tab) const = 0;
+
+ // The Tab2 has been clicked and should become selected.
+ virtual void SelectTab(Tab2* tab) = 0;
+
+ // The mouse has been pressed down on the Tab2, pertinent information for any
+ // drag that might occur should be captured at this time.
+ virtual void CaptureDragInfo(Tab2* tab,
+ const views::MouseEvent& drag_event) = 0;
+
+ // The mouse has been dragged after a press on the Tab2.
+ virtual bool DragTab(Tab2* tab, const views::MouseEvent& drag_event) = 0;
+
+ // The current drag operation has ended.
+ virtual void DragEnded(Tab2* tab) = 0;
+
+ // TODO(beng): get rid of this once animator is on View.
+ virtual views::AnimatorDelegate* AsAnimatorDelegate() = 0;
+};
+
+// A view that represents a Tab in a TabStrip2.
+class Tab2 : public views::View {
+ public:
+ explicit Tab2(Tab2Model* model);
+ virtual ~Tab2();
+
+ bool dragging() const { return dragging_; }
+
+ bool removing() const { return removing_; }
+ void set_removing(bool removing) { removing_ = removing; }
+
+ // Assigns and takes ownership of a model object to be used when painting this
+ // Tab2 after the underlying data object has been removed from TabStrip2's
+ // model.
+ void SetRemovingModel(Tab2Model* model);
+
+ // Returns true if the Tab2 is being animated.
+ bool IsAnimating() const;
+
+ // Returns the Tab2's animator, creating one if necessary.
+ // TODO(beng): consider moving to views::View.
+ views::Animator* GetAnimator();
+
+ // Returns the ideal size of the Tab2.
+ static gfx::Size GetStandardSize();
+
+ // Adds the shape of the tab to the specified path. Used to create a clipped
+ // window during detached window dragging operations.
+ void AddTabShapeToPath(gfx::Path* path) const;
+
+ // Overridden from views::View:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+ virtual void Paint(gfx::Canvas* canvas);
+ virtual bool OnMousePressed(const views::MouseEvent& event);
+ virtual bool OnMouseDragged(const views::MouseEvent& event);
+ virtual void OnMouseReleased(const views::MouseEvent& event,
+ bool canceled);
+ virtual void DidChangeBounds(const gfx::Rect& previous,
+ const gfx::Rect& current);
+
+ private:
+ Tab2Model* model_;
+
+ // True if the Tab2 is being dragged currently.
+ bool dragging_;
+
+ // True if the Tab2 represents an object removed from its containing
+ // TabStrip2's model, and is currently being animated closed.
+ bool removing_;
+
+ // Our animator.
+ scoped_ptr<views::Animator> animator_;
+
+ // A dummy model to use for painting the tab after it's been removed from the
+ // TabStrip2's model but while it's still visible in the presentation (being
+ // animated out of existence).
+ scoped_ptr<Tab2Model> removing_model_;
+
+ DISALLOW_COPY_AND_ASSIGN(Tab2);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_TABS_TAB_2_H_
diff --git a/chrome/browser/views/tabs/tab_strip_2.cc b/chrome/browser/views/tabs/tab_strip_2.cc
new file mode 100644
index 0000000..579882c
--- /dev/null
+++ b/chrome/browser/views/tabs/tab_strip_2.cc
@@ -0,0 +1,406 @@
+// 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_strip_2.h"
+
+#include "app/gfx/canvas.h"
+#include "app/slide_animation.h"
+#include "app/win_util.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "chrome/common/chrome_switches.h"
+#include "views/animator.h"
+#include "views/screen.h"
+#include "views/widget/widget.h"
+#include "views/window/non_client_view.h"
+#include "views/window/window.h"
+
+static const int kHorizontalMoveThreshold = 16; // pixels
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, public:
+
+TabStrip2::TabStrip2(TabStrip2Model* model)
+ : model_(model),
+ last_move_screen_x_(0),
+ detach_factory_(this),
+ drag_start_factory_(this) {
+}
+
+TabStrip2::~TabStrip2() {
+}
+
+// static
+bool TabStrip2::Enabled() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableTabtastic2);
+}
+
+void TabStrip2::AddTabAt(int index) {
+ Tab2* tab = new Tab2(this);
+ int insertion_index = GetInternalIndex(index);
+ tabs_.insert(tabs_.begin() + insertion_index, tab);
+ AddChildView(insertion_index, tab);
+ LayoutImpl(LS_TAB_ADD);
+}
+
+void TabStrip2::RemoveTabAt(int index, Tab2Model* removing_model) {
+ Tab2* tab = GetTabAt(GetInternalIndex(index));
+
+ DCHECK(!tab->removing());
+ tab->set_removing(true);
+
+ DCHECK(removing_model);
+ tab->SetRemovingModel(removing_model);
+
+ LayoutImpl(LS_TAB_REMOVE);
+}
+
+void TabStrip2::SelectTabAt(int index) {
+ LayoutImpl(LS_TAB_SELECT);
+ SchedulePaint();
+}
+
+void TabStrip2::MoveTabAt(int index, int to_index) {
+ int from_index = GetInternalIndex(index);
+ Tab2* tab = GetTabAt(from_index);
+ tabs_.erase(tabs_.begin() + from_index);
+ tabs_.insert(tabs_.begin() + GetInternalIndex(to_index), tab);
+ LayoutImpl(LS_TAB_DRAG_REORDER);
+}
+
+int TabStrip2::GetTabCount() const {
+ return tabs_.size();
+}
+
+Tab2* TabStrip2::GetTabAt(int index) const {
+ return tabs_.at(index);
+}
+
+int TabStrip2::GetTabIndex(Tab2* tab) const {
+ std::vector<Tab2*>::const_iterator it = find(tabs_.begin(), tabs_.end(), tab);
+ if (it != tabs_.end())
+ return it - tabs_.begin();
+ return -1;
+}
+
+int TabStrip2::GetInsertionIndexForPoint(const gfx::Point& point) const {
+ int tab_count = GetTabCount();
+ for (int i = 0; i < tab_count; ++i) {
+ if (GetTabAt(i)->removing())
+ continue;
+ gfx::Rect tab_bounds = GetTabAt(i)->bounds();
+ gfx::Rect tab_left_half = tab_bounds;
+ tab_left_half.set_width(tab_left_half.width() / 2);
+ if (point.x() >= tab_left_half.x() && point.x() <= tab_left_half.right())
+ return i;
+ gfx::Rect tab_right_half = tab_bounds;
+ tab_right_half.set_x(tab_right_half.width() / 2);
+ tab_right_half.set_width(tab_right_half.x());
+ if (point.x() > tab_right_half.x() && point.x() <= tab_right_half.right())
+ if (tab_right_half.Contains(point))
+ return i + 1;
+ }
+ return tab_count;
+}
+
+gfx::Rect TabStrip2::GetDraggedTabScreenBounds(const gfx::Point& screen_point) {
+ gfx::Point tab_screen_origin(screen_point);
+ tab_screen_origin.Offset(mouse_tab_offset_.x(), mouse_tab_offset_.y());
+ return gfx::Rect(tab_screen_origin, GetTabAt(0)->bounds().size());
+}
+
+void TabStrip2::SetDraggedTabBounds(int index, const gfx::Rect& tab_bounds) {
+ // This function should only ever be called goats
+ Tab2* dragged_tab = GetTabAt(index);
+ dragged_tab->SetBounds(tab_bounds);
+ SchedulePaint();
+}
+
+void TabStrip2::SendDraggedTabHome() {
+ LayoutImpl(LS_TAB_DRAG_REORDER);
+}
+
+void TabStrip2::ResumeDraggingTab(int index, const gfx::Rect& tab_bounds) {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ drag_start_factory_.NewRunnableMethod(&TabStrip2::StartDragTabImpl, index,
+ tab_bounds));
+}
+
+// static
+bool TabStrip2::IsDragRearrange(TabStrip2* tabstrip,
+ const gfx::Point& screen_point) {
+ gfx::Point origin;
+ View::ConvertPointToScreen(tabstrip, &origin);
+ gfx::Rect tabstrip_bounds_in_screen_coords(origin, tabstrip->bounds().size());
+ if (tabstrip_bounds_in_screen_coords.Contains(screen_point))
+ return true;
+
+ // The tab is only detached if the tab is moved outside the bounds of the
+ // TabStrip to the left or right, or a certain distance above or below the
+ // TabStrip defined by the vertical detach magnetism below. This is to
+ // prevent accidental detaches when rearranging horizontally.
+ static const int kVerticalDetachMagnetism = 45;
+
+ bool rearrange = true;
+ if (screen_point.x() < tabstrip_bounds_in_screen_coords.right() &&
+ screen_point.x() >= tabstrip_bounds_in_screen_coords.x()) {
+ int lower_threshold =
+ tabstrip_bounds_in_screen_coords.bottom() + kVerticalDetachMagnetism;
+ int upper_threshold =
+ tabstrip_bounds_in_screen_coords.y() - kVerticalDetachMagnetism;
+ return screen_point.y() >= upper_threshold &&
+ screen_point.y() <= lower_threshold;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, Tab2Model implementation:
+
+string16 TabStrip2::GetTitle(Tab2* tab) const {
+ return model_->GetTitle(GetTabIndex(tab));
+}
+
+bool TabStrip2::IsSelected(Tab2* tab) const {
+ return model_->IsSelected(GetTabIndex(tab));
+}
+
+void TabStrip2::SelectTab(Tab2* tab) {
+ model_->SelectTabAt(GetTabIndex(tab));
+}
+
+void TabStrip2::CaptureDragInfo(Tab2* tab,
+ const views::MouseEvent& drag_event) {
+ mouse_tab_offset_ = drag_event.location();
+}
+
+bool TabStrip2::DragTab(Tab2* tab, const views::MouseEvent& drag_event) {
+ if (!model_->CanDragTabs())
+ return false;
+
+ int tab_x = tab->x() + drag_event.location().x() - mouse_tab_offset_.x();
+ if (tab_x < 0)
+ tab_x = 0;
+ if ((tab_x + tab->width()) > bounds().right())
+ tab_x = bounds().right() - tab_x - tab->width();
+ tab->SetBounds(tab_x, tab->y(), tab->width(), tab->height());
+ SchedulePaint();
+
+ int tab_index = GetTabIndex(tab);
+ int dest_index = tab_index;
+
+ Tab2* next_tab = NULL;
+ Tab2* prev_tab = NULL;
+ int next_tab_index = tab_index + 1;
+ if (next_tab_index < GetTabCount())
+ next_tab = GetTabAt(next_tab_index);
+ int prev_tab_index = tab_index - 1;
+ if (prev_tab_index >= 0)
+ prev_tab = GetTabAt(prev_tab_index);
+
+ if (next_tab) {
+ int next_tab_middle_x = next_tab->x() + next_tab->bounds().width() / 2;
+ if (!next_tab->IsAnimating() && tab->bounds().right() > next_tab_middle_x)
+ ++dest_index;
+ }
+ if (prev_tab) {
+ int prev_tab_middle_x = prev_tab->x() + prev_tab->bounds().width() / 2;
+ if (!prev_tab->IsAnimating() && tab->bounds().x() < prev_tab_middle_x)
+ --dest_index;
+ }
+
+ gfx::Point screen_point = views::Screen::GetCursorScreenPoint();
+ if (IsDragRearrange(this, screen_point)) {
+ if (abs(screen_point.x() - last_move_screen_x_) >
+ kHorizontalMoveThreshold) {
+ if (dest_index != tab_index) {
+ last_move_screen_x_ = screen_point.x();
+ model_->MoveTabAt(tab_index, dest_index);
+ }
+ }
+ } else {
+ // We're going to detach. We need to release mouse capture so that further
+ // mouse events will be sent to the appropriate window (the detached window)
+ // and so that we don't recursively create nested message loops (dragging
+ // is done by windows in a nested message loop).
+ ReleaseCapture();
+ MessageLoop::current()->PostTask(FROM_HERE,
+ detach_factory_.NewRunnableMethod(&TabStrip2::DragDetachTabImpl,
+ tab, tab_index));
+ }
+ return true;
+}
+
+void TabStrip2::DragEnded(Tab2* tab) {
+ LayoutImpl(LS_TAB_DRAG_NORMALIZE);
+}
+
+views::AnimatorDelegate* TabStrip2::AsAnimatorDelegate() {
+ return this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, views::View overrides:
+
+gfx::Size TabStrip2::GetPreferredSize() {
+ return gfx::Size(0, 27);
+}
+
+void TabStrip2::Layout() {
+ LayoutImpl(LS_OTHER);
+}
+
+void TabStrip2::Paint(gfx::Canvas* canvas) {
+ canvas->FillRectInt(SK_ColorBLUE, 0, 0, width(), height());
+}
+
+void TabStrip2::PaintChildren(gfx::Canvas* canvas) {
+ // Paint the tabs in reverse order, so they stack to the left.
+ Tab2* selected_tab = NULL;
+ for (int i = GetTabCount() - 1; i >= 0; --i) {
+ Tab2* tab = GetTabAt(i);
+ // We must ask the _Tab's_ model, not ourselves, because in some situations
+ // the model will be different to this object, e.g. when a Tab is being
+ // removed after its TabContents has been destroyed.
+ if (!IsSelected(tab)) {
+ tab->ProcessPaint(canvas);
+ } else {
+ selected_tab = tab;
+ }
+ }
+
+ if (GetWindow()->GetNonClientView()->UseNativeFrame()) {
+ // Make sure unselected tabs are somewhat transparent.
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB(200, 255, 255, 255));
+ paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->FillRectInt(
+ 0, 0, width(),
+ height() - 2, // Visible region that overlaps the toolbar.
+ paint);
+ }
+
+ // Paint the selected tab last, so it overlaps all the others.
+ if (selected_tab)
+ selected_tab->ProcessPaint(canvas);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, views::AnimatorDelegate implementation:
+
+views::View* TabStrip2::GetClampedView(views::View* host) {
+ int tab_count = GetTabCount();
+ for (int i = 0; i < tab_count; ++i) {
+ Tab2* tab = GetTabAt(i);
+ if (tab == host && i > 0)
+ return GetTabAt(i - 1);
+ }
+ return NULL;
+}
+
+void TabStrip2::AnimationCompletedForHost(View* host) {
+ Tab2* tab = static_cast<Tab2*>(host);
+ if (tab->removing()) {
+ tabs_.erase(find(tabs_.begin(), tabs_.end(), tab));
+ RemoveChildView(tab);
+ delete tab;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, private:
+
+int TabStrip2::GetAnimateFlagsForLayoutSource(LayoutSource source) const {
+ switch (source) {
+ case LS_TAB_ADD:
+ case LS_TAB_SELECT:
+ case LS_TAB_REMOVE:
+ return views::Animator::ANIMATE_WIDTH | views::Animator::ANIMATE_X |
+ views::Animator::ANIMATE_CLAMP;
+ case LS_TAB_DRAG_REORDER:
+ case LS_TAB_DRAG_NORMALIZE:
+ return views::Animator::ANIMATE_X;
+ }
+ DCHECK(source == LS_OTHER);
+ return views::Animator::ANIMATE_NONE;
+}
+
+void TabStrip2::LayoutImpl(LayoutSource source) {
+ int child_count = GetTabCount();
+ if (child_count > 0) {
+ int child_width = width() / child_count;
+ child_width = std::min(child_width, Tab2::GetStandardSize().width());
+
+ int animate_flags = GetAnimateFlagsForLayoutSource(source);
+ int removing_count = 0;
+ for (int i = 0; i < child_count; ++i) {
+ Tab2* tab = GetTabAt(i);
+ if (tab->removing())
+ ++removing_count;
+ if (!tab->dragging()) {
+ int tab_x = i * child_width - removing_count * child_width;
+ int tab_width = tab->removing() ? 0 : child_width;
+ gfx::Rect new_bounds(tab_x, 0, tab_width, height());
+
+ // Tabs that are currently being removed can have their bounds reset
+ // when another tab in the tabstrip is removed before their remove
+ // animation completes. Before they are given a new target bounds to
+ // animate to, we need to unset the removing property so that they are
+ // not pre-emptively deleted.
+ bool removing = tab->removing();
+ tab->set_removing(false);
+ tab->GetAnimator()->AnimateToBounds(new_bounds, animate_flags);
+ // Now restore the removing property.
+ tab->set_removing(removing);
+ }
+ }
+ }
+}
+
+void TabStrip2::DragDetachTabImpl(Tab2* tab, int index) {
+ gfx::Rect tab_bounds = tab->bounds();
+
+ // Determine the origin of the new window. We start with the current mouse
+ // position:
+ gfx::Point new_window_origin(views::Screen::GetCursorScreenPoint());
+ // Subtract the offset of the mouse pointer from the tab top left when the
+ // drag action began.
+ new_window_origin.Offset(-mouse_tab_offset_.x(), -mouse_tab_offset_.y());
+ // Subtract the offset of the tab's current position from the window.
+ gfx::Point tab_window_origin;
+ View::ConvertPointToWidget(tab, &tab_window_origin);
+ new_window_origin.Offset(-tab_window_origin.x(), -tab_window_origin.y());
+
+ // The new window is created with the same size as the source window but at
+ // the origin calculated above.
+ gfx::Rect new_window_bounds = GetWindow()->GetBounds();
+ new_window_bounds.set_origin(new_window_origin);
+
+ model_->DetachTabAt(index, new_window_bounds, tab_bounds);
+}
+
+void TabStrip2::StartDragTabImpl(int index, const gfx::Rect& tab_bounds) {
+ SetDraggedTabBounds(index, tab_bounds);
+ gfx::Rect tab_local_bounds(tab_bounds);
+ tab_local_bounds.set_origin(gfx::Point());
+ GetWidget()->GenerateMousePressedForView(GetTabAt(index),
+ tab_local_bounds.CenterPoint());
+}
+
+int TabStrip2::GetInternalIndex(int public_index) const {
+ std::vector<Tab2*>::const_iterator it;
+ int internal_index = public_index;
+ int valid_tab_count = 0;
+ for (it = tabs_.begin(); it != tabs_.end(); ++it) {
+ if (public_index >= valid_tab_count)
+ break;
+ if ((*it)->removing())
+ ++internal_index;
+ else
+ ++valid_tab_count;
+ }
+ return internal_index;
+}
diff --git a/chrome/browser/views/tabs/tab_strip_2.h b/chrome/browser/views/tabs/tab_strip_2.h
new file mode 100644
index 0000000..7bbce90
--- /dev/null
+++ b/chrome/browser/views/tabs/tab_strip_2.h
@@ -0,0 +1,163 @@
+// 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_STRIP_2_H_
+#define CHROME_BROWSER_VIEWS_TABS_TAB_STRIP_2_H_
+
+#include <vector>
+
+#include "base/task.h"
+#include "chrome/browser/views/tabs/tab_2.h"
+#include "views/animator.h"
+#include "views/view.h"
+
+namespace gfx {
+class Canvas;
+}
+
+// An interface implemented by an object that provides state for objects in the
+// TabStrip2. This object is never owned by the TabStrip2.
+// TODO(beng): maybe TabStrip2Delegate?
+class TabStrip2Model {
+ public:
+ // Get presentation state for a particular Tab2.
+ virtual string16 GetTitle(int index) const = 0;
+ virtual bool IsSelected(int index) const = 0;
+
+ // The Tab2 at the specified index has been selected.
+ virtual void SelectTabAt(int index) = 0;
+
+ // Returns true if Tab2s can be dragged.
+ virtual bool CanDragTabs() const = 0;
+
+ // The Tab2 at the specified source index has moved to the specified
+ // destination index.
+ virtual void MoveTabAt(int index, int to_index) = 0;
+
+ // The Tab2 at the specified index was detached. |window_bounds| are the
+ // screen bounds of the current window, and |tab_bounds| are the bounds of the
+ // Tab2 in screen coordinates.
+ virtual void DetachTabAt(int index,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& tab_bounds) = 0;
+};
+
+// A TabStrip view.
+class TabStrip2 : public views::View,
+ public Tab2Model,
+ public views::AnimatorDelegate {
+ public:
+ explicit TabStrip2(TabStrip2Model* model);
+ virtual ~TabStrip2();
+
+ // Returns true if the new TabStrip is enabled.
+ static bool Enabled();
+
+ // API for adding, removing, selecting and moving tabs around.
+ void AddTabAt(int index);
+ void RemoveTabAt(int index, Tab2Model* removing_model);
+ void SelectTabAt(int index);
+ void MoveTabAt(int index, int to_index);
+
+ int GetTabCount() const;
+ Tab2* GetTabAt(int index) const;
+ int GetTabIndex(Tab2* tab) const;
+
+ // Returns the index to insert an item into the TabStrip at for a drop at the
+ // specified point in TabStrip coordinates.
+ int GetInsertionIndexForPoint(const gfx::Point& point) const;
+
+ // Returns the bounds of the Tab2 under |screen_point| in screen coordinates.
+ gfx::Rect GetDraggedTabScreenBounds(const gfx::Point& screen_point);
+
+ // Sets the bounds of the Tab2 at the specified index to |tab_bounds|.
+ void SetDraggedTabBounds(int index, const gfx::Rect& tab_bounds);
+
+ // Animates the dragged Tab2 to the location implied by its index in the
+ // model.
+ void SendDraggedTabHome();
+
+ // Continue a drag operation on the Tab2 at the specified index.
+ void ResumeDraggingTab(int index, const gfx::Rect& tab_bounds);
+
+ // Returns true if the mouse pointer at the specified point (screen bounds)
+ // constitutes a rearrange rather than a detach.
+ static bool IsDragRearrange(TabStrip2* tabstrip,
+ const gfx::Point& screen_point);
+
+ // Overridden from Tab2Model:
+ virtual string16 GetTitle(Tab2* tab) const;
+ virtual bool IsSelected(Tab2* tab) const;
+ virtual void SelectTab(Tab2* tab);
+ virtual void CaptureDragInfo(Tab2* tab, const views::MouseEvent& drag_event);
+ virtual bool DragTab(Tab2* tab, const views::MouseEvent& drag_event);
+ virtual void DragEnded(Tab2* tab);
+ virtual views::AnimatorDelegate* AsAnimatorDelegate();
+
+ // Overridden from views::View:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+ virtual void Paint(gfx::Canvas* canvas);
+
+ private:
+ virtual void PaintChildren(gfx::Canvas* canvas);
+
+ // Overridden from views::AnimatorDelegate:
+ virtual views::View* GetClampedView(views::View* host);
+ virtual void AnimationCompletedForHost(View* host);
+
+ // Specifies what kind of TabStrip2 operation initiated the Layout, so the
+ // heuristic can adapt accordingly.
+ enum LayoutSource {
+ LS_TAB_ADD,
+ LS_TAB_REMOVE,
+ LS_TAB_SELECT,
+ LS_TAB_DRAG_REORDER,
+ LS_TAB_DRAG_NORMALIZE,
+ LS_OTHER
+ };
+
+ // Returns the animation directions for the specified layout source event.
+ int GetAnimateFlagsForLayoutSource(LayoutSource source) const;
+
+ // Lays out the contents of the TabStrip2.
+ void LayoutImpl(LayoutSource source);
+
+ // Execute the tab detach operation after a return to the message loop.
+ void DragDetachTabImpl(Tab2* tab, int index);
+
+ // Execute the drag initiation operation after a return to the message loop.
+ void StartDragTabImpl(int index, const gfx::Rect& tab_bounds);
+
+ // Returns the index into |tabs_| that corresponds to a publicly visible
+ // index. The index spaces are different since when a tab is closed we retain
+ // the tab in the presentation (and this our tab vector) until the tab has
+ // animated itself out of existence, but the clients of our API expect that
+ // index to be synchronously removed.
+ int GetInternalIndex(int public_index) const;
+
+ TabStrip2Model* model_;
+
+ // A vector of our Tabs. Stored separately from the child views, the child
+ // view order does not map directly to the presentation order, and because
+ // we can have child views that aren't Tab2s.
+ std::vector<Tab2*> tabs_;
+
+ // The position of the mouse relative to the widget when drag information was
+ // captured.
+ gfx::Point mouse_tab_offset_;
+
+ // The last position of the mouse along the horizontal axis of the TabStrip
+ // prior to the current drag event. Used to determine that the mouse has moved
+ // beyond the minimum horizontal threshold to initiate a drag operation.
+ int last_move_screen_x_;
+
+ // Factories to help break up work and avoid nesting message loops.
+ ScopedRunnableMethodFactory<TabStrip2> detach_factory_;
+ ScopedRunnableMethodFactory<TabStrip2> drag_start_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabStrip2);
+};
+
+#endif // #ifndef CHROME_BROWSER_VIEWS_TABS_TAB_STRIP_2_H_
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 5a95508..e801ae3 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1712,8 +1712,12 @@
'browser/views/tabs/native_view_photobooth_gtk.h',
'browser/views/tabs/native_view_photobooth_win.cc',
'browser/views/tabs/native_view_photobooth_win.h',
+ 'browser/views/tabs/browser_tab_strip.cc',
+ 'browser/views/tabs/browser_tab_strip.h',
'browser/views/tabs/tab.cc',
'browser/views/tabs/tab.h',
+ 'browser/views/tabs/tab_2.cc',
+ 'browser/views/tabs/tab_2.h',
'browser/views/tabs/tab_overview_cell.cc',
'browser/views/tabs/tab_overview_cell.h',
'browser/views/tabs/tab_overview_container.cc',
@@ -1732,6 +1736,8 @@
'browser/views/tabs/tab_renderer.h',
'browser/views/tabs/tab_strip.cc',
'browser/views/tabs/tab_strip.h',
+ 'browser/views/tabs/tab_strip_2.cc',
+ 'browser/views/tabs/tab_strip_2.h',
'browser/views/task_manager_view.cc',
'browser/views/theme_helpers.cc',
'browser/views/theme_helpers.h',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index be103f9..453426d 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -532,4 +532,7 @@ const wchar_t kEnableMonitorProfile[] = L"enable-monitor-profile";
// still experimental.
const wchar_t kEnableXSSAuditor[] = L"enable-xss-auditor";
+// Enables the new Tabstrip on Windows.
+const wchar_t kEnableTabtastic2[] = L"enable-tabtastic2";
+
} // namespace switches
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index e4b40d8..9ba2999 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -203,6 +203,8 @@ extern const wchar_t kEnableMonitorProfile[];
extern const wchar_t kEnableXSSAuditor[];
+extern const wchar_t kEnableTabtastic2[];
+
} // namespace switches
#endif // CHROME_COMMON_CHROME_SWITCHES_H_
diff --git a/views/animator.cc b/views/animator.cc
new file mode 100644
index 0000000..2b2a7db
--- /dev/null
+++ b/views/animator.cc
@@ -0,0 +1,151 @@
+// 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 "views/animator.h"
+
+#include <algorithm>
+
+#include "app/slide_animation.h"
+#include "views/view.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// Animator, public:
+
+Animator::Animator(View* host)
+ : host_(host),
+ delegate_(NULL),
+ direction_(ANIMATE_NONE) {
+ InitAnimation();
+}
+
+Animator::Animator(View* host, AnimatorDelegate* delegate)
+ : host_(host),
+ animation_(NULL),
+ delegate_(delegate),
+ direction_(ANIMATE_NONE) {
+ InitAnimation();
+}
+
+Animator::~Animator() {
+ // Explicitly NULL the delegate so we don't call back through to the delegate
+ // when the animation is stopped. The Animator is designed to be owned by a
+ // View and at this point the View is dust.
+ delegate_ = NULL;
+ animation_->Stop();
+}
+
+bool Animator::IsAnimating() const {
+ return animation_->IsAnimating();
+}
+
+void Animator::AnimateToBounds(const gfx::Rect& bounds, int direction) {
+ direction_ = direction;
+ start_bounds_ = host_->bounds();
+ target_bounds_ = bounds;
+
+ // Stop any running animation before we have a chance to return.
+ animation_->Stop();
+
+ if (bounds == host_->bounds())
+ return;
+
+ if (direction_ == ANIMATE_NONE) {
+ host_->SetBounds(bounds);
+ return;
+ }
+
+ if (direction_ & ANIMATE_X) {
+ if (direction_ & ANIMATE_CLAMP)
+ start_bounds_.set_x(GetClampedX());
+ } else {
+ start_bounds_.set_x(target_bounds_.x());
+ }
+
+ if (direction_ & ANIMATE_Y) {
+ if (direction_ & ANIMATE_CLAMP)
+ start_bounds_.set_y(GetClampedY());
+ } else {
+ start_bounds_.set_y(target_bounds_.y());
+ }
+
+ if (!(direction_ & ANIMATE_WIDTH))
+ start_bounds_.set_width(target_bounds_.width());
+ if (!(direction_ & ANIMATE_HEIGHT))
+ start_bounds_.set_height(target_bounds_.height());
+
+ // Make sure the host view has the start bounds to avoid a flicker.
+ host_->SetBounds(start_bounds_);
+
+ // Start the animation from the beginning.
+ animation_->Reset(0.0);
+ animation_->Show();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Animator, AnimationDelegate:
+
+void Animator::AnimationEnded(const Animation* animation) {
+ // |delegate_| could be NULL if we're called back from the destructor.
+ if (delegate_)
+ delegate_->AnimationCompletedForHost(host_);
+}
+
+void Animator::AnimationProgressed(const Animation* animation) {
+ int delta_x = target_bounds_.x() - start_bounds_.x();
+ int delta_y = target_bounds_.y() - start_bounds_.y();
+ int delta_width = target_bounds_.width() - start_bounds_.width();
+ int delta_height = target_bounds_.height() - start_bounds_.height();
+
+ // The current frame's position and size is the animation's progress percent
+ // multiplied by the delta between the start and target position/size...
+ double cv = animation_->GetCurrentValue();
+ int frame_x = start_bounds_.x() + static_cast<int>(delta_x * cv);
+ int frame_y = start_bounds_.y() + static_cast<int>(delta_y * cv);
+ // ... except for clamped positions, which remain clamped at the right/bottom
+ // edge of the previous view in the layout flow.
+ if (direction_ & ANIMATE_CLAMP && direction_ & ANIMATE_X)
+ frame_x = GetClampedX();
+ if (direction_ & ANIMATE_CLAMP && direction_ & ANIMATE_Y)
+ frame_y = GetClampedY();
+ int frame_width = start_bounds_.width() + static_cast<int>(delta_width * cv);
+ int frame_height =
+ start_bounds_.height() + static_cast<int>(delta_height * cv);
+ host_->SetBounds(frame_x, frame_y, frame_width, frame_height);
+ host_->GetParent()->SchedulePaint();
+}
+
+void Animator::AnimationCanceled(const Animation* animation) {
+ AnimationEnded(animation);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Animator, private:
+
+void Animator::InitAnimation() {
+ animation_.reset(new SlideAnimation(this));
+ animation_->SetSlideDuration(150);
+ animation_->SetTweenType(SlideAnimation::EASE_OUT);
+}
+
+int Animator::GetClampedX() const {
+ if (delegate_ && direction_ & ANIMATE_CLAMP && direction_ & ANIMATE_X) {
+ View* previous_view = delegate_->GetClampedView(host_);
+ if (previous_view)
+ return previous_view->bounds().right();
+ }
+ return host_->x();
+}
+
+int Animator::GetClampedY() const {
+ if (delegate_ && direction_ & ANIMATE_CLAMP && direction_ & ANIMATE_Y) {
+ View* previous_view = delegate_->GetClampedView(host_);
+ if (previous_view)
+ return previous_view->bounds().bottom();
+ }
+ return host_->y();
+}
+
+} // namespace views
diff --git a/views/animator.h b/views/animator.h
new file mode 100644
index 0000000..2719fd6
--- /dev/null
+++ b/views/animator.h
@@ -0,0 +1,110 @@
+// 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 VIEWS_ANIMATOR_H_
+#define VIEWS_ANIMATOR_H_
+
+#include <xutility>
+
+#include "app/animation.h"
+#include "base/gfx/rect.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+
+class SlideAnimation;
+
+////////////////////////////////////////////////////////////////////////////////
+// ALERT!
+//
+// This API is preliminary and subject to change. Talk to beng before using it!
+//
+
+namespace views {
+
+class View;
+
+class AnimatorDelegate {
+ public:
+ // Returns the view in the visual layout whose trailing edge the view that
+ // hosts an animator should be clamped to during animations, when
+ // ANIMATE_CLAMP is specified in combination with ANIMATE_X or ANIMATE_Y.
+ virtual View* GetClampedView(View* host) = 0;
+
+ // Notifies the delegate that the active animation running for |host| has
+ // completed.
+ virtual void AnimationCompletedForHost(View* host) = 0;
+};
+
+// An animator is an object that can animate actions on a host view. Once
+// created, an Animator is typically owned by its host view.
+class Animator : public AnimationDelegate {
+ public:
+ enum BoundsChangeFlags {
+ ANIMATE_NONE = 0x0, // Don't animate anything... o_O
+ ANIMATE_X = 0x1, // Animate the host view's x position
+ ANIMATE_Y = 0x2, // Animate the host view's y position
+ ANIMATE_WIDTH = 0x4, // Animate the host view's width
+ ANIMATE_HEIGHT = 0x8, // Animate the host view's height
+ ANIMATE_CLAMP = 0x10 // Clamp the host view's x or y position to the
+ // trailing edge of the view returned by
+ // AnimatorDelegate::GetClampedView.
+ };
+
+ // Creates the animator for the specified host view. Optionally an
+ // AnimationContext can be provided to animate multiple views from a single
+ // animation.
+ explicit Animator(View* host);
+ Animator(View* host, AnimatorDelegate* delegate);
+ virtual ~Animator();
+
+ // Returns true if the animator is currently animating.
+ bool IsAnimating() const;
+
+ // Moves/sizes the host view to the specified bounds. |direction| is a
+ // combination of the above flags indicating what aspects of the bounds should
+ // be animated.
+ void AnimateToBounds(const gfx::Rect& bounds, int direction);
+ void AnimateToBounds(int x, int y, int width, int height, int direction) {
+ AnimateToBounds(gfx::Rect(x, y, std::max(0, width), std::max(0, height)),
+ direction);
+ }
+
+ // Overridden from AnimationDelegate:
+ virtual void AnimationEnded(const Animation* animation);
+ virtual void AnimationProgressed(const Animation* animation);
+ virtual void AnimationCanceled(const Animation* animation);
+
+ private:
+ void InitAnimation();
+
+ // Get the current X/Y position of the host view, clamped to the right edge of
+ // the previous view in the visual layout, if applicable (See
+ // AnimatorDelegate for more info).
+ int GetClampedX() const;
+ int GetClampedY() const;
+
+ // The view that this animator is attached to.
+ View* host_;
+
+ // Start and end bounds for the animation.
+ gfx::Rect start_bounds_;
+ gfx::Rect target_bounds_;
+
+ // The animation used by this animator.
+ scoped_ptr<SlideAnimation> animation_;
+
+ // A delegate object that provides information about surrounding views.
+ // Will be NULL during this class' destructor.
+ AnimatorDelegate* delegate_;
+
+ // Some combination of BoundsChangeFlags indicating the type of bounds change
+ // the host view is subject to.
+ int direction_;
+
+ DISALLOW_COPY_AND_ASSIGN(Animator);
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_ANIMATOR_H_
diff --git a/views/views.gyp b/views/views.gyp
index abb790f..4504f99 100644
--- a/views/views.gyp
+++ b/views/views.gyp
@@ -58,6 +58,8 @@
'accessibility/view_accessibility.h',
'accessibility/view_accessibility_wrapper.cc',
'accessibility/view_accessibility_wrapper.h',
+ 'animator.cc',
+ 'animator.h',
'background.cc',
'background.h',
'border.cc',
diff --git a/views/widget/widget.h b/views/widget/widget.h
index 9c4c5ef..0566b0b 100644
--- a/views/widget/widget.h
+++ b/views/widget/widget.h
@@ -20,6 +20,7 @@ class Accelerator;
class FocusManager;
class RootView;
class TooltipManager;
+class View;
class Window;
////////////////////////////////////////////////////////////////////////////////
@@ -96,6 +97,11 @@ class Widget {
return NULL;
}
+ // Starts a drag operation for the specified view. |point| is a position in
+ // |view| coordinates that the drag was initiated from.
+ virtual void GenerateMousePressedForView(View* view,
+ const gfx::Point& point) = 0;
+
// Returns the accelerator given a command id. Returns false if there is
// no accelerator associated with a given id, which is a common condition.
virtual bool GetAccelerator(int cmd_id,
diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc
index 3bec281..c4ea3b5 100644
--- a/views/widget/widget_gtk.cc
+++ b/views/widget/widget_gtk.cc
@@ -368,6 +368,11 @@ bool WidgetGtk::IsActive() const {
return gtk_window_is_active(GTK_WINDOW(widget_));
}
+void WidgetGtk::GenerateMousePressedForView(View* view,
+ const gfx::Point& point) {
+ NOTIMPLEMENTED();
+}
+
TooltipManager* WidgetGtk::GetTooltipManager() {
return tooltip_manager_.get();
}
diff --git a/views/widget/widget_gtk.h b/views/widget/widget_gtk.h
index 58d8895..28cf8be 100644
--- a/views/widget/widget_gtk.h
+++ b/views/widget/widget_gtk.h
@@ -92,6 +92,8 @@ class WidgetGtk : public Widget, public MessageLoopForUI::Observer {
virtual Widget* GetRootWidget() const;
virtual bool IsVisible() const;
virtual bool IsActive() const;
+ virtual void GenerateMousePressedForView(View* view,
+ const gfx::Point& point);
virtual TooltipManager* GetTooltipManager();
virtual bool GetAccelerator(int cmd_id, Accelerator* accelerator);
virtual Window* GetWindow();
diff --git a/views/widget/widget_win.cc b/views/widget/widget_win.cc
index 8d39991..ed58ae9 100644
--- a/views/widget/widget_win.cc
+++ b/views/widget/widget_win.cc
@@ -392,6 +392,14 @@ bool WidgetWin::IsActive() const {
return win_util::IsWindowActive(GetNativeView());
}
+void WidgetWin::GenerateMousePressedForView(View* view,
+ const gfx::Point& point) {
+ gfx::Point point_in_widget(point);
+ View::ConvertPointToWidget(view, &point_in_widget);
+ root_view_->SetMouseHandler(view);
+ ProcessMousePressed(point_in_widget.ToPOINT(), MK_LBUTTON, false, false);
+}
+
TooltipManager* WidgetWin::GetTooltipManager() {
return tooltip_manager_.get();
}
diff --git a/views/widget/widget_win.h b/views/widget/widget_win.h
index 33c247e..d3665d2 100644
--- a/views/widget/widget_win.h
+++ b/views/widget/widget_win.h
@@ -159,6 +159,7 @@ class WidgetWin : public Widget,
MSG_WM_ENDSESSION(OnEndSession)
MSG_WM_ENTERSIZEMOVE(OnEnterSizeMove)
MSG_WM_EXITMENULOOP(OnExitMenuLoop)
+ MSG_WM_EXITSIZEMOVE(OnExitSizeMove)
MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo)
MSG_WM_HSCROLL(OnHScroll)
MSG_WM_INITMENU(OnInitMenu)
@@ -225,6 +226,8 @@ class WidgetWin : public Widget,
virtual Widget* GetRootWidget() const;
virtual bool IsVisible() const;
virtual bool IsActive() const;
+ virtual void GenerateMousePressedForView(View* view,
+ const gfx::Point& point);
virtual TooltipManager* GetTooltipManager();
virtual ThemeProvider* GetThemeProvider() const;
virtual Window* GetWindow();
@@ -371,10 +374,11 @@ class WidgetWin : public Widget,
}
virtual void OnEndSession(BOOL ending, UINT logoff) { SetMsgHandled(FALSE); }
virtual void OnEnterSizeMove() { SetMsgHandled(FALSE); }
+ virtual LRESULT OnEraseBkgnd(HDC dc);
virtual void OnExitMenuLoop(BOOL is_track_popup_menu) {
SetMsgHandled(FALSE);
}
- virtual LRESULT OnEraseBkgnd(HDC dc);
+ virtual void OnExitSizeMove() { SetMsgHandled(FALSE); }
virtual LRESULT OnGetObject(UINT uMsg, WPARAM w_param, LPARAM l_param);
virtual void OnGetMinMaxInfo(MINMAXINFO* minmax_info) {
SetMsgHandled(FALSE);