diff options
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); |