From 27239fb2b868ef6aff2d9d13932ba5debe87e9c7 Mon Sep 17 00:00:00 2001 From: "ben@chromium.org" Date: Fri, 19 Mar 2010 22:22:56 +0000 Subject: Allow dynamic switching in and out of sidetabs mode. BUG=none TEST=none Review URL: http://codereview.chromium.org/1001003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42156 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/app/generated_resources.grd | 3 + chrome/browser/browser.cc | 12 ++- chrome/browser/browser.h | 5 + chrome/browser/browser_window.h | 3 + chrome/browser/cocoa/browser_window_cocoa.h | 1 + chrome/browser/cocoa/browser_window_cocoa.mm | 4 + chrome/browser/gtk/browser_window_gtk.cc | 4 + chrome/browser/gtk/browser_window_gtk.h | 1 + chrome/browser/tab_menu_model.cc | 5 + chrome/browser/tabs/tab_strip_model.cc | 24 ++++- chrome/browser/tabs/tab_strip_model.h | 11 +++ chrome/browser/views/frame/browser_frame.h | 8 +- chrome/browser/views/frame/browser_frame_gtk.cc | 6 +- chrome/browser/views/frame/browser_frame_gtk.h | 2 +- chrome/browser/views/frame/browser_frame_win.cc | 15 ++- chrome/browser/views/frame/browser_frame_win.h | 2 +- chrome/browser/views/frame/browser_view.cc | 43 ++++++--- chrome/browser/views/frame/browser_view.h | 10 +- chrome/browser/views/frame/browser_view_layout.cc | 6 +- .../views/frame/glass_browser_frame_view.cc | 11 ++- .../views/tabs/browser_tab_strip_controller.cc | 102 +++++++++++++++++++++ .../views/tabs/browser_tab_strip_controller.h | 17 ++++ chrome/browser/views/tabs/side_tab.cc | 26 +++++- chrome/browser/views/tabs/side_tab.h | 9 ++ chrome/browser/views/tabs/side_tab_strip.cc | 10 +- chrome/browser/views/tabs/side_tab_strip.h | 5 +- chrome/browser/views/tabs/side_tab_strip_model.h | 9 ++ chrome/browser/views/tabs/tab.cc | 6 +- chrome/browser/views/tabs/tab.h | 4 + chrome/browser/views/tabs/tab_strip.cc | 24 ++++- chrome/browser/views/tabs/tab_strip.h | 7 ++ chrome/test/test_browser_window.h | 1 + 32 files changed, 337 insertions(+), 59 deletions(-) diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 02486fd..7d81442 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4344,6 +4344,9 @@ Keep your key file in a safe place. You will need it to create new versions of y Bookmark all tabs... + + Use side tabs + diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index 378d86e..42e0834 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -190,6 +190,7 @@ Browser::Browser(Type type, Profile* profile) encoding_auto_detect_.Init(prefs::kWebKitUsesUniversalDetector, profile_->GetPrefs(), NULL); + use_vertical_tabs_.Init(prefs::kUseVerticalTabs, profile_->GetPrefs(), this); } Browser::~Browser() { @@ -1566,7 +1567,7 @@ void Browser::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterBooleanPref(prefs::kWebAppCreateOnDesktop, true); prefs->RegisterBooleanPref(prefs::kWebAppCreateInAppsMenu, true); prefs->RegisterBooleanPref(prefs::kWebAppCreateInQuickLaunchBar, true); - prefs->RegisterBooleanPref(prefs::kUseVerticalTabs, true); + prefs->RegisterBooleanPref(prefs::kUseVerticalTabs, false); prefs->RegisterBooleanPref(prefs::kEnableTranslate, true); } @@ -1994,6 +1995,15 @@ void Browser::BookmarkAllTabs() { BookmarkEditor::SHOW_TREE, NULL); } +bool Browser::UseVerticalTabs() const { + return use_vertical_tabs_.GetValue(); +} + +void Browser::ToggleUseVerticalTabs() { + use_vertical_tabs_.SetValue(!UseVerticalTabs()); + window()->ToggleTabStripMode(); +} + /////////////////////////////////////////////////////////////////////////////// // Browser, TabStripModelObserver implementation: diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h index 51470f7..e98966f 100644 --- a/chrome/browser/browser.h +++ b/chrome/browser/browser.h @@ -604,6 +604,8 @@ class Browser : public TabStripModelDelegate, virtual bool CanCloseContentsAt(int index); virtual bool CanBookmarkAllTabs() const; virtual void BookmarkAllTabs(); + virtual bool UseVerticalTabs() const; + virtual void ToggleUseVerticalTabs(); // Overridden from TabStripModelObserver: virtual void TabInsertedAt(TabContents* contents, @@ -944,6 +946,9 @@ class Browser : public TabStripModelDelegate, // from a TabContents. Currently, only one pending action is allowed. WebAppAction pending_web_app_action_; + // Tracks the display mode of the tabstrip. + mutable BooleanPrefMember use_vertical_tabs_; + DISALLOW_COPY_AND_ASSIGN(Browser); }; diff --git a/chrome/browser/browser_window.h b/chrome/browser/browser_window.h index a4ff572..1d3501a 100644 --- a/chrome/browser/browser_window.h +++ b/chrome/browser/browser_window.h @@ -310,6 +310,9 @@ class BrowserWindow { virtual void Copy() = 0; virtual void Paste() = 0; + // Switches between available tabstrip display modes. + virtual void ToggleTabStripMode() = 0; + // Construct a BrowserWindow implementation for the specified |browser|. static BrowserWindow* CreateBrowserWindow(Browser* browser); diff --git a/chrome/browser/cocoa/browser_window_cocoa.h b/chrome/browser/cocoa/browser_window_cocoa.h index b0ace80..86a5e57 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.h +++ b/chrome/browser/cocoa/browser_window_cocoa.h @@ -102,6 +102,7 @@ class BrowserWindowCocoa : public BrowserWindow, virtual void Cut(); virtual void Copy(); virtual void Paste(); + virtual void ToggleTabStripMode(); // Overridden from NotificationObserver virtual void Observe(NotificationType type, diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm index 01d592e..e9d0a31 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/cocoa/browser_window_cocoa.mm @@ -540,6 +540,10 @@ void BrowserWindowCocoa::Paste() { [NSApp sendAction:@selector(paste:) to:nil from:nil]; } +void BrowserWindowCocoa::ToggleTabStripMode() { + NOTIMPLEMENTED(); +} + void BrowserWindowCocoa::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc index 873ad44..e79da68 100644 --- a/chrome/browser/gtk/browser_window_gtk.cc +++ b/chrome/browser/gtk/browser_window_gtk.cc @@ -1044,6 +1044,10 @@ void BrowserWindowGtk::Paste() { DoCutCopyPaste(this, &RenderViewHost::Paste, "paste-clipboard"); } +void BrowserWindowGtk::ToggleTabStripMode() { + NOTIMPLEMENTED(); +} + void BrowserWindowGtk::ConfirmBrowserCloseWithPendingDownloads() { new DownloadInProgressDialogGtk(browser()); } diff --git a/chrome/browser/gtk/browser_window_gtk.h b/chrome/browser/gtk/browser_window_gtk.h index f24e094..db1aba2 100644 --- a/chrome/browser/gtk/browser_window_gtk.h +++ b/chrome/browser/gtk/browser_window_gtk.h @@ -120,6 +120,7 @@ class BrowserWindowGtk : public BrowserWindow, virtual void Cut(); virtual void Copy(); virtual void Paste(); + virtual void ToggleTabStripMode(); // Overridden from NotificationObserver: virtual void Observe(NotificationType type, diff --git a/chrome/browser/tab_menu_model.cc b/chrome/browser/tab_menu_model.cc index f0fe251..2845240 100644 --- a/chrome/browser/tab_menu_model.cc +++ b/chrome/browser/tab_menu_model.cc @@ -43,4 +43,9 @@ void TabMenuModel::Build() { AddItemWithStringId(TabStripModel::CommandRestoreTab, IDS_RESTORE_TAB); AddItemWithStringId(TabStripModel::CommandBookmarkAllTabs, IDS_TAB_CXMENU_BOOKMARK_ALL_TABS); +#if defined(OS_WIN) + AddSeparator(); + AddCheckItemWithStringId(TabStripModel::CommandUseVerticalTabs, + IDS_TAB_CXMENU_USE_VERTICAL_TABS); +#endif } diff --git a/chrome/browser/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc index 2fba4b5..dc9b968 100644 --- a/chrome/browser/tabs/tab_strip_model.cc +++ b/chrome/browser/tabs/tab_strip_model.cc @@ -574,11 +574,25 @@ bool TabStripModel::IsContextMenuCommandEnabled( return delegate_->CanRestoreTab(); case CommandTogglePinned: return true; - case CommandBookmarkAllTabs: { + case CommandBookmarkAllTabs: return delegate_->CanBookmarkAllTabs(); - } + case CommandUseVerticalTabs: + return true; + default: + NOTREACHED(); + } + return false; +} + +bool TabStripModel::IsContextMenuCommandChecked( + int context_index, + ContextMenuCommand command_id) const { + switch (command_id) { + case CommandUseVerticalTabs: + return delegate()->UseVerticalTabs(); default: NOTREACHED(); + break; } return false; } @@ -656,6 +670,12 @@ void TabStripModel::ExecuteContextMenuCommand( delegate_->BookmarkAllTabs(); break; } + case CommandUseVerticalTabs: { + UserMetrics::RecordAction("TabContextMenu_UseVerticalTabs", profile_); + + delegate()->ToggleUseVerticalTabs(); + break; + } default: NOTREACHED(); } diff --git a/chrome/browser/tabs/tab_strip_model.h b/chrome/browser/tabs/tab_strip_model.h index a4c3641..1f5214f 100644 --- a/chrome/browser/tabs/tab_strip_model.h +++ b/chrome/browser/tabs/tab_strip_model.h @@ -223,6 +223,12 @@ class TabStripModelDelegate { // Creates a bookmark folder containing a bookmark for all open tabs. virtual void BookmarkAllTabs() = 0; + + // Returns true if the vertical tabstrip presentation should be used. + virtual bool UseVerticalTabs() const = 0; + + // Toggles the use of the vertical tabstrip. + virtual void ToggleUseVerticalTabs() = 0; }; //////////////////////////////////////////////////////////////////////////////// @@ -547,6 +553,7 @@ class TabStripModel : public NotificationObserver { CommandRestoreTab, CommandTogglePinned, CommandBookmarkAllTabs, + CommandUseVerticalTabs, CommandLast }; @@ -554,6 +561,10 @@ class TabStripModel : public NotificationObserver { bool IsContextMenuCommandEnabled(int context_index, ContextMenuCommand command_id) const; + // Returns true if the specified command is checked. + bool IsContextMenuCommandChecked(int context_index, + ContextMenuCommand command_id) const; + // Performs the action associated with the specified command for the given // TabStripModel index |context_index|. void ExecuteContextMenuCommand(int context_index, diff --git a/chrome/browser/views/frame/browser_frame.h b/chrome/browser/views/frame/browser_frame.h index fd94d51..392200d 100644 --- a/chrome/browser/views/frame/browser_frame.h +++ b/chrome/browser/views/frame/browser_frame.h @@ -41,10 +41,6 @@ class BrowserFrame { // construction. virtual views::Window* GetWindow() = 0; - // Notification that the tab strip has been created. This should let the - // BrowserRootView know about it so it can enable drag and drop. - virtual void TabStripCreated(BaseTabStrip* tabstrip) = 0; - // Determine the distance of the left edge of the minimize button from the // left edge of the window. Used in our Non-Client View's Layout. virtual int GetMinimizeButtonOffset() const = 0; @@ -74,6 +70,10 @@ class BrowserFrame { // calls this method _after_ the TabStrip has painted itself so the shadow is // rendered above the tabs. virtual void PaintTabStripShadow(gfx::Canvas* canvas) = 0; + + // Notifies the frame that the tab strip display mode changed so it can update + // its frame treatment if necessary. + virtual void TabStripDisplayModeChanged() = 0; }; #endif // CHROME_BROWSER_VIEWS_FRAME_BROWSER_FRAME_H_ diff --git a/chrome/browser/views/frame/browser_frame_gtk.cc b/chrome/browser/views/frame/browser_frame_gtk.cc index 9e1699c..97fbedc 100644 --- a/chrome/browser/views/frame/browser_frame_gtk.cc +++ b/chrome/browser/views/frame/browser_frame_gtk.cc @@ -102,9 +102,6 @@ views::Window* BrowserFrameGtk::GetWindow() { return this; } -void BrowserFrameGtk::TabStripCreated(BaseTabStrip* tabstrip) { -} - int BrowserFrameGtk::GetMinimizeButtonOffset() const { NOTIMPLEMENTED(); return 0; @@ -139,6 +136,9 @@ views::View* BrowserFrameGtk::GetFrameView() const { void BrowserFrameGtk::PaintTabStripShadow(gfx::Canvas* canvas) { } +void BrowserFrameGtk::TabStripDisplayModeChanged() { +} + ThemeProvider* BrowserFrameGtk::GetThemeProvider() const { return profile_->GetThemeProvider(); } diff --git a/chrome/browser/views/frame/browser_frame_gtk.h b/chrome/browser/views/frame/browser_frame_gtk.h index 93330c1..f8e8f2a 100644 --- a/chrome/browser/views/frame/browser_frame_gtk.h +++ b/chrome/browser/views/frame/browser_frame_gtk.h @@ -28,7 +28,6 @@ class BrowserFrameGtk : public BrowserFrame, // Overridden from BrowserFrame: virtual views::Window* GetWindow(); - virtual void TabStripCreated(BaseTabStrip* tabstrip); virtual int GetMinimizeButtonOffset() const; virtual gfx::Rect GetBoundsForTabStrip(BaseTabStrip* tabstrip) const; virtual void UpdateThrobber(bool running); @@ -37,6 +36,7 @@ class BrowserFrameGtk : public BrowserFrame, virtual bool AlwaysUseNativeFrame() const; virtual views::View* GetFrameView() const; virtual void PaintTabStripShadow(gfx::Canvas* canvas); + virtual void TabStripDisplayModeChanged(); // Overridden from views::Widget: virtual ThemeProvider* GetThemeProvider() const; diff --git a/chrome/browser/views/frame/browser_frame_win.cc b/chrome/browser/views/frame/browser_frame_win.cc index 696e109..4578829 100644 --- a/chrome/browser/views/frame/browser_frame_win.cc +++ b/chrome/browser/views/frame/browser_frame_win.cc @@ -69,9 +69,6 @@ views::Window* BrowserFrameWin::GetWindow() { return this; } -void BrowserFrameWin::TabStripCreated(BaseTabStrip* tabstrip) { -} - int BrowserFrameWin::GetMinimizeButtonOffset() const { TITLEBARINFOEX titlebar_info; titlebar_info.cbSize = sizeof(TITLEBARINFOEX); @@ -127,6 +124,14 @@ void BrowserFrameWin::PaintTabStripShadow(gfx::Canvas* canvas) { browser_frame_view_->PaintTabStripShadow(canvas); } +void BrowserFrameWin::TabStripDisplayModeChanged() { + UpdateDWMFrame(); + // We need to relayout only after the window frame has had a chance to update + // the size/position of its controls (e.g. minimize/maximize/close) so that + // the tabstrip can layout at its correct bounds. + GetRootView()->Layout(); +} + /////////////////////////////////////////////////////////////////////////////// // BrowserFrame, views::WindowWin overrides: @@ -294,7 +299,7 @@ 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_->UsingSideTabs()) { + if (browser_view_->UseVerticalTabs()) { margins.cxLeftWidth += GetBoundsForTabStrip(browser_view_->tabstrip()).right(); margins.cyTopHeight += GetSystemMetrics(SM_CYSIZEFRAME); @@ -309,7 +314,7 @@ void BrowserFrameWin::UpdateDWMFrame() { DwmExtendFrameIntoClientArea(GetNativeView(), &margins); DWORD window_style = GetWindowLong(GWL_STYLE); - if (browser_view_->UsingSideTabs()) { + if (browser_view_->UseVerticalTabs()) { if (window_style & WS_CAPTION) SetWindowLong(GWL_STYLE, window_style & ~WS_CAPTION); } else { diff --git a/chrome/browser/views/frame/browser_frame_win.h b/chrome/browser/views/frame/browser_frame_win.h index d928f93..42e8974 100644 --- a/chrome/browser/views/frame/browser_frame_win.h +++ b/chrome/browser/views/frame/browser_frame_win.h @@ -37,7 +37,6 @@ class BrowserFrameWin : public BrowserFrame, public views::WindowWin { // BrowserFrame implementation. virtual views::Window* GetWindow(); - virtual void TabStripCreated(BaseTabStrip* tabstrip); virtual int GetMinimizeButtonOffset() const; virtual gfx::Rect GetBoundsForTabStrip(BaseTabStrip* tabstrip) const; virtual void UpdateThrobber(bool running); @@ -46,6 +45,7 @@ class BrowserFrameWin : public BrowserFrame, public views::WindowWin { virtual bool AlwaysUseNativeFrame() const; virtual views::View* GetFrameView() const; virtual void PaintTabStripShadow(gfx::Canvas* canvas); + virtual void TabStripDisplayModeChanged(); protected: // Overridden from views::WindowWin: diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc index 3312812..f8e4da4 100644 --- a/chrome/browser/views/frame/browser_view.cc +++ b/chrome/browser/views/frame/browser_view.cc @@ -544,8 +544,8 @@ bool BrowserView::IsTabStripVisible() const { return browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP); } -bool BrowserView::UsingSideTabs() const { - return SideTabStrip::Visible(browser_->profile()); +bool BrowserView::UseVerticalTabs() const { + return browser_->tabstrip_model()->delegate()->UseVerticalTabs(); } bool BrowserView::IsOffTheRecord() const { @@ -1224,6 +1224,11 @@ void BrowserView::Paste() { false, false); } +void BrowserView::ToggleTabStripMode() { + InitTabStrip(browser_->tabstrip_model()); + frame_->TabStripDisplayModeChanged(); +} + /////////////////////////////////////////////////////////////////////////////// // BrowserView, BrowserWindowTesting implementation: @@ -1641,13 +1646,32 @@ views::LayoutManager* BrowserView::CreateLayoutManager() const { return new BrowserViewLayout; } -BaseTabStrip* BrowserView::CreateTabStrip(TabStripModel* model) { - if (UsingSideTabs()) { +void BrowserView::InitTabStrip(TabStripModel* model) { +// Throw away the existing tabstrip if we're switching display modes. + if (tabstrip_) { + tabstrip_->GetParent()->RemoveChildView(tabstrip_); + delete tabstrip_; + } + + TabStrip* tabstrip_as_tabstrip = NULL; + BrowserTabStripController* tabstrip_controller = NULL; + + if (UseVerticalTabs()) { SideTabStrip* tabstrip = new SideTabStrip; - tabstrip->SetModel(new BrowserTabStripController(model, tabstrip)); - return tabstrip; + tabstrip_controller = new BrowserTabStripController(model, tabstrip); + tabstrip->SetModel(tabstrip_controller); + tabstrip_ = tabstrip; + } else { + tabstrip_as_tabstrip = new TabStrip(model); + tabstrip_ = tabstrip_as_tabstrip; } - return new TabStrip(model); + tabstrip_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TABSTRIP)); + AddChildView(tabstrip_); + + if (tabstrip_controller) + tabstrip_controller->InitFromModel(); + else + tabstrip_as_tabstrip->InitFromModel(); } /////////////////////////////////////////////////////////////////////////////// @@ -1674,10 +1698,7 @@ void BrowserView::Init() { LoadAccelerators(); SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME)); - tabstrip_ = CreateTabStrip(browser_->tabstrip_model()); - tabstrip_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TABSTRIP)); - AddChildView(tabstrip_); - frame_->TabStripCreated(tabstrip_); + InitTabStrip(browser_->tabstrip_model()); toolbar_ = new ToolbarView(browser_.get()); AddChildView(toolbar_); diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h index bf753e9..5452bc8 100644 --- a/chrome/browser/views/frame/browser_view.h +++ b/chrome/browser/views/frame/browser_view.h @@ -151,7 +151,7 @@ class BrowserView : public BrowserBubbleHost, bool IsTabStripVisible() const; // Returns true if the vertical tabstrip is in use. - bool UsingSideTabs() const; + bool UseVerticalTabs() const; // Returns true if the profile associated with this Browser window is // off the record. @@ -322,6 +322,7 @@ class BrowserView : public BrowserBubbleHost, virtual void Cut(); virtual void Copy(); virtual void Paste(); + virtual void ToggleTabStripMode(); // Overridden from BrowserWindowTesting: virtual BookmarkBarView* GetBookmarkBarView() const; @@ -397,9 +398,10 @@ class BrowserView : public BrowserBubbleHost, // override to implemnet different layout pocily. virtual views::LayoutManager* CreateLayoutManager() const; - // Returns a new TabStrip for the browser view. A subclass may - // override to return a different TabStrip implementation. - virtual BaseTabStrip* CreateTabStrip(TabStripModel* tab_strip_model); + // Initializes a new TabStrip for the browser view. This can be performed + // multiple times over the life of the browser, and is run when the display + // mode for the tabstrip changes from horizontal to vertical. + void InitTabStrip(TabStripModel* tab_strip_model); // Browser window related initializations. virtual void Init(); diff --git a/chrome/browser/views/frame/browser_view_layout.cc b/chrome/browser/views/frame/browser_view_layout.cc index 2c11574..e632534 100644 --- a/chrome/browser/views/frame/browser_view_layout.cc +++ b/chrome/browser/views/frame/browser_view_layout.cc @@ -285,7 +285,7 @@ int BrowserViewLayout::LayoutTabStrip() { gfx::Rect layout_bounds = browser_view_->frame()->GetBoundsForTabStrip(tabstrip_); - if (browser_view_->UsingSideTabs()) { + if (browser_view_->UseVerticalTabs()) { vertical_layout_rect_.Inset( layout_bounds.right() - kBrowserViewTabStripHorizontalOverlap, 0, 0, 0); } else { @@ -301,7 +301,7 @@ int BrowserViewLayout::LayoutTabStrip() { layout_bounds.set_origin(tabstrip_origin); tabstrip_->SetVisible(true); tabstrip_->SetBounds(layout_bounds); - return browser_view_->UsingSideTabs() ? 0 : layout_bounds.bottom(); + return browser_view_->UseVerticalTabs() ? 0 : layout_bounds.bottom(); } int BrowserViewLayout::LayoutToolbar(int top) { @@ -309,7 +309,7 @@ int BrowserViewLayout::LayoutToolbar(int top) { bool visible = browser_view_->IsToolbarVisible(); toolbar_->location_bar()->SetFocusable(visible); int y = 0; - if (!browser_view_->UsingSideTabs()) { + if (!browser_view_->UseVerticalTabs()) { y = top - ((visible && browser_view_->IsTabStripVisible()) ? kToolbarTabStripVerticalOverlap : 0); diff --git a/chrome/browser/views/frame/glass_browser_frame_view.cc b/chrome/browser/views/frame/glass_browser_frame_view.cc index 04c2bd6..f62d063 100644 --- a/chrome/browser/views/frame/glass_browser_frame_view.cc +++ b/chrome/browser/views/frame/glass_browser_frame_view.cc @@ -83,7 +83,7 @@ GlassBrowserFrameView::~GlassBrowserFrameView() { gfx::Rect GlassBrowserFrameView::GetBoundsForTabStrip( BaseTabStrip* tabstrip) const { - if (browser_view_->UsingSideTabs()) { + if (browser_view_->UseVerticalTabs()) { gfx::Size ps = tabstrip->GetPreferredSize(); return gfx::Rect(0, NonClientTopBorderHeight(), ps.width(), browser_view_->height()); @@ -120,6 +120,9 @@ void GlassBrowserFrameView::UpdateThrobber(bool running) { } void GlassBrowserFrameView::PaintTabStripShadow(gfx::Canvas* canvas) { + if (!browser_view_->UseVerticalTabs()) + return; + ThemeProvider* tp = GetThemeProvider(); SkBitmap* shadow_top = tp->GetBitmapNamed(IDR_SIDETABS_SHADOW_TOP); SkBitmap* shadow_middle = tp->GetBitmapNamed(IDR_SIDETABS_SHADOW_MIDDLE); @@ -242,7 +245,7 @@ int GlassBrowserFrameView::NonClientTopBorderHeight() const { // We'd like to use FrameBorderThickness() here, but the maximized Aero glass // frame has a 0 frame border around most edges and a CXSIZEFRAME-thick border // at the top (see AeroGlassFrame::OnGetMinMaxInfo()). - const int kRestoredHeight = browser_view_->UsingSideTabs() ? + const int kRestoredHeight = browser_view_->UseVerticalTabs() ? -2 : kNonClientRestoredExtraThickness; return GetSystemMetrics(SM_CXSIZEFRAME) + (browser_view_->IsMaximized() ? -kTabstripTopShadowThickness : kRestoredHeight); @@ -274,7 +277,7 @@ void GlassBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) { // Draw the toolbar background, setting src_y of the paint to the tab // strip height as the toolbar background begins at the top of the tabs. - int src_y = browser_view_->UsingSideTabs() + int src_y = browser_view_->UseVerticalTabs() ? TabRenderer::GetMinimumUnselectedSize().height() : browser_view_->GetTabStripHeight() - 1; canvas->TileImageInt(*theme_toolbar, 0, src_y, @@ -324,7 +327,7 @@ void GlassBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) { tp->GetBitmapNamed(IDR_CONTENT_TOP_LEFT_CORNER)->height(); gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height()); - if (browser_view_->UsingSideTabs()) { + if (browser_view_->UseVerticalTabs()) { client_area_bounds.Inset( GetBoundsForTabStrip(browser_view_->tabstrip()).width() - 4, 0, 0, 0); } diff --git a/chrome/browser/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/views/tabs/browser_tab_strip_controller.cc index 343ee09..da30f47 100644 --- a/chrome/browser/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/views/tabs/browser_tab_strip_controller.cc @@ -5,7 +5,68 @@ #include "chrome/browser/views/tabs/browser_tab_strip_controller.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_menu_model.h" #include "chrome/browser/views/tabs/side_tab_strip.h" +#include "views/controls/menu/menu_2.h" +#include "views/widget/widget.h" + +class BrowserTabStripController::TabContextMenuContents + : public menus::SimpleMenuModel::Delegate { + public: + TabContextMenuContents(int tab_index, BrowserTabStripController* controller) + : ALLOW_THIS_IN_INITIALIZER_LIST(model_(this)), + tab_index_(tab_index), + controller_(controller) { + Build(); + } + virtual ~TabContextMenuContents() { + menu_->CancelMenu(); + } + + void RunMenuAt(const gfx::Point& point) { + menu_->RunMenuAt(point, views::Menu2::ALIGN_TOPLEFT); + } + + // Overridden from menus::SimpleMenuModel::Delegate: + virtual bool IsCommandIdChecked(int command_id) const { + return controller_->IsCommandCheckedForTab( + static_cast(command_id), + tab_index_); + } + virtual bool IsCommandIdEnabled(int command_id) const { + return controller_->IsCommandEnabledForTab( + static_cast(command_id), + tab_index_); + } + virtual bool GetAcceleratorForCommandId( + int command_id, + menus::Accelerator* accelerator) { + return controller_->tabstrip_->GetWidget()->GetAccelerator(command_id, + accelerator); + } + virtual void ExecuteCommand(int command_id) { + controller_->ExecuteCommandForTab( + static_cast(command_id), + tab_index_); + } + + private: + void Build() { + menu_.reset(new views::Menu2(&model_)); + } + + TabMenuModel model_; + scoped_ptr menu_; + + // The index of the tab we are showing the context menu for. + int tab_index_; + + // A pointer back to our hosting controller, for command state information. + BrowserTabStripController* controller_; + + DISALLOW_COPY_AND_ASSIGN(TabContextMenuContents); +}; + //////////////////////////////////////////////////////////////////////////////// // BrowserTabStripController, public: @@ -18,6 +79,40 @@ BrowserTabStripController::BrowserTabStripController(TabStripModel* model, } BrowserTabStripController::~BrowserTabStripController() { + model_->RemoveObserver(this); +} + +void BrowserTabStripController::InitFromModel() { + // Walk the model, calling our insertion observer method for each item within + // it. + for (int i = 0; i < model_->count(); ++i) { + TabInsertedAt(model_->GetTabContentsAt(i), i, + i == model_->selected_index()); + } +} + +bool BrowserTabStripController::IsCommandEnabledForTab( + TabStripModel::ContextMenuCommand command_id, int tab_index) const { + if (model_->ContainsIndex(tab_index)) + return model_->IsContextMenuCommandEnabled(tab_index, command_id); + return false; +} + +bool BrowserTabStripController::IsCommandCheckedForTab( + TabStripModel::ContextMenuCommand command_id, int tab_index) const { + // TODO(beng): move to TabStripModel, see note in IsTabPinned. + if (command_id == TabStripModel::CommandTogglePinned) + return false; + + if (model_->ContainsIndex(tab_index)) + return model_->IsContextMenuCommandChecked(tab_index, command_id); + return false; +} + +void BrowserTabStripController::ExecuteCommandForTab( + TabStripModel::ContextMenuCommand command_id, int tab_index) { + if (model_->ContainsIndex(tab_index)) + model_->ExecuteContextMenuCommand(tab_index, command_id); } //////////////////////////////////////////////////////////////////////////////// @@ -53,6 +148,13 @@ void BrowserTabStripController::CloseTab(int index) { model_->CloseTabContentsAt(index); } +void BrowserTabStripController::ShowContextMenu(int index, + const gfx::Point& p) { + if (!context_menu_contents_.get()) + context_menu_contents_.reset(new TabContextMenuContents(index, this)); + context_menu_contents_->RunMenuAt(p); +} + //////////////////////////////////////////////////////////////////////////////// // BrowserTabStripController, TabStripModelObserver implementation: diff --git a/chrome/browser/views/tabs/browser_tab_strip_controller.h b/chrome/browser/views/tabs/browser_tab_strip_controller.h index e127659..c50fcdb 100644 --- a/chrome/browser/views/tabs/browser_tab_strip_controller.h +++ b/chrome/browser/views/tabs/browser_tab_strip_controller.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_VIEWS_TABS_BROWSER_TAB_STRIP_CONTROLLER_H_ #define CHROME_BROWSER_VIEWS_TABS_BROWSER_TAB_STRIP_CONTROLLER_H_ +#include "base/scoped_ptr.h" + #include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/views/tabs/side_tab_strip_model.h" @@ -18,6 +20,15 @@ class BrowserTabStripController : public SideTabStripModel, BrowserTabStripController(TabStripModel* model, SideTabStrip* tabstrip); virtual ~BrowserTabStripController(); + void InitFromModel(); + + bool IsCommandEnabledForTab(TabStripModel::ContextMenuCommand command_id, + int tab_index) const; + bool IsCommandCheckedForTab(TabStripModel::ContextMenuCommand command_id, + int tab_index) const; + void ExecuteCommandForTab(TabStripModel::ContextMenuCommand command_id, + int tab_index); + // SideTabStripModel implementation: virtual SkBitmap GetIcon(int index) const; virtual string16 GetTitle(int index) const; @@ -25,6 +36,7 @@ class BrowserTabStripController : public SideTabStripModel, virtual NetworkState GetNetworkState(int index) const; virtual void SelectTab(int index); virtual void CloseTab(int index); + virtual void ShowContextMenu(int index, const gfx::Point& p); // TabStripModelObserver implementation: virtual void TabInsertedAt(TabContents* contents, int index, @@ -43,9 +55,14 @@ class BrowserTabStripController : public SideTabStripModel, virtual void TabBlockedStateChanged(TabContents* contents, int index); private: + class TabContextMenuContents; + TabStripModel* model_; SideTabStrip* tabstrip_; + // If non-NULL it means we're showing a menu for the tab. + scoped_ptr context_menu_contents_; + DISALLOW_COPY_AND_ASSIGN(BrowserTabStripController); }; diff --git a/chrome/browser/views/tabs/side_tab.cc b/chrome/browser/views/tabs/side_tab.cc index c08eaaa..ee160d7 100644 --- a/chrome/browser/views/tabs/side_tab.cc +++ b/chrome/browser/views/tabs/side_tab.cc @@ -56,6 +56,8 @@ SideTab::SideTab(SideTabModel* model) hover_animation_.reset(new SlideAnimation(this)); hover_animation_->SetSlideDuration(kHoverDurationMs); + + SetContextMenuController(this); } SideTab::~SideTab() { @@ -110,6 +112,15 @@ void SideTab::ButtonPressed(views::Button* sender, const views::Event& event) { } //////////////////////////////////////////////////////////////////////////////// +// SideTab, views::ContextMenuController implementation: + +void SideTab::ShowContextMenu(views::View* source, + const gfx::Point& p, + bool is_mouse_gesture) { + model_->ShowContextMenu(this, p); +} + +//////////////////////////////////////////////////////////////////////////////// // SideTab, views::View overrides: void SideTab::Layout() { @@ -119,14 +130,19 @@ void SideTab::Layout() { gfx::Size ps = close_button_->GetPreferredSize(); int close_y = (height() - ps.height()) / 2; - close_button_->SetBounds(width() - ps.width() - close_y, close_y, ps.width(), - ps.height()); + close_button_->SetBounds( + std::max(0, width() - ps.width() - close_y), + close_y, + ps.width(), + ps.height()); int title_y = (height() - font_->height()) / 2; int title_x = icon_bounds_.right() + kIconTitleSpacing; - title_bounds_.SetRect(title_x, title_y, - close_button_->x() - kTitleCloseSpacing - title_x, - font_->height()); + title_bounds_.SetRect( + title_x, + title_y, + std::max(0, close_button_->x() - kTitleCloseSpacing - title_x), + font_->height()); } void SideTab::Paint(gfx::Canvas* canvas) { diff --git a/chrome/browser/views/tabs/side_tab.h b/chrome/browser/views/tabs/side_tab.h index 72fc8b0..59e568b 100644 --- a/chrome/browser/views/tabs/side_tab.h +++ b/chrome/browser/views/tabs/side_tab.h @@ -26,9 +26,13 @@ class SideTabModel { // Closes the tab. virtual void CloseTab(SideTab* tab) = 0; + + // Shows a context menu for the tab at the specified point in screen coords. + virtual void ShowContextMenu(SideTab* tab, const gfx::Point& p) = 0; }; class SideTab : public views::View, + public views::ContextMenuController, public views::ButtonListener, public AnimationDelegate { public: @@ -48,6 +52,11 @@ class SideTab : public views::View, // views::ButtonListener implementation: virtual void ButtonPressed(views::Button* sender, const views::Event& event); + // views::ContextMenuController implementation: + virtual void ShowContextMenu(views::View* source, + const gfx::Point& p, + bool is_mouse_gesture); + // views::View Overrides: virtual void Layout(); virtual void Paint(gfx::Canvas* canvas); diff --git a/chrome/browser/views/tabs/side_tab_strip.cc b/chrome/browser/views/tabs/side_tab_strip.cc index 6896d42..6912f2c 100644 --- a/chrome/browser/views/tabs/side_tab_strip.cc +++ b/chrome/browser/views/tabs/side_tab_strip.cc @@ -38,12 +38,6 @@ bool SideTabStrip::Available() { switches::kEnableVerticalTabs); } -// static -bool SideTabStrip::Visible(Profile* profile) { - return Available() && - profile->GetPrefs()->GetBoolean(prefs::kUseVerticalTabs); -} - void SideTabStrip::AddTabAt(int index) { SideTab* tab = new SideTab(this); AddChildView(tab); @@ -88,6 +82,10 @@ void SideTabStrip::CloseTab(SideTab* tab) { model_->CloseTab(GetIndexOfSideTab(tab)); } +void SideTabStrip::ShowContextMenu(SideTab* tab, const gfx::Point& p) { + model_->ShowContextMenu(GetIndexOfSideTab(tab), p); +} + //////////////////////////////////////////////////////////////////////////////// // SideTabStrip, BaseTabStrip implementation: diff --git a/chrome/browser/views/tabs/side_tab_strip.h b/chrome/browser/views/tabs/side_tab_strip.h index 71b704a..b1716ba 100644 --- a/chrome/browser/views/tabs/side_tab_strip.h +++ b/chrome/browser/views/tabs/side_tab_strip.h @@ -25,10 +25,6 @@ class SideTabStrip : public BaseTabStrip, // command line flag that allows the SideTabStrip to be optionally shown. static bool Available(); - // Whether or not the vertical tabstrip is shown. Only valid if Available() - // returns true. - static bool Visible(Profile* profile); - // Notifies the SideTabStrip that a tab was added in the model at |index|. void AddTabAt(int index); @@ -48,6 +44,7 @@ class SideTabStrip : public BaseTabStrip, virtual bool IsSelected(SideTab* tab) const; virtual void SelectTab(SideTab* tab); virtual void CloseTab(SideTab* tab); + virtual void ShowContextMenu(SideTab* tab, const gfx::Point& p); // BaseTabStrip implementation: virtual int GetPreferredHeight(); diff --git a/chrome/browser/views/tabs/side_tab_strip_model.h b/chrome/browser/views/tabs/side_tab_strip_model.h index a456c36..835e5ba 100644 --- a/chrome/browser/views/tabs/side_tab_strip_model.h +++ b/chrome/browser/views/tabs/side_tab_strip_model.h @@ -7,12 +7,17 @@ #include "base/string16.h" +namespace gfx { +class Point; +} class SkBitmap; // A model interface implemented by an object that can provide information // about SideTabs in a SideTabStrip. class SideTabStripModel { public: + virtual ~SideTabStripModel() {} + // Returns metadata about the tab at the specified index. virtual SkBitmap GetIcon(int index) const = 0; virtual string16 GetTitle(int index) const = 0; @@ -35,6 +40,10 @@ class SideTabStripModel { // Closes the tab at the specified index in the model. virtual void CloseTab(int index) = 0; + + // Shows a context menu for the tab at the specified index at the specified + // point in screen coords. + virtual void ShowContextMenu(int index, const gfx::Point& p) = 0; }; #endif // CHROME_BROWSER_VIEWS_TABS_SIDE_TAB_STRIP_MODEL_H_ diff --git a/chrome/browser/views/tabs/tab.cc b/chrome/browser/views/tabs/tab.cc index 4c6aa46..275af42 100644 --- a/chrome/browser/views/tabs/tab.cc +++ b/chrome/browser/views/tabs/tab.cc @@ -51,9 +51,9 @@ class Tab::TabContextMenuContents : public menus::SimpleMenuModel::Delegate { // Overridden from menus::SimpleMenuModel::Delegate: virtual bool IsCommandIdChecked(int command_id) const { - if (!tab_ || command_id != TabStripModel::CommandTogglePinned) - return false; - return tab_->delegate()->IsTabPinned(tab_); + return tab_ && tab_->delegate()->IsCommandCheckedForTab( + static_cast(command_id), + tab_); } virtual bool IsCommandIdEnabled(int command_id) const { return tab_ && tab_->delegate()->IsCommandEnabledForTab( diff --git a/chrome/browser/views/tabs/tab.h b/chrome/browser/views/tabs/tab.h index f491291..6a9bf21 100644 --- a/chrome/browser/views/tabs/tab.h +++ b/chrome/browser/views/tabs/tab.h @@ -46,6 +46,10 @@ class Tab : public TabRenderer, virtual bool IsCommandEnabledForTab( TabStripModel::ContextMenuCommand command_id, const Tab* tab) const = 0; + // Returns true if the specified command is checked for the specified Tab. + virtual bool IsCommandCheckedForTab( + TabStripModel::ContextMenuCommand command_id, const Tab* tab) const = 0; + // Executes the specified command for the specified Tab. virtual void ExecuteCommandForTab( TabStripModel::ContextMenuCommand command_id, Tab* tab) = 0; diff --git a/chrome/browser/views/tabs/tab_strip.cc b/chrome/browser/views/tabs/tab_strip.cc index ec15794..80d761b 100644 --- a/chrome/browser/views/tabs/tab_strip.cc +++ b/chrome/browser/views/tabs/tab_strip.cc @@ -744,8 +744,7 @@ TabStrip::TabStrip(TabStripModel* model) TabStrip::~TabStrip() { active_animation_.reset(NULL); - // TODO(beng): (1031854) Restore this line once XPFrame/VistaFrame are dead. - // model_->RemoveObserver(this); + model_->RemoveObserver(this); // TODO(beng): remove this if it doesn't work to fix the TabSelectedAt bug. drag_controller_.reset(NULL); @@ -818,6 +817,15 @@ bool TabStrip::IsCompatibleWith(TabStrip* other) const { return model_->profile() == other->model()->profile(); } +void TabStrip::InitFromModel() { + // Walk the model, calling our insertion observer method for each item within + // it. + for (int i = 0; i < model_->count(); ++i) { + TabInsertedAt(model_->GetTabContentsAt(i), i, + i == model_->selected_index()); + } +} + //////////////////////////////////////////////////////////////////////////////// // TabStrip, BaseTabStrip implementation: @@ -1309,6 +1317,18 @@ bool TabStrip::IsCommandEnabledForTab( return false; } +bool TabStrip::IsCommandCheckedForTab( + TabStripModel::ContextMenuCommand command_id, const Tab* tab) const { + // TODO(beng): move to TabStripModel, see note in IsTabPinned. + if (command_id == TabStripModel::CommandTogglePinned) + return IsTabPinned(tab); + + int index = GetIndexOfTab(tab); + if (model_->ContainsIndex(index)) + return model_->IsContextMenuCommandChecked(index, command_id); + return false; +} + void TabStrip::ExecuteCommandForTab( TabStripModel::ContextMenuCommand command_id, Tab* tab) { int index = GetIndexOfTab(tab); diff --git a/chrome/browser/views/tabs/tab_strip.h b/chrome/browser/views/tabs/tab_strip.h index 6285e1d..4e2dec4 100644 --- a/chrome/browser/views/tabs/tab_strip.h +++ b/chrome/browser/views/tabs/tab_strip.h @@ -74,6 +74,11 @@ class TabStrip : public BaseTabStrip, // Compatible tab strips can transfer tabs during drag and drop. bool IsCompatibleWith(TabStrip* other) const; + // Populates the BaseTabStrip implementation from its model. This is primarily + // useful when switching between display types and there are existing tabs. + // Upon initial creation the TabStrip is empty. + void InitFromModel(); + // BaseTabStrip implementation: virtual int GetPreferredHeight(); virtual void SetBackgroundOffset(const gfx::Point& offset); @@ -136,6 +141,8 @@ class TabStrip : public BaseTabStrip, virtual void CloseTab(Tab* tab); virtual bool IsCommandEnabledForTab( TabStripModel::ContextMenuCommand command_id, const Tab* tab) const; + virtual bool IsCommandCheckedForTab( + TabStripModel::ContextMenuCommand command_id, const Tab* tab) const; virtual void ExecuteCommandForTab( TabStripModel::ContextMenuCommand command_id, Tab* tab); virtual void StartHighlightTabsForCommand( diff --git a/chrome/test/test_browser_window.h b/chrome/test/test_browser_window.h index 169afdb..405f361 100644 --- a/chrome/test/test_browser_window.h +++ b/chrome/test/test_browser_window.h @@ -99,6 +99,7 @@ class TestBrowserWindow : public BrowserWindow { virtual void Cut() { } virtual void Copy() { } virtual void Paste() { } + virtual void ToggleTabStripMode() { } protected: virtual void DestroyBrowser() {} -- cgit v1.1