summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);