diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-13 22:11:03 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-13 22:11:03 +0000 |
commit | 5e26d9d482761e38a15f8cb987294d979bc58576 (patch) | |
tree | fb218ddc03bb4e3a3996c114c5ad3aa40a0e6914 /chrome/browser | |
parent | 7cd70c394eeaeb6a93a8e28142942b5584f01e2d (diff) | |
download | chromium_src-5e26d9d482761e38a15f8cb987294d979bc58576.zip chromium_src-5e26d9d482761e38a15f8cb987294d979bc58576.tar.gz chromium_src-5e26d9d482761e38a15f8cb987294d979bc58576.tar.bz2 |
Refactors TabStrip code for better sharing between TabStrip and
SideTabStrip. I still have a bunch of cleanup to do, but this is a
good checkpoint.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/2037012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47202 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
26 files changed, 1193 insertions, 1153 deletions
diff --git a/chrome/browser/chromeos/frame/browser_view.cc b/chrome/browser/chromeos/frame/browser_view.cc index 354676f..761459c 100644 --- a/chrome/browser/chromeos/frame/browser_view.cc +++ b/chrome/browser/chromeos/frame/browser_view.cc @@ -73,6 +73,8 @@ class Spacer : public views::View { DISALLOW_COPY_AND_ASSIGN(Spacer); }; +// TODO(sky): wire this back up. +/* // A chromeos implementation of Tab that shows the compact location bar. class ChromeosTab : public Tab { public: @@ -113,6 +115,7 @@ class ChromeosTabStrip : public TabStrip { DISALLOW_COPY_AND_ASSIGN(ChromeosTabStrip); }; +*/ } // namespace @@ -444,11 +447,6 @@ views::LayoutManager* BrowserView::CreateLayoutManager() const { return new BrowserViewLayout(); } -BaseTabStrip* BrowserView::CreateTabStrip( - TabStripModel* tab_strip_model) { - return new ChromeosTabStrip(tab_strip_model, this); -} - void BrowserView::ChildPreferredSizeChanged(View* child) { Layout(); SchedulePaint(); diff --git a/chrome/browser/chromeos/frame/browser_view.h b/chrome/browser/chromeos/frame/browser_view.h index 87dc59b..49c0daa 100644 --- a/chrome/browser/chromeos/frame/browser_view.h +++ b/chrome/browser/chromeos/frame/browser_view.h @@ -64,7 +64,6 @@ class BrowserView : public ::BrowserView, virtual void SetFocusToLocationBar(bool select_all); virtual void ToggleCompactNavigationBar(); virtual views::LayoutManager* CreateLayoutManager() const; - virtual BaseTabStrip* CreateTabStrip(TabStripModel* tab_strip_model); virtual void ChildPreferredSizeChanged(View* child); // views::ButtonListener overrides. diff --git a/chrome/browser/tabs/tab_strip_model.h b/chrome/browser/tabs/tab_strip_model.h index 71c0cbb..bbfde0d 100644 --- a/chrome/browser/tabs/tab_strip_model.h +++ b/chrome/browser/tabs/tab_strip_model.h @@ -103,7 +103,7 @@ class TabStripModelObserver { virtual void TabReplacedAt(TabContents* old_contents, TabContents* new_contents, int index) {} - // Invoked when the mini state of a tab changes. This is not invoked if the + // Invoked when the pinned state of a tab changes. This is not invoked if the // tab ends up moving as a result of the mini state changing. // See note in TabMiniStateChanged as to how this relates to // TabMiniStateChanged. diff --git a/chrome/browser/views/extensions/extension_installed_bubble.cc b/chrome/browser/views/extensions/extension_installed_bubble.cc index 4104664..29aa69a 100644 --- a/chrome/browser/views/extensions/extension_installed_bubble.cc +++ b/chrome/browser/views/extensions/extension_installed_bubble.cc @@ -14,6 +14,8 @@ #include "chrome/browser/views/browser_actions_container.h" #include "chrome/browser/views/frame/browser_view.h" #include "chrome/browser/views/location_bar/location_bar_view.h" +#include "chrome/browser/views/tabs/base_tab_renderer.h" +#include "chrome/browser/views/tabs/base_tab_strip.h" #include "chrome/browser/views/toolbar_view.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/common/extensions/extension.h" @@ -315,9 +317,9 @@ void ExtensionInstalledBubble::ShowInternal() { extension_->page_action()); DCHECK(reference_view); } else if (type_ == EXTENSION_APP) { - TabStrip* tabstrip = browser_view->tabstrip()->AsTabStrip(); - Tab* tab = tabstrip->GetSelectedTab(); - DCHECK(tab->app()); + BaseTabStrip* tabstrip = browser_view->tabstrip(); + BaseTabRenderer* tab = tabstrip->GetSelectedBaseTab(); + DCHECK(tab->data().app); reference_view = tab; } diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc index 59f013c..2ef1fe1 100644 --- a/chrome/browser/views/frame/browser_view.cc +++ b/chrome/browser/views/frame/browser_view.cc @@ -1655,27 +1655,20 @@ void BrowserView::InitTabStrip(TabStripModel* model) { delete tabstrip_; } - TabStrip* tabstrip_as_tabstrip = NULL; - BrowserTabStripController* tabstrip_controller = NULL; - - if (UseVerticalTabs()) { - SideTabStrip* tabstrip = new SideTabStrip; - tabstrip_controller = new BrowserTabStripController(model, tabstrip); - tabstrip->SetModel(tabstrip_controller); - tabstrip_ = tabstrip; - } else { - tabstrip_as_tabstrip = new TabStrip(model); - tabstrip_ = tabstrip_as_tabstrip; - } + BrowserTabStripController* tabstrip_controller = + new BrowserTabStripController(model); + + if (UseVerticalTabs()) + tabstrip_ = new SideTabStrip(tabstrip_controller); + else + tabstrip_ = new TabStrip(tabstrip_controller); + tabstrip_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_TABSTRIP)); if (browser_->extension_app() && tabstrip_->AsTabStrip()) tabstrip_->AsTabStrip()->set_new_tab_button_enabled(false); AddChildView(tabstrip_); - if (tabstrip_controller) - tabstrip_controller->InitFromModel(); - else - tabstrip_as_tabstrip->InitFromModel(); + tabstrip_controller->InitFromModel(tabstrip_); } /////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/browser/views/tabs/base_tab_renderer.cc b/chrome/browser/views/tabs/base_tab_renderer.cc new file mode 100644 index 0000000..e9f138d --- /dev/null +++ b/chrome/browser/views/tabs/base_tab_renderer.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2010 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/base_tab_renderer.h" + +BaseTabRenderer::BaseTabRenderer(TabController* controller) + : controller_(controller) { +} + +void BaseTabRenderer::SetData(const TabRendererData& data) { + TabRendererData old(data_); + data_ = data; + DataChanged(old); + Layout(); +} + +void BaseTabRenderer::UpdateLoadingAnimation( + TabRendererData::NetworkState state) { + if (state == data_.network_state && + state == TabRendererData::NETWORK_STATE_NONE) { + // If the network state is none and hasn't changed, do nothing. Otherwise we + // need to advance the animation frame. + return; + } + + data_.network_state = state; + AdvanceLoadingAnimation(state); +} diff --git a/chrome/browser/views/tabs/base_tab_renderer.h b/chrome/browser/views/tabs/base_tab_renderer.h new file mode 100644 index 0000000..8c80a53 --- /dev/null +++ b/chrome/browser/views/tabs/base_tab_renderer.h @@ -0,0 +1,80 @@ +// Copyright (c) 2010 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_BASE_TAB_RENDERER_H_ +#define CHROME_BROWSER_VIEWS_TABS_BASE_TAB_RENDERER_H_ + +#include "chrome/browser/views/tabs/tab_renderer_data.h" +#include "views/view.h" + +class BaseTabRenderer; + +// Controller for tabs. +class TabController { + public: + // Selects the tab. + virtual void SelectTab(BaseTabRenderer* tab) = 0; + + // Closes the tab. + virtual void CloseTab(BaseTabRenderer* tab) = 0; + + // Shows a context menu for the tab at the specified point in screen coords. + virtual void ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p) = 0; + + // Returns true if the specified Tab is selected. + virtual bool IsTabSelected(const BaseTabRenderer* tab) const = 0; + + // Returns true if the specified Tab is pinned. + virtual bool IsTabPinned(const BaseTabRenderer* tab) const = 0; + + // Potentially starts a drag for the specified Tab. + virtual void MaybeStartDrag(BaseTabRenderer* tab, + const views::MouseEvent& event) = 0; + + // Continues dragging a Tab. + virtual void ContinueDrag(const views::MouseEvent& event) = 0; + + // Ends dragging a Tab. |canceled| is true if the drag was aborted in a way + // other than the user releasing the mouse. Returns whether the tab has been + // destroyed. + virtual bool EndDrag(bool canceled) = 0; + + protected: + virtual ~TabController() {} +}; + +// Base class for tab renderers. +class BaseTabRenderer : public views::View { + public: + explicit BaseTabRenderer(TabController* controller); + + // Sets the data this tabs displays. Invokes DataChanged for subclasses to + // update themselves appropriately. + void SetData(const TabRendererData& data); + const TabRendererData& data() const { return data_; } + + // Sets the network state. If the network state changes NetworkStateChanged is + // invoked. + virtual void UpdateLoadingAnimation(TabRendererData::NetworkState state); + + protected: + // Invoked from SetData after |data_| has been updated to the new data. + virtual void DataChanged(const TabRendererData& old) {} + + // Invoked if data_.network_state changes, or the network_state is not none. + virtual void AdvanceLoadingAnimation(TabRendererData::NetworkState state) {} + + TabController* controller() const { return controller_; } + + private: + // The controller. + // WARNING: this is null during detached tab dragging. + TabController* controller_; + + TabRendererData data_; + + DISALLOW_COPY_AND_ASSIGN(BaseTabRenderer); +}; + +#endif // CHROME_BROWSER_VIEWS_TABS_BASE_TAB_RENDERER_H_ diff --git a/chrome/browser/views/tabs/base_tab_strip.cc b/chrome/browser/views/tabs/base_tab_strip.cc new file mode 100644 index 0000000..a39633b --- /dev/null +++ b/chrome/browser/views/tabs/base_tab_strip.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2010 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/base_tab_strip.h" + +#include "chrome/browser/views/tabs/tab_strip_controller.h" + +BaseTabStrip::BaseTabStrip(TabStripController* controller) + : controller_(controller) { +} + +BaseTabStrip::~BaseTabStrip() { +} + +void BaseTabStrip::UpdateLoadingAnimations() { + controller_->UpdateLoadingAnimations(); +} + +BaseTabRenderer* BaseTabStrip::GetSelectedBaseTab() const { + return GetBaseTabAtModelIndex(controller_->GetSelectedIndex()); +} + +int BaseTabStrip::GetModelCount() const { + return controller_->GetCount(); +} + +bool BaseTabStrip::IsValidModelIndex(int model_index) const { + return controller_->IsValidIndex(model_index); +} + +void BaseTabStrip::SelectTab(BaseTabRenderer* tab) { + int model_index = GetModelIndexOfBaseTab(tab); + if (IsValidModelIndex(model_index)) + controller_->SelectTab(model_index); +} + +void BaseTabStrip::CloseTab(BaseTabRenderer* tab) { + int model_index = GetModelIndexOfBaseTab(tab); + if (IsValidModelIndex(model_index)) + controller_->CloseTab(model_index); +} + +void BaseTabStrip::ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p) { + controller_->ShowContextMenu(tab, p); +} + +bool BaseTabStrip::IsTabSelected(const BaseTabRenderer* tab) const { + int model_index = GetModelIndexOfBaseTab(tab); + return IsValidModelIndex(model_index) && + controller_->IsTabSelected(model_index); +} + +bool BaseTabStrip::IsTabPinned(const BaseTabRenderer* tab) const { + int model_index = GetModelIndexOfBaseTab(tab); + return IsValidModelIndex(model_index) && + controller_->IsTabPinned(model_index); +} diff --git a/chrome/browser/views/tabs/base_tab_strip.h b/chrome/browser/views/tabs/base_tab_strip.h index fbb7612..eb3208b 100644 --- a/chrome/browser/views/tabs/base_tab_strip.h +++ b/chrome/browser/views/tabs/base_tab_strip.h @@ -5,14 +5,21 @@ #ifndef CHROME_BROWSER_VIEWS_TABS_BASE_TAB_STRIP_H_ #define CHROME_BROWSER_VIEWS_TABS_BASE_TAB_STRIP_H_ +#include "base/scoped_ptr.h" +#include "chrome/browser/views/tabs/base_tab_renderer.h" #include "views/view.h" class TabStrip; +class TabStripController; +class ThemeProvider; -// A class that represents the common interface to the tabstrip used by classes -// such as BrowserView etc. -class BaseTabStrip : public views::View { +// Base class for the view tab strip implementations. +class BaseTabStrip : public views::View, + public TabController { public: + explicit BaseTabStrip(TabStripController* controller); + virtual ~BaseTabStrip(); + // Returns the preferred height of this TabStrip. This is based on the // typical height of its constituent tabs. virtual int GetPreferredHeight() = 0; @@ -34,7 +41,7 @@ class BaseTabStrip : public views::View { // Updates the loading animations displayed by tabs in the tabstrip to the // next frame. - virtual void UpdateLoadingAnimations() = 0; + void UpdateLoadingAnimations(); // Returns true if Tabs in this TabStrip are currently changing size or // position. @@ -42,7 +49,73 @@ class BaseTabStrip : public views::View { // Returns this object as a TabStrip if it is one. virtual TabStrip* AsTabStrip() = 0; + + // Starts highlighting the tab at the specified index. + virtual void StartHighlight(int model_index) = 0; + + // Stops all tab higlighting. + virtual void StopAllHighlighting() = 0; + + // Returns the tab at the specified model index. + virtual BaseTabRenderer* GetBaseTabAtModelIndex(int model_index) const = 0; + + // Returns the tab at the specified tab index. + virtual BaseTabRenderer* GetBaseTabAtTabIndex(int tab_index) const = 0; + + // Returns the index of the specified tab in the model coordiate system, or + // -1 if tab is closing or not valid. + virtual int GetModelIndexOfBaseTab(const BaseTabRenderer* tab) const = 0; + + // Returns the number of tabs. + virtual int GetTabCount() const = 0; + + // Returns the selected tab. + virtual BaseTabRenderer* GetSelectedBaseTab() const; + + // Creates and returns a tab that can be used for dragging. Ownership passes + // to the caller. + virtual BaseTabRenderer* CreateTabForDragging() = 0; + + // Adds a tab at the specified index. + virtual void AddTabAt(int model_index, + bool foreground, + const TabRendererData& data) = 0; + + // Removes a tab at the specified index. + virtual void RemoveTabAt(int model_index) = 0; + + // Selects a tab at the specified index. |old_model_index| is the selected + // index prior to the selection change. + virtual void SelectTabAt(int old_model_index, int new_model_index) = 0; + + // Moves a tab. + virtual void MoveTab(int from_model_index, int to_model_index) = 0; + + // Invoked when the title of a tab changes and the tab isn't loading. + virtual void TabTitleChangedNotLoading(int model_index) = 0; + + // Sets the tab data at the specified model index. + virtual void SetTabData(int model_index, const TabRendererData& data) = 0; + + // Cover methods for TabStripController method. + int GetModelCount() const; + bool IsValidModelIndex(int model_index) const; + + // TabController overrides: + virtual void SelectTab(BaseTabRenderer* tab); + virtual void CloseTab(BaseTabRenderer* tab); + virtual void ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p); + virtual bool IsTabSelected(const BaseTabRenderer* tab) const; + virtual bool IsTabPinned(const BaseTabRenderer* tab) const; + virtual void MaybeStartDrag(BaseTabRenderer* tab, + const views::MouseEvent& event) = 0; + virtual void ContinueDrag(const views::MouseEvent& event) = 0; + virtual bool EndDrag(bool canceled) = 0; + + TabStripController* controller() const { return controller_.get(); } + + private: + scoped_ptr<TabStripController> controller_; }; #endif // CHROME_BROWSER_VIEWS_TABS_BASE_TAB_STRIP_H_ - diff --git a/chrome/browser/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/views/tabs/browser_tab_strip_controller.cc index e828326..a2a8c92 100644 --- a/chrome/browser/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/views/tabs/browser_tab_strip_controller.cc @@ -4,40 +4,70 @@ #include "chrome/browser/views/tabs/browser_tab_strip_controller.h" +#include "base/command_line.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/renderer_host/render_view_host.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 "chrome/browser/views/app_launcher.h" +#include "chrome/browser/views/tabs/base_tab_strip.h" +#include "chrome/browser/views/tabs/tab_renderer_data.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/url_constants.h" #include "views/controls/menu/menu_2.h" #include "views/widget/widget.h" +static TabRendererData::NetworkState TabContentsNetworkState( + TabContents* contents) { + if (!contents || !contents->is_loading()) + return TabRendererData::NETWORK_STATE_NONE; + if (contents->waiting_for_response()) + return TabRendererData::NETWORK_STATE_WAITING; + return TabRendererData::NETWORK_STATE_LOADING; +} + class BrowserTabStripController::TabContextMenuContents : public menus::SimpleMenuModel::Delegate { public: - TabContextMenuContents(int tab_index, BrowserTabStripController* controller) + TabContextMenuContents(BaseTabRenderer* tab, + BrowserTabStripController* controller) : ALLOW_THIS_IN_INITIALIZER_LIST( - model_(this, controller->IsTabPinned(tab_index))), - tab_index_(tab_index), - controller_(controller) { + model_(this, controller->IsTabPinned(tab))), + tab_(tab), + controller_(controller), + last_command_(TabStripModel::CommandFirst) { Build(); } virtual ~TabContextMenuContents() { menu_->CancelMenu(); + if (controller_) + controller_->tabstrip_->StopAllHighlighting(); + } + + void Cancel() { + controller_ = NULL; } void RunMenuAt(const gfx::Point& point) { + BrowserTabStripController* controller = controller_; menu_->RunMenuAt(point, views::Menu2::ALIGN_TOPLEFT); + // We could be gone now. Assume |this| is junk! + if (controller) + controller->tabstrip_->StopAllHighlighting(); } // Overridden from menus::SimpleMenuModel::Delegate: virtual bool IsCommandIdChecked(int command_id) const { return controller_->IsCommandCheckedForTab( static_cast<TabStripModel::ContextMenuCommand>(command_id), - tab_index_); + tab_); } virtual bool IsCommandIdEnabled(int command_id) const { return controller_->IsCommandEnabledForTab( static_cast<TabStripModel::ContextMenuCommand>(command_id), - tab_index_); + tab_); } virtual bool GetAcceleratorForCommandId( int command_id, @@ -45,10 +75,15 @@ class BrowserTabStripController::TabContextMenuContents return controller_->tabstrip_->GetWidget()->GetAccelerator(command_id, accelerator); } + virtual void CommandIdHighlighted(int command_id) { + controller_->StopHighlightTabsForCommand(last_command_, tab_); + last_command_ = static_cast<TabStripModel::ContextMenuCommand>(command_id); + controller_->StartHighlightTabsForCommand(last_command_, tab_); + } virtual void ExecuteCommand(int command_id) { controller_->ExecuteCommandForTab( static_cast<TabStripModel::ContextMenuCommand>(command_id), - tab_index_); + tab_); } private: @@ -59,31 +94,40 @@ class BrowserTabStripController::TabContextMenuContents TabMenuModel model_; scoped_ptr<views::Menu2> menu_; - // The index of the tab we are showing the context menu for. - int tab_index_; + // The tab we're showing a menu for. + BaseTabRenderer* tab_; // A pointer back to our hosting controller, for command state information. BrowserTabStripController* controller_; + // The last command that was selected, so that we can start/stop highlighting + // appropriately as the user moves through the menu. + TabStripModel::ContextMenuCommand last_command_; + DISALLOW_COPY_AND_ASSIGN(TabContextMenuContents); }; - //////////////////////////////////////////////////////////////////////////////// // BrowserTabStripController, public: -BrowserTabStripController::BrowserTabStripController(TabStripModel* model, - SideTabStrip* tabstrip) +BrowserTabStripController::BrowserTabStripController(TabStripModel* model) : model_(model), - tabstrip_(tabstrip) { + tabstrip_(NULL) { model_->AddObserver(this); } BrowserTabStripController::~BrowserTabStripController() { + // When we get here the TabStrip is being deleted. We need to explicitly + // cancel the menu, otherwise it may try to invoke something on the tabstrip + // from it's destructor. + if (context_menu_contents_.get()) + context_menu_contents_->Cancel(); + model_->RemoveObserver(this); } -void BrowserTabStripController::InitFromModel() { +void BrowserTabStripController::InitFromModel(BaseTabStrip* tabstrip) { + tabstrip_ = tabstrip; // Walk the model, calling our insertion observer method for each item within // it. for (int i = 0; i < model_->count(); ++i) { @@ -93,111 +137,266 @@ void BrowserTabStripController::InitFromModel() { } 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; + TabStripModel::ContextMenuCommand command_id, + BaseTabRenderer* tab) const { + int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); + return model_->ContainsIndex(model_index) ? + model_->IsContextMenuCommandEnabled(model_index, command_id) : 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; + TabStripModel::ContextMenuCommand command_id, + BaseTabRenderer* tab) const { + int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); + return model_->ContainsIndex(model_index) ? + model_->IsContextMenuCommandChecked(model_index, command_id) : false; } void BrowserTabStripController::ExecuteCommandForTab( - TabStripModel::ContextMenuCommand command_id, int tab_index) { - if (model_->ContainsIndex(tab_index)) - model_->ExecuteContextMenuCommand(tab_index, command_id); + TabStripModel::ContextMenuCommand command_id, + BaseTabRenderer* tab) { + int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); + if (model_->ContainsIndex(model_index)) + model_->ExecuteContextMenuCommand(model_index, command_id); } -bool BrowserTabStripController::IsTabPinned(int tab_index) { - return model_->ContainsIndex(tab_index) ? - model_->IsTabPinned(tab_index) : false; +bool BrowserTabStripController::IsTabPinned(BaseTabRenderer* tab) { + return IsTabPinned(tabstrip_->GetModelIndexOfBaseTab(tab)); } -//////////////////////////////////////////////////////////////////////////////// -// BrowserTabStripController, SideTabStripModel implementation: +int BrowserTabStripController::GetCount() const { + return model_->count(); +} -SkBitmap BrowserTabStripController::GetIcon(int index) const { - return model_->GetTabContentsAt(index)->GetFavIcon(); +bool BrowserTabStripController::IsValidIndex(int index) const { + return model_->ContainsIndex(index); } -string16 BrowserTabStripController::GetTitle(int index) const { - return model_->GetTabContentsAt(index)->GetTitle(); +int BrowserTabStripController::GetSelectedIndex() const { + return model_->selected_index(); } -bool BrowserTabStripController::IsSelected(int index) const { - return model_->selected_index() == index; +bool BrowserTabStripController::IsTabSelected(int model_index) const { + return model_->selected_index() == model_index; } -SideTabStripModel::NetworkState - BrowserTabStripController::GetNetworkState(int index) const { - TabContents* contents = model_->GetTabContentsAt(index); - if (!contents || !contents->is_loading()) - return NetworkState_None; - if (contents->waiting_for_response()) - return NetworkState_Waiting; - return NetworkState_Loading; +bool BrowserTabStripController::IsTabPinned(int model_index) const { + return model_->ContainsIndex(model_index) && model_->IsTabPinned(model_index); +} + +bool BrowserTabStripController::IsNewTabPage(int model_index) const { + return model_->ContainsIndex(model_index) && + model_->GetTabContentsAt(model_index)->GetURL() == + GURL(chrome::kChromeUINewTabURL); } -void BrowserTabStripController::SelectTab(int index) { - model_->SelectTabContentsAt(index, true); +void BrowserTabStripController::SelectTab(int model_index) { + model_->SelectTabContentsAt(model_index, true); } -void BrowserTabStripController::CloseTab(int index) { - model_->CloseTabContentsAt(index); +void BrowserTabStripController::CloseTab(int model_index) { + model_->CloseTabContentsAt(model_index); } -void BrowserTabStripController::ShowContextMenu(int index, +void BrowserTabStripController::ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p) { - context_menu_contents_.reset(new TabContextMenuContents(index, this)); + context_menu_contents_.reset(new TabContextMenuContents(tab, this)); context_menu_contents_->RunMenuAt(p); } +void BrowserTabStripController::UpdateLoadingAnimations() { + // Don't use the model count here as it's possible for this to be invoked + // before we've applied an update from the model (Browser::TabInsertedAt may + // be processed before us and invokes this). + for (int tab_index = 0, tab_count = tabstrip_->GetTabCount(); + tab_index < tab_count; ++tab_index) { + BaseTabRenderer* tab = tabstrip_->GetBaseTabAtTabIndex(tab_index); + int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); + if (model_->ContainsIndex(model_index)) { + TabContents* contents = model_->GetTabContentsAt(model_index); + tab->UpdateLoadingAnimation(TabContentsNetworkState(contents)); + } + } +} + +int BrowserTabStripController::HasAvailableDragActions() const { + return model_->delegate()->GetDragActions(); +} + +void BrowserTabStripController::PerformDrop(bool drop_before, + int index, + const GURL& url) { + if (drop_before) { + UserMetrics::RecordAction(UserMetricsAction("Tab_DropURLBetweenTabs"), + model_->profile()); + + // Insert a new tab. + TabContents* contents = model_->delegate()->CreateTabContentsForURL( + url, GURL(), model_->profile(), PageTransition::TYPED, false, NULL); + model_->AddTabContents(contents, index, false, + PageTransition::GENERATED, true); + } else { + UserMetrics::RecordAction(UserMetricsAction("Tab_DropURLOnTab"), + model_->profile()); + + model_->GetTabContentsAt(index)->controller().LoadURL( + url, GURL(), PageTransition::GENERATED); + model_->SelectTabContentsAt(index, true); + } +} + +bool BrowserTabStripController::IsCompatibleWith(BaseTabStrip* other) const { + Profile* other_profile = + static_cast<BrowserTabStripController*>(other->controller())->profile(); + return other_profile == profile(); +} + +void BrowserTabStripController::CreateNewTab() { + // TODO(jcampan): if we decide to keep the app launcher as the default + // behavior for the new tab button, we should add a method + // on the TabStripDelegate to do so. + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsPanel)) { + NavigationController& controller = + model_->GetSelectedTabContents()->controller(); + AppLauncher::ShowForNewTab( + Browser::GetBrowserForController(&controller, NULL), std::string()); + return; + } + UserMetrics::RecordAction(UserMetricsAction("NewTab_Button"), + model_->profile()); + model_->delegate()->AddBlankTab(true); +} + //////////////////////////////////////////////////////////////////////////////// // BrowserTabStripController, TabStripModelObserver implementation: -void BrowserTabStripController::TabInsertedAt(TabContents* contents, int index, +void BrowserTabStripController::TabInsertedAt(TabContents* contents, + int model_index, bool foreground) { - tabstrip_->AddTabAt(index); + DCHECK(contents); + DCHECK(model_index == TabStripModel::kNoTab || + model_->ContainsIndex(model_index)); + // This tab may be attached to another browser window, we should notify + // renderer. + contents->render_view_host()->UpdateBrowserWindowId( + contents->controller().window_id().id()); + + TabRendererData data; + SetTabRendererDataFromModel(contents, model_index, &data); + tabstrip_->AddTabAt(model_index, foreground, data); } void BrowserTabStripController::TabDetachedAt(TabContents* contents, - int index) { - tabstrip_->RemoveTabAt(index); + int model_index) { + tabstrip_->RemoveTabAt(model_index); } void BrowserTabStripController::TabSelectedAt(TabContents* old_contents, - TabContents* contents, int index, + TabContents* contents, + int model_index, bool user_gesture) { - tabstrip_->SelectTabAt(index); + tabstrip_->SelectTabAt(model_->GetIndexOfTabContents(old_contents), + model_index); } -void BrowserTabStripController::TabMoved(TabContents* contents, int from_index, - int to_index) { +void BrowserTabStripController::TabMoved(TabContents* contents, + int from_model_index, + int to_model_index) { + // Update the data first as the pinned state may have changed. + TabRendererData data; + SetTabRendererDataFromModel(contents, to_model_index, &data); + tabstrip_->SetTabData(from_model_index, data); + + tabstrip_->MoveTab(from_model_index, to_model_index); } -void BrowserTabStripController::TabChangedAt(TabContents* contents, int index, +void BrowserTabStripController::TabChangedAt(TabContents* contents, + int model_index, TabChangeType change_type) { - tabstrip_->UpdateTabAt(index); + if (change_type == TITLE_NOT_LOADING) { + tabstrip_->TabTitleChangedNotLoading(model_index); + // We'll receive another notification of the change asynchronously. + return; + } + + SetTabDataAt(contents, model_index); } void BrowserTabStripController::TabReplacedAt(TabContents* old_contents, TabContents* new_contents, - int index) { + int model_index) { + SetTabDataAt(new_contents, model_index); } void BrowserTabStripController::TabPinnedStateChanged(TabContents* contents, - int index) { + int model_index) { + // Currently none of the renderers render pinned state differently. +} + +void BrowserTabStripController::TabMiniStateChanged( + TabContents* contents, + int model_index) { + SetTabDataAt(contents, model_index); } void BrowserTabStripController::TabBlockedStateChanged(TabContents* contents, - int index) { + int model_index) { + SetTabDataAt(contents, model_index); +} + +void BrowserTabStripController::SetTabDataAt(TabContents* contents, + int model_index) { + TabRendererData data; + SetTabRendererDataFromModel(contents, model_index, &data); + tabstrip_->SetTabData(model_index, data); +} + +void BrowserTabStripController::SetTabRendererDataFromModel( + TabContents* contents, + int model_index, + TabRendererData* data) { + SkBitmap* app_icon = contents->GetExtensionAppIcon(); + if (app_icon) + data->favicon = *app_icon; + else + data->favicon = contents->GetFavIcon(); + data->network_state = TabContentsNetworkState(contents); + data->title = contents->GetTitle(); + data->loading = contents->is_loading(); + data->crashed = contents->is_crashed(); + data->off_the_record = contents->profile()->IsOffTheRecord(); + data->show_icon = contents->ShouldDisplayFavIcon(); + data->mini = model_->IsMiniTab(model_index); + data->blocked = model_->IsTabBlocked(model_index); + data->phantom = model_->IsPhantomTab(model_index); + data->app = contents->is_app(); } +void BrowserTabStripController::StartHighlightTabsForCommand( + TabStripModel::ContextMenuCommand command_id, + BaseTabRenderer* tab) { + if (command_id == TabStripModel::CommandCloseTabsOpenedBy || + command_id == TabStripModel::CommandCloseOtherTabs || + command_id == TabStripModel::CommandCloseTabsToRight) { + int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); + if (IsValidIndex(model_index)) { + std::vector<int> indices = + model_->GetIndicesClosedByCommand(model_index, command_id); + for (std::vector<int>::const_iterator i = indices.begin(); + i != indices.end(); ++i) { + tabstrip_->StartHighlight(*i); + } + } + } +} + +void BrowserTabStripController::StopHighlightTabsForCommand( + TabStripModel::ContextMenuCommand command_id, + BaseTabRenderer* tab) { + if (command_id == TabStripModel::CommandCloseTabsOpenedBy || + command_id == TabStripModel::CommandCloseTabsToRight || + command_id == TabStripModel::CommandCloseOtherTabs) { + // Just tell all Tabs to stop pulsing - it's safe. + tabstrip_->StopAllHighlighting(); + } +} diff --git a/chrome/browser/views/tabs/browser_tab_strip_controller.h b/chrome/browser/views/tabs/browser_tab_strip_controller.h index f4a0295..c15386b 100644 --- a/chrome/browser/views/tabs/browser_tab_strip_controller.h +++ b/chrome/browser/views/tabs/browser_tab_strip_controller.h @@ -7,58 +7,94 @@ #include "base/scoped_ptr.h" #include "chrome/browser/tabs/tab_strip_model.h" -#include "chrome/browser/views/tabs/side_tab_strip_model.h" +#include "chrome/browser/views/tabs/tab_strip_controller.h" -class SideTabStrip; +class BaseTabRenderer; +class BaseTabStrip; -// An implementation of SideTabStripModel that sources data from -// the TabContentses in a TabStripModel. -class BrowserTabStripController : public SideTabStripModel, +struct TabRendererData; + +// An implementation of TabStripController that sources data from the +// TabContentses in a TabStripModel. +class BrowserTabStripController : public TabStripController, public TabStripModelObserver { public: - BrowserTabStripController(TabStripModel* model, SideTabStrip* tabstrip); + explicit BrowserTabStripController(TabStripModel* model); virtual ~BrowserTabStripController(); - void InitFromModel(); + void InitFromModel(BaseTabStrip* tabstrip); + + TabStripModel* model() const { return model_; } bool IsCommandEnabledForTab(TabStripModel::ContextMenuCommand command_id, - int tab_index) const; + BaseTabRenderer* tab) const; bool IsCommandCheckedForTab(TabStripModel::ContextMenuCommand command_id, - int tab_index) const; + BaseTabRenderer* tab) const; void ExecuteCommandForTab(TabStripModel::ContextMenuCommand command_id, - int tab_index); - bool IsTabPinned(int tab_index); - - // SideTabStripModel implementation: - virtual SkBitmap GetIcon(int index) const; - virtual string16 GetTitle(int index) const; - virtual bool IsSelected(int index) const; - 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); + BaseTabRenderer* tab); + bool IsTabPinned(BaseTabRenderer* tab); + + // TabStripController implementation: + virtual int GetCount() const; + virtual bool IsValidIndex(int model_index) const; + virtual int GetSelectedIndex() const; + virtual bool IsTabSelected(int model_index) const; + virtual bool IsTabPinned(int model_index) const; + virtual bool IsNewTabPage(int model_index) const; + virtual void SelectTab(int model_index); + virtual void CloseTab(int model_index); + virtual void ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p); + virtual void UpdateLoadingAnimations(); + virtual int HasAvailableDragActions() const; + virtual void PerformDrop(bool drop_before, int index, const GURL& url); + virtual bool IsCompatibleWith(BaseTabStrip* other) const; + virtual void CreateNewTab(); // TabStripModelObserver implementation: - virtual void TabInsertedAt(TabContents* contents, int index, + virtual void TabInsertedAt(TabContents* contents, + int model_index, bool foreground); - virtual void TabDetachedAt(TabContents* contents, int index); + virtual void TabDetachedAt(TabContents* contents, int model_index); virtual void TabSelectedAt(TabContents* old_contents, - TabContents* contents, int index, + TabContents* contents, + int model_index, bool user_gesture); - virtual void TabMoved(TabContents* contents, int from_index, - int to_index); - virtual void TabChangedAt(TabContents* contents, int index, + virtual void TabMoved(TabContents* contents, + int from_model_index, + int to_model_index); + virtual void TabChangedAt(TabContents* contents, + int model_index, TabChangeType change_type); virtual void TabReplacedAt(TabContents* old_contents, - TabContents* new_contents, int index); - virtual void TabPinnedStateChanged(TabContents* contents, int index); - virtual void TabBlockedStateChanged(TabContents* contents, int index); + TabContents* new_contents, + int model_index); + virtual void TabPinnedStateChanged(TabContents* contents, int model_index); + virtual void TabMiniStateChanged(TabContents* contents, int model_index); + virtual void TabBlockedStateChanged(TabContents* contents, int model_index); private: class TabContextMenuContents; + // Invokes tabstrip_->SetTabData. + void SetTabDataAt(TabContents* contents, int model_index); + + // Sets the TabRendererData from the TabStripModel. + void SetTabRendererDataFromModel(TabContents* contents, + int model_index, + TabRendererData* data); + + void StartHighlightTabsForCommand( + TabStripModel::ContextMenuCommand command_id, + BaseTabRenderer* tab); + void StopHighlightTabsForCommand( + TabStripModel::ContextMenuCommand command_id, + BaseTabRenderer* tab); + + Profile* profile() const { return model_->profile(); } + TabStripModel* model_; - SideTabStrip* tabstrip_; + + BaseTabStrip* tabstrip_; // If non-NULL it means we're showing a menu for the tab. scoped_ptr<TabContextMenuContents> context_menu_contents_; diff --git a/chrome/browser/views/tabs/dragged_tab_controller.cc b/chrome/browser/views/tabs/dragged_tab_controller.cc index 61b6686..3a4ee25 100644 --- a/chrome/browser/views/tabs/dragged_tab_controller.cc +++ b/chrome/browser/views/tabs/dragged_tab_controller.cc @@ -20,6 +20,7 @@ #include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/views/frame/browser_view.h" +#include "chrome/browser/views/tabs/browser_tab_strip_controller.h" #include "chrome/browser/views/tabs/dragged_tab_view.h" #include "chrome/browser/views/tabs/native_view_photobooth.h" #include "chrome/browser/views/tabs/tab.h" @@ -322,11 +323,11 @@ DraggedTabController::DraggedTabController(Tab* source_tab, old_focused_view_(NULL), in_destructor_(false), last_move_screen_x_(0), - mini_(source_tabstrip->model()->IsMiniTab(source_model_index_)), - pinned_(source_tabstrip->model()->IsTabPinned(source_model_index_)), + mini_(source_tab->data().mini), + pinned_(source_tabstrip->IsTabPinned(source_tab)), started_drag_(false) { SetDraggedContents( - source_tabstrip_->model()->GetTabContentsAt(source_model_index_)); + GetModel(source_tabstrip_)->GetTabContentsAt(source_model_index_)); // Listen for Esc key presses. MessageLoopForUI::current()->AddObserver(this); } @@ -653,7 +654,7 @@ void DraggedTabController::MoveAttachedTab(const gfx::Point& screen_point) { gfx::Point dragged_view_point = GetAttachedTabDragPoint(screen_point); - TabStripModel* attached_model = attached_tabstrip_->model(); + TabStripModel* attached_model = GetModel(attached_tabstrip_); int from_index = attached_model->GetIndexOfTabContents(dragged_contents_); // Determine the horizontal move threshold. This is dependent on the width @@ -738,7 +739,7 @@ TabStrip* DraggedTabController::GetTabStripForPoint( return NULL; TabStrip* other_tabstrip = browser->tabstrip()->AsTabStrip(); - if (!other_tabstrip->IsCompatibleWith(source_tabstrip_)) + if (!other_tabstrip->controller()->IsCompatibleWith(source_tabstrip_)) return NULL; return GetTabStripIfItContains(other_tabstrip, screen_point); } @@ -802,8 +803,8 @@ void DraggedTabController::Attach(TabStrip* attached_tabstrip, gfx::Rect bounds = GetDraggedViewTabStripBounds(screen_point); int index = GetInsertionIndexForDraggedBounds(bounds, false); attached_tabstrip_->set_attaching_dragged_tab(true); - attached_tabstrip_->model()->InsertTabContentsAt(index, dragged_contents_, - true, false, pinned_); + GetModel(attached_tabstrip_)->InsertTabContentsAt(index, dragged_contents_, + true, false, pinned_); attached_tabstrip_->set_attaching_dragged_tab(false); tab = GetTabMatchingDraggedContents(attached_tabstrip_); @@ -828,7 +829,8 @@ void DraggedTabController::Detach() { dragged_contents_->set_capturing_contents(true); // Update the Model. - TabStripModel* attached_model = attached_tabstrip_->model(); + TabRendererData tab_data = attached_tab_->data(); + TabStripModel* attached_model = GetModel(attached_tabstrip_); int index = attached_model->GetIndexOfTabContents(dragged_contents_); DCHECK(index != -1); attached_model->DetachTabContentsAt(index); @@ -845,7 +847,7 @@ void DraggedTabController::Detach() { } // Create the dragged view. - EnsureDraggedView(); + EnsureDraggedView(tab_data); view_->Attach(attached_tab_->width()); view_->Detach(photobooth_.get()); @@ -896,14 +898,14 @@ int DraggedTabController::GetInsertionIndexForDraggedBounds( } if (index == -1) { if (adjusted_bounds.right() > right_tab_x) - index = attached_tabstrip_->model()->count(); + index = GetModel(attached_tabstrip_)->count(); else index = 0; } - index = attached_tabstrip_->model()->ConstrainInsertionIndex(index, mini_); + index = GetModel(attached_tabstrip_)->ConstrainInsertionIndex(index, mini_); if (is_tab_attached && mini_ && - index == attached_tabstrip_->model()->IndexOfFirstNonMiniTab()) { + index == GetModel(attached_tabstrip_)->IndexOfFirstNonMiniTab()) { index--; } return index; @@ -950,7 +952,7 @@ gfx::Point DraggedTabController::GetAttachedTabDragPoint( Tab* DraggedTabController::GetTabMatchingDraggedContents( TabStrip* tabstrip) const { - int model_index = tabstrip->model()->GetIndexOfTabContents(dragged_contents_); + int model_index = GetModel(tabstrip)->GetIndexOfTabContents(dragged_contents_); return model_index == TabStripModel::kNoTab ? NULL : tabstrip->GetTabAtModelIndex(model_index); } @@ -1011,22 +1013,22 @@ void DraggedTabController::RevertDrag() { // We save this here because code below will modify |attached_tabstrip_|. bool restore_frame = attached_tabstrip_ != source_tabstrip_; if (attached_tabstrip_) { - int index = attached_tabstrip_->model()->GetIndexOfTabContents( + int index = GetModel(attached_tabstrip_)->GetIndexOfTabContents( dragged_contents_); if (attached_tabstrip_ != source_tabstrip_) { // The Tab was inserted into another TabStrip. We need to put it back // into the original one. - attached_tabstrip_->model()->DetachTabContentsAt(index); + GetModel(attached_tabstrip_)->DetachTabContentsAt(index); // TODO(beng): (Cleanup) seems like we should use Attach() for this // somehow. attached_tabstrip_ = source_tabstrip_; - source_tabstrip_->model()->InsertTabContentsAt(source_model_index_, + GetModel(source_tabstrip_)->InsertTabContentsAt(source_model_index_, dragged_contents_, true, false, pinned_); } else { // The Tab was moved within the TabStrip where the drag was initiated. // Move it back to the starting location. source_tabstrip_->StoppedDraggingTab(attached_tab_); - source_tabstrip_->model()->MoveTabContentsAt(index, source_model_index_, + GetModel(source_tabstrip_)->MoveTabContentsAt(index, source_model_index_, true); } } else { @@ -1036,7 +1038,7 @@ void DraggedTabController::RevertDrag() { // The Tab was detached from the TabStrip where the drag began, and has not // been attached to any other TabStrip. We need to put it back into the // source TabStrip. - source_tabstrip_->model()->InsertTabContentsAt(source_model_index_, + GetModel(source_tabstrip_)->InsertTabContentsAt(source_model_index_, dragged_contents_, true, false, pinned_); } @@ -1063,47 +1065,48 @@ void DraggedTabController::CompleteDrag() { attached_tabstrip_->StoppedDraggingTab(attached_tab_); } else { if (dock_info_.type() != DockInfo::NONE) { + Profile* profile = GetModel(source_tabstrip_)->profile(); switch (dock_info_.type()) { case DockInfo::LEFT_OF_WINDOW: UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Left"), - source_tabstrip_->model()->profile()); + profile); break; case DockInfo::RIGHT_OF_WINDOW: UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Right"), - source_tabstrip_->model()->profile()); + profile); break; case DockInfo::BOTTOM_OF_WINDOW: UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Bottom"), - source_tabstrip_->model()->profile()); + profile); break; case DockInfo::TOP_OF_WINDOW: UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Top"), - source_tabstrip_->model()->profile()); + profile); break; case DockInfo::MAXIMIZE: UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Maximize"), - source_tabstrip_->model()->profile()); + profile); break; case DockInfo::LEFT_HALF: UserMetrics::RecordAction(UserMetricsAction("DockingWindow_LeftHalf"), - source_tabstrip_->model()->profile()); + profile); break; case DockInfo::RIGHT_HALF: UserMetrics::RecordAction( UserMetricsAction("DockingWindow_RightHalf"), - source_tabstrip_->model()->profile()); + profile); break; case DockInfo::BOTTOM_HALF: UserMetrics::RecordAction( UserMetricsAction("DockingWindow_BottomHalf"), - source_tabstrip_->model()->profile()); + profile); break; default: @@ -1126,7 +1129,7 @@ void DraggedTabController::CompleteDrag() { window_bounds.set_x(window_bounds.x() - window_bounds.width()); } Browser* new_browser = - source_tabstrip_->model()->delegate()->CreateNewStripWithContents( + GetModel(source_tabstrip_)->delegate()->CreateNewStripWithContents( dragged_contents_, window_bounds, dock_info_); TabStripModel* new_model = new_browser->tabstrip_model(); new_model->SetTabPinned(new_model->GetIndexOfTabContents(dragged_contents_), @@ -1137,13 +1140,12 @@ void DraggedTabController::CompleteDrag() { CleanUpHiddenFrame(); } -void DraggedTabController::EnsureDraggedView() { +void DraggedTabController::EnsureDraggedView(const TabRendererData& data) { if (!view_.get()) { gfx::Rect tab_bounds; dragged_contents_->GetContainerBounds(&tab_bounds); - TabRenderer* renderer = new TabRenderer(); - renderer->UpdateData(dragged_contents_, false, false); - renderer->set_mini(mini_); + BaseTabRenderer* renderer = source_tabstrip_->CreateTabForDragging(); + renderer->SetData(data); // DraggedTabView takes ownership of renderer. view_.reset(new DraggedTabView(renderer, mouse_offset_, tab_bounds.size(), @@ -1172,7 +1174,7 @@ gfx::Rect DraggedTabController::GetViewScreenBounds(views::View* view) const { int DraggedTabController::NormalizeIndexToAttachedTabStrip(int index) const { DCHECK(attached_tabstrip_) << "Can only be called when attached!"; - TabStripModel* attached_model = attached_tabstrip_->model(); + TabStripModel* attached_model = GetModel(attached_tabstrip_); if (index >= attached_model->count()) return attached_model->count() - 1; if (index == TabStripModel::kNoTab) @@ -1201,8 +1203,8 @@ void DraggedTabController::HideFrame() { void DraggedTabController::CleanUpHiddenFrame() { // If the model we started dragging from is now empty, we must ask the // delegate to close the frame. - if (!source_tabstrip_->model()->HasNonPhantomTabs()) - source_tabstrip_->model()->delegate()->CloseFrameAfterDragSession(); + if (!GetModel(source_tabstrip_)->HasNonPhantomTabs()) + GetModel(source_tabstrip_)->delegate()->CloseFrameAfterDragSession(); } void DraggedTabController::DockDisplayerDestroyed( @@ -1248,3 +1250,8 @@ void DraggedTabController::BringWindowUnderMouseToFront() { #endif } } + +TabStripModel* DraggedTabController::GetModel(BaseTabStrip* tabstrip) const { + return static_cast<BrowserTabStripController*>(tabstrip->controller())-> + model(); +} diff --git a/chrome/browser/views/tabs/dragged_tab_controller.h b/chrome/browser/views/tabs/dragged_tab_controller.h index 4e68605..f4aca80 100644 --- a/chrome/browser/views/tabs/dragged_tab_controller.h +++ b/chrome/browser/views/tabs/dragged_tab_controller.h @@ -15,10 +15,14 @@ namespace views { class View; } +class BaseTabStrip; class DraggedTabView; class NativeViewPhotobooth; class Tab; class TabStrip; +class TabStripModel; + +struct TabRendererData; /////////////////////////////////////////////////////////////////////////////// // @@ -199,7 +203,7 @@ class DraggedTabController : public TabContentsDelegate, void CompleteDrag(); // Create the DraggedTabView, if it does not yet exist. - void EnsureDraggedView(); + void EnsureDraggedView(const TabRendererData& data); // Utility for getting the mouse position in screen coordinates. gfx::Point GetCursorScreenPoint() const; @@ -222,6 +226,9 @@ class DraggedTabController : public TabContentsDelegate, void BringWindowUnderMouseToFront(); + // Returns the TabStripModel for the specified tabstrip. + TabStripModel* GetModel(BaseTabStrip* tabstrip) const; + // Handles registering for notifications. NotificationRegistrar registrar_; diff --git a/chrome/browser/views/tabs/side_tab.cc b/chrome/browser/views/tabs/side_tab.cc index 54795fb..c134b59 100644 --- a/chrome/browser/views/tabs/side_tab.cc +++ b/chrome/browser/views/tabs/side_tab.cc @@ -41,10 +41,9 @@ SkBitmap* SideTab::close_button_p_ = NULL; //////////////////////////////////////////////////////////////////////////////// // SideTab, public: -SideTab::SideTab(SideTabModel* model) - : model_(model), +SideTab::SideTab(TabController* controller) + : BaseTabRenderer(controller), close_button_(NULL), - current_state_(SideTabStripModel::NetworkState_None), loading_animation_frame_(0) { InitClass(); @@ -64,31 +63,6 @@ SideTab::SideTab(SideTabModel* model) SideTab::~SideTab() { } -void SideTab::SetNetworkState(SideTabStripModel::NetworkState state) { - if (current_state_ != state) { - // The waiting animation is the reverse of the loading animation, but at a - // different rate - the following reverses and scales the animation_frame_ - // so that the frame is at an equivalent position when going from one - // animation to the other. - if (current_state_ == SideTabStripModel::NetworkState_Waiting && - current_state_ == SideTabStripModel::NetworkState_Loading) { - loading_animation_frame_ = loading_animation_frame_count - - (loading_animation_frame_ / waiting_to_loading_frame_count_ratio); - } - current_state_ = state; - } - - if (current_state_ != SideTabStripModel::NetworkState_None) { - loading_animation_frame_ = ++loading_animation_frame_ % - ((current_state_ == SideTabStripModel::NetworkState_Waiting) ? - waiting_animation_frame_count : - loading_animation_frame_count); - } else { - loading_animation_frame_ = 0; - } - SchedulePaint(); -} - //////////////////////////////////////////////////////////////////////////////// // SideTab, AnimationDelegate implementation: @@ -109,7 +83,7 @@ void SideTab::AnimationEnded(const Animation* animation) { void SideTab::ButtonPressed(views::Button* sender, const views::Event& event) { DCHECK(sender == close_button_); - model_->CloseTab(this); + controller()->CloseTab(this); } //////////////////////////////////////////////////////////////////////////////// @@ -118,7 +92,7 @@ void SideTab::ButtonPressed(views::Button* sender, const views::Event& event) { void SideTab::ShowContextMenu(views::View* source, const gfx::Point& p, bool is_mouse_gesture) { - model_->ShowContextMenu(this, p); + controller()->ShowContextMenu(this, p); } //////////////////////////////////////////////////////////////////////////////// @@ -155,11 +129,11 @@ void SideTab::Paint(gfx::Canvas* canvas) { canvas->drawPath(tab_shape, paint); PaintIcon(canvas); - canvas->DrawStringInt(UTF16ToWideHack(model_->GetTitle(this)), *font_, + canvas->DrawStringInt(UTF16ToWideHack(data().title), *font_, SK_ColorBLACK, title_bounds_.x(), title_bounds_.y(), title_bounds_.width(), title_bounds_.height()); - if (!model_->IsSelected(this) && + if (!controller()->IsTabSelected(this) && GetThemeProvider()->ShouldUseNativeFrame()) { // Make sure un-selected tabs are somewhat transparent. SkPaint paint; @@ -192,10 +166,33 @@ void SideTab::OnMouseExited(const views::MouseEvent& event) { bool SideTab::OnMousePressed(const views::MouseEvent& event) { if (event.IsOnlyLeftMouseButton()) - model_->SelectTab(this); + controller()->SelectTab(this); return true; } +// TODO(sky): refactor to BaseTabRenderer. +void SideTab::AdvanceLoadingAnimation(TabRendererData::NetworkState state) { + // The waiting animation is the reverse of the loading animation, but at a + // different rate - the following reverses and scales the animation_frame_ + // so that the frame is at an equivalent position when going from one + // animation to the other. + if (state == TabRendererData::NETWORK_STATE_WAITING && + state == TabRendererData::NETWORK_STATE_LOADING) { + loading_animation_frame_ = loading_animation_frame_count - + (loading_animation_frame_ / waiting_to_loading_frame_count_ratio); + } + + if (state != TabRendererData::NETWORK_STATE_NONE) { + loading_animation_frame_ = ++loading_animation_frame_ % + ((state == TabRendererData::NETWORK_STATE_WAITING) ? + waiting_animation_frame_count : + loading_animation_frame_count); + } else { + loading_animation_frame_ = 0; + } + SchedulePaint(); +} + //////////////////////////////////////////////////////////////////////////////// // SideTab, private: @@ -219,8 +216,8 @@ void SideTab::FillTabShapePath(gfx::Path* path) { } void SideTab::PaintIcon(gfx::Canvas* canvas) { - if (current_state_ == SideTabStripModel::NetworkState_None) { - canvas->DrawBitmapInt(model_->GetIcon(this), 0, 0, kIconSize, kIconSize, + if (data().network_state == TabRendererData::NETWORK_STATE_NONE) { + canvas->DrawBitmapInt(data().favicon, 0, 0, kIconSize, kIconSize, icon_bounds_.x(), icon_bounds_.y(), icon_bounds_.width(), icon_bounds_.height(), false); } else { @@ -230,7 +227,7 @@ void SideTab::PaintIcon(gfx::Canvas* canvas) { void SideTab::PaintLoadingAnimation(gfx::Canvas* canvas) { SkBitmap* frames = - (current_state_ == SideTabStripModel::NetworkState_Waiting) ? + (data().network_state == TabRendererData::NETWORK_STATE_WAITING) ? waiting_animation_frames : loading_animation_frames; int image_size = frames->height(); int image_offset = loading_animation_frame_ * image_size; diff --git a/chrome/browser/views/tabs/side_tab.h b/chrome/browser/views/tabs/side_tab.h index 7c0e9d6..79fbac8 100644 --- a/chrome/browser/views/tabs/side_tab.h +++ b/chrome/browser/views/tabs/side_tab.h @@ -6,44 +6,22 @@ #define CHROME_BROWSER_VIEWS_TABS_SIDE_TAB_H_ #include "app/slide_animation.h" -#include "chrome/browser/views/tabs/side_tab_strip_model.h" -#include "third_party/skia/include/core/SkBitmap.h" +#include "chrome/browser/views/tabs/base_tab_renderer.h" #include "gfx/font.h" #include "views/controls/button/button.h" #include "views/view.h" class SideTab; +class TabStripController; -class SideTabModel { - public: - // Returns metadata about the specified |tab|. - virtual string16 GetTitle(SideTab* tab) const = 0; - virtual SkBitmap GetIcon(SideTab* tab) const = 0; - virtual bool IsSelected(SideTab* tab) const = 0; - - // Selects the tab. - virtual void SelectTab(SideTab* tab) = 0; - - // 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, +class SideTab : public BaseTabRenderer, public views::ContextMenuController, public views::ButtonListener, public AnimationDelegate { public: - explicit SideTab(SideTabModel* model); + explicit SideTab(TabController* controller); virtual ~SideTab(); - // Sets the current network state of the tab. The tab renders different - // animations in place of the icon when different types of network activity - // are occurring. - void SetNetworkState(SideTabStripModel::NetworkState state); - // AnimationDelegate implementation: virtual void AnimationProgressed(const Animation* animation); virtual void AnimationCanceled(const Animation* animation); @@ -65,6 +43,10 @@ class SideTab : public views::View, virtual void OnMouseExited(const views::MouseEvent& event); virtual bool OnMousePressed(const views::MouseEvent& event); + protected: + // BaseTabRenderer overrides. + virtual void AdvanceLoadingAnimation(TabRendererData::NetworkState state); + private: void FillTabShapePath(gfx::Path* path); @@ -75,8 +57,6 @@ class SideTab : public views::View, // Loads class-specific resources. static void InitClass(); - SideTabModel* model_; - gfx::Rect icon_bounds_; gfx::Rect title_bounds_; @@ -91,9 +71,6 @@ class SideTab : public views::View, static SkBitmap* close_button_h_; static SkBitmap* close_button_p_; - // The current network state for this tab. - SideTabStripModel::NetworkState current_state_; - // The current index into the Animation image strip. int loading_animation_frame_; diff --git a/chrome/browser/views/tabs/side_tab_strip.cc b/chrome/browser/views/tabs/side_tab_strip.cc index c7a343c..41dbc53 100644 --- a/chrome/browser/views/tabs/side_tab_strip.cc +++ b/chrome/browser/views/tabs/side_tab_strip.cc @@ -4,6 +4,7 @@ #include "chrome/browser/views/tabs/side_tab_strip.h" +#include "chrome/browser/views/tabs/side_tab.h" #include "chrome/browser/view_ids.h" namespace { @@ -15,99 +16,107 @@ const int kTabStripInset = 3; //////////////////////////////////////////////////////////////////////////////// // SideTabStrip, public: -SideTabStrip::SideTabStrip() { +SideTabStrip::SideTabStrip(TabStripController* controller) + : BaseTabStrip(controller) { SetID(VIEW_ID_TAB_STRIP); } SideTabStrip::~SideTabStrip() { } -void SideTabStrip::SetModel(SideTabStripModel* model) { - model_.reset(model); +//////////////////////////////////////////////////////////////////////////////// +// SideTabStrip, BaseTabStrip implementation: + +int SideTabStrip::GetPreferredHeight() { + return 0; } -void SideTabStrip::AddTabAt(int index) { - SideTab* tab = new SideTab(this); - AddChildView(tab); - Layout(); +void SideTabStrip::SetBackgroundOffset(const gfx::Point& offset) { } -void SideTabStrip::RemoveTabAt(int index) { - View* v = GetChildViewAt(index); - RemoveChildView(v); - delete v; - Layout(); +bool SideTabStrip::IsPositionInWindowCaption(const gfx::Point& point) { + return GetViewForPoint(point) == this; } -void SideTabStrip::SelectTabAt(int index) { - GetChildViewAt(index)->SchedulePaint(); +void SideTabStrip::SetDraggedTabBounds(int tab_index, + const gfx::Rect& tab_bounds) { } -void SideTabStrip::UpdateTabAt(int index) { - GetChildViewAt(index)->SchedulePaint(); +bool SideTabStrip::IsDragSessionActive() const { + return false; } -//////////////////////////////////////////////////////////////////////////////// -// SideTabStrip, SideTabModel implementation: +bool SideTabStrip::IsAnimating() const { + return false; +} -string16 SideTabStrip::GetTitle(SideTab* tab) const { - return model_->GetTitle(GetIndexOfSideTab(tab)); +TabStrip* SideTabStrip::AsTabStrip() { + return NULL; } -SkBitmap SideTabStrip::GetIcon(SideTab* tab) const { - return model_->GetIcon(GetIndexOfSideTab(tab)); +void SideTabStrip::StartHighlight(int model_index) { } -bool SideTabStrip::IsSelected(SideTab* tab) const { - return model_->IsSelected(GetIndexOfSideTab(tab)); +void SideTabStrip::StopAllHighlighting() { } -void SideTabStrip::SelectTab(SideTab* tab) { - model_->SelectTab(GetIndexOfSideTab(tab)); +BaseTabRenderer* SideTabStrip::GetBaseTabAtModelIndex(int model_index) const { + return static_cast<BaseTabRenderer*>(GetChildViewAt(model_index)); } -void SideTabStrip::CloseTab(SideTab* tab) { - model_->CloseTab(GetIndexOfSideTab(tab)); +BaseTabRenderer* SideTabStrip::GetBaseTabAtTabIndex(int tab_index) const { + return static_cast<BaseTabRenderer*>(GetChildViewAt(tab_index)); } -void SideTabStrip::ShowContextMenu(SideTab* tab, const gfx::Point& p) { - model_->ShowContextMenu(GetIndexOfSideTab(tab), p); +int SideTabStrip::GetTabCount() const { + return GetChildViewCount(); } -//////////////////////////////////////////////////////////////////////////////// -// SideTabStrip, BaseTabStrip implementation: +BaseTabRenderer* SideTabStrip::CreateTabForDragging() { + return new SideTab(NULL); +} -int SideTabStrip::GetPreferredHeight() { - return 0; +int SideTabStrip::GetModelIndexOfBaseTab(const BaseTabRenderer* tab) const { + return GetChildIndex(tab); } -void SideTabStrip::SetBackgroundOffset(const gfx::Point& offset) { +void SideTabStrip::AddTabAt(int model_index, + bool foreground, + const TabRendererData& data) { + SideTab* tab = new SideTab(this); + AddChildView(tab); + Layout(); } -bool SideTabStrip::IsPositionInWindowCaption(const gfx::Point& point) { - return GetViewForPoint(point) == this; +void SideTabStrip::RemoveTabAt(int index) { + View* v = GetChildViewAt(index); + RemoveChildView(v); + delete v; + Layout(); } -void SideTabStrip::SetDraggedTabBounds(int tab_index, - const gfx::Rect& tab_bounds) { +void SideTabStrip::SelectTabAt(int old_model_index, int new_model_index) { + GetChildViewAt(new_model_index)->SchedulePaint(); } -bool SideTabStrip::IsDragSessionActive() const { - return false; +void SideTabStrip::MoveTab(int from_model_index, int to_model_index) { } -void SideTabStrip::UpdateLoadingAnimations() { - int count = GetChildViewCount(); - for (int i = 0; i < count; ++i) - GetSideTabAt(i)->SetNetworkState(model_->GetNetworkState(i)); +void SideTabStrip::TabTitleChangedNotLoading(int model_index) { } -bool SideTabStrip::IsAnimating() const { - return false; +void SideTabStrip::SetTabData(int model_index, const TabRendererData& data) { } -TabStrip* SideTabStrip::AsTabStrip() { - return NULL; +void SideTabStrip::MaybeStartDrag(BaseTabRenderer* tab, + const views::MouseEvent& event) { +} + +void SideTabStrip::ContinueDrag(const views::MouseEvent& event) { +} + +bool SideTabStrip::EndDrag(bool canceled) { + return false; } //////////////////////////////////////////////////////////////////////////////// @@ -128,14 +137,3 @@ void SideTabStrip::Layout() { gfx::Size SideTabStrip::GetPreferredSize() { return gfx::Size(kTabStripWidth, 0); } - -//////////////////////////////////////////////////////////////////////////////// -// SideTabStrip, private: - -int SideTabStrip::GetIndexOfSideTab(SideTab* tab) const { - return GetChildIndex(tab); -} - -SideTab* SideTabStrip::GetSideTabAt(int index) const { - return static_cast<SideTab*>(GetChildViewAt(index)); -} diff --git a/chrome/browser/views/tabs/side_tab_strip.h b/chrome/browser/views/tabs/side_tab_strip.h index 558d1a1..892004d 100644 --- a/chrome/browser/views/tabs/side_tab_strip.h +++ b/chrome/browser/views/tabs/side_tab_strip.h @@ -5,43 +5,15 @@ #ifndef CHROME_BROWSER_VIEWS_TABS_SIDE_TAB_STRIP_H_ #define CHROME_BROWSER_VIEWS_TABS_SIDE_TAB_STRIP_H_ -#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/views/tabs/base_tab_strip.h" -#include "chrome/browser/views/tabs/side_tab.h" -class Profile; -class SideTabStripModel; +struct TabRendererData; -class SideTabStrip : public BaseTabStrip, - public SideTabModel { +class SideTabStrip : public BaseTabStrip { public: - SideTabStrip(); + explicit SideTabStrip(TabStripController* controller); virtual ~SideTabStrip(); - // Associate a model with this SideTabStrip. The SideTabStrip owns its model. - void SetModel(SideTabStripModel* model); - - // Notifies the SideTabStrip that a tab was added in the model at |index|. - void AddTabAt(int index); - - // Notifies the SideTabStrip that a tab was removed from the model at |index|. - void RemoveTabAt(int index); - - // Notifies the SideTabStrip that a tab was selected in the model at |index|. - void SelectTabAt(int index); - - // Notifies the SideTabStrip that the tab at |index| needs to be redisplayed - // since some of its metadata has changed. - void UpdateTabAt(int index); - - // SideTabModel implementation: - virtual string16 GetTitle(SideTab* tab) const; - virtual SkBitmap GetIcon(SideTab* tab) const; - 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(); virtual void SetBackgroundOffset(const gfx::Point& offset); @@ -49,23 +21,34 @@ class SideTabStrip : public BaseTabStrip, virtual void SetDraggedTabBounds(int tab_index, const gfx::Rect& tab_bounds); virtual bool IsDragSessionActive() const; - virtual void UpdateLoadingAnimations(); virtual bool IsAnimating() const; virtual TabStrip* AsTabStrip(); + virtual void StartHighlight(int model_index); + virtual void StopAllHighlighting(); + virtual BaseTabRenderer* GetBaseTabAtModelIndex(int model_index) const; + virtual BaseTabRenderer* GetBaseTabAtTabIndex(int tab_index) const; + virtual int GetModelIndexOfBaseTab(const BaseTabRenderer* tab) const; + virtual int GetTabCount() const; + virtual BaseTabRenderer* CreateTabForDragging(); + virtual void AddTabAt(int model_index, + bool foreground, + const TabRendererData& data); + virtual void RemoveTabAt(int model_index); + virtual void SelectTabAt(int old_model_index, int new_model_index); + virtual void MoveTab(int from_model_index, int to_model_index); + virtual void TabTitleChangedNotLoading(int model_index); + virtual void SetTabData(int model_index, const TabRendererData& data); + virtual void MaybeStartDrag(BaseTabRenderer* tab, + const views::MouseEvent& event); + virtual void ContinueDrag(const views::MouseEvent& event); + virtual bool EndDrag(bool canceled); + // views::View overrides: virtual void Layout(); virtual gfx::Size GetPreferredSize(); private: - // Returns the model index of the specified |tab|. - int GetIndexOfSideTab(SideTab* tab) const; - - // Returns the SideTab at the specified |index|. - SideTab* GetSideTabAt(int index) const; - - scoped_ptr<SideTabStripModel> model_; - DISALLOW_COPY_AND_ASSIGN(SideTabStrip); }; diff --git a/chrome/browser/views/tabs/side_tab_strip_model.h b/chrome/browser/views/tabs/side_tab_strip_model.h deleted file mode 100644 index 835e5ba..0000000 --- a/chrome/browser/views/tabs/side_tab_strip_model.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2010 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_SIDE_TAB_STRIP_MODEL_H_ -#define CHROME_BROWSER_VIEWS_TABS_SIDE_TAB_STRIP_MODEL_H_ - -#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; - virtual bool IsSelected(int index) const = 0; - - // Different types of network activity for a tab. The NetworkState of a tab - // may be used to alter the UI (e.g. show different kinds of loading - // animations). - enum NetworkState { - NetworkState_None, // no network activity. - NetworkState_Waiting, // waiting for a connection. - NetworkState_Loading // connected, transferring data. - }; - - // Returns the NetworkState of the tab at the specified index. - virtual NetworkState GetNetworkState(int index) const = 0; - - // Select the tab at the specified index in the model. - virtual void SelectTab(int index) = 0; - - // 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 03487a9..8a7b731 100644 --- a/chrome/browser/views/tabs/tab.cc +++ b/chrome/browser/views/tabs/tab.cc @@ -26,87 +26,11 @@ static const SkScalar kTabCapWidth = 15; static const SkScalar kTabTopCurveWidth = 4; static const SkScalar kTabBottomCurveWidth = 3; -class Tab::TabContextMenuContents : public menus::SimpleMenuModel::Delegate { - public: - explicit TabContextMenuContents(Tab* tab) - : ALLOW_THIS_IN_INITIALIZER_LIST( - model_(this, tab->delegate()->IsTabPinned(tab))), - tab_(tab), - last_command_(TabStripModel::CommandFirst) { - Build(); - } - virtual ~TabContextMenuContents() { - menu_->CancelMenu(); - tab_->delegate()->StopAllHighlighting(); - } - - void RunMenuAt(const gfx::Point& point) { - // Save a pointer to delegate before we call RunMenuAt, because it runs a - // nested message loop that may not return until after we are deleted. - Tab::TabDelegate* delegate = tab_->delegate(); - menu_->RunMenuAt(point, views::Menu2::ALIGN_TOPLEFT); - // We could be gone now. Assume |this| is junk! - if (delegate) - delegate->StopAllHighlighting(); - } - - // Overridden from menus::SimpleMenuModel::Delegate: - virtual bool IsCommandIdChecked(int command_id) const { - return tab_ && tab_->delegate()->IsCommandCheckedForTab( - static_cast<TabStripModel::ContextMenuCommand>(command_id), - tab_); - } - virtual bool IsCommandIdEnabled(int command_id) const { - return tab_ && tab_->delegate()->IsCommandEnabledForTab( - static_cast<TabStripModel::ContextMenuCommand>(command_id), - tab_); - } - virtual bool GetAcceleratorForCommandId( - int command_id, - menus::Accelerator* accelerator) { - return tab_->GetWidget()->GetAccelerator(command_id, accelerator); - } - virtual void CommandIdHighlighted(int command_id) { - if (!tab_) - return; - - tab_->delegate()->StopHighlightTabsForCommand(last_command_, tab_); - last_command_ = static_cast<TabStripModel::ContextMenuCommand>(command_id); - tab_->delegate()->StartHighlightTabsForCommand(last_command_, tab_); - } - virtual void ExecuteCommand(int command_id) { - if (!tab_) - return; - tab_->delegate()->ExecuteCommandForTab( - static_cast<TabStripModel::ContextMenuCommand>(command_id), - tab_); - } - - private: - void Build() { - menu_.reset(new views::Menu2(&model_)); - } - - TabMenuModel model_; - scoped_ptr<views::Menu2> menu_; - - // The Tab the context menu was brought up for. Set to NULL when the menu - // is canceled. - Tab* tab_; - - // The last command that was selected, so that we can start/stop highlighting - // appropriately as the user moves through the menu. - TabStripModel::ContextMenuCommand last_command_; - - DISALLOW_COPY_AND_ASSIGN(TabContextMenuContents); -}; - /////////////////////////////////////////////////////////////////////////////// // Tab, public: -Tab::Tab(TabDelegate* delegate) - : TabRenderer(), - delegate_(delegate), +Tab::Tab(TabController* controller) + : TabRenderer(controller), closing_(false), dragging_(false) { close_button()->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_CLOSE_TAB)); @@ -122,7 +46,7 @@ Tab::~Tab() { // Tab, TabRenderer overrides: bool Tab::IsSelected() const { - return delegate_->IsTabSelected(this); + return controller() ? controller()->IsTabSelected(this) : true; } /////////////////////////////////////////////////////////////////////////////// @@ -143,15 +67,15 @@ bool Tab::OnMousePressed(const views::MouseEvent& event) { // it was in the background. bool just_selected = !IsSelected(); if (just_selected) { - delegate_->SelectTab(this); + controller()->SelectTab(this); } - delegate_->MaybeStartDrag(this, event); + controller()->MaybeStartDrag(this, event); } return true; } bool Tab::OnMouseDragged(const views::MouseEvent& event) { - delegate_->ContinueDrag(event); + controller()->ContinueDrag(event); return true; } @@ -161,14 +85,14 @@ void Tab::OnMouseReleased(const views::MouseEvent& event, bool canceled) { // In some cases, ending the drag will schedule the tab for destruction; if // so, bail immediately, since our members are already dead and we shouldn't // do anything else except drop the tab where it is. - if (delegate_->EndDrag(canceled)) + if (controller()->EndDrag(canceled)) return; // Close tab on middle click, but only if the button is released over the tab // (normal windows behavior is to discard presses of a UI element where the // releases happen off the element). if (event.IsMiddleMouseButton() && HitTest(event.location())) - delegate_->CloseTab(this); + controller()->CloseTab(this); } bool Tab::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) { @@ -204,8 +128,7 @@ bool Tab::GetAccessibleRole(AccessibilityTypes::Role* role) { void Tab::ShowContextMenu(views::View* source, const gfx::Point& p, bool is_mouse_gesture) { - context_menu_contents_.reset(new TabContextMenuContents(this)); - context_menu_contents_->RunMenuAt(p); + controller()->ShowContextMenu(this, p); } /////////////////////////////////////////////////////////////////////////////// @@ -213,7 +136,7 @@ void Tab::ShowContextMenu(views::View* source, void Tab::ButtonPressed(views::Button* sender, const views::Event& event) { if (sender == close_button()) - delegate_->CloseTab(this); + controller()->CloseTab(this); } /////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/browser/views/tabs/tab.h b/chrome/browser/views/tabs/tab.h index af7eb20..e5ab9b9 100644 --- a/chrome/browser/views/tabs/tab.h +++ b/chrome/browser/views/tabs/tab.h @@ -25,67 +25,9 @@ class Tab : public TabRenderer, public: static const std::string kTabClassName; - // An interface implemented by an object that can help this Tab complete - // various actions. The index parameter is the index of this Tab in the - // TabRenderer::Model. - class TabDelegate { - public: - // Returns true if the specified Tab is selected. - virtual bool IsTabSelected(const Tab* tab) const = 0; - - // Returns true if the specified Tab is pinned. - virtual bool IsTabPinned(const Tab* tab) const = 0; - - // Selects the specified Tab. - virtual void SelectTab(Tab* tab) = 0; - - // Closes the specified Tab. - virtual void CloseTab(Tab* tab) = 0; - - // Returns true if the specified command is enabled for the specified Tab. - 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; - - // Starts/Stops highlighting the tabs that will be affected by the - // specified command for the specified Tab. - virtual void StartHighlightTabsForCommand( - TabStripModel::ContextMenuCommand command_id, Tab* tab) = 0; - virtual void StopHighlightTabsForCommand( - TabStripModel::ContextMenuCommand command_id, Tab* tab) = 0; - virtual void StopAllHighlighting() = 0; - - // Potentially starts a drag for the specified Tab. - virtual void MaybeStartDrag(Tab* tab, const views::MouseEvent& event) = 0; - - // Continues dragging a Tab. - virtual void ContinueDrag(const views::MouseEvent& event) = 0; - - // Ends dragging a Tab. |canceled| is true if the drag was aborted in a way - // other than the user releasing the mouse. Returns whether the tab has been - // destroyed. - virtual bool EndDrag(bool canceled) = 0; - - // Returns true if the associated TabStrip's delegate supports tab moving or - // detaching. Used by the Frame to determine if dragging on the Tab - // itself should move the window in cases where there's only one - // non drag-able Tab. - virtual bool HasAvailableDragActions() const = 0; - }; - - explicit Tab(TabDelegate* delegate); + explicit Tab(TabController* controller); virtual ~Tab(); - // Access the delegate. - TabDelegate* delegate() const { return delegate_; } - // Used to set/check whether this Tab is being animated closed. void set_closing(bool closing) { closing_ = closing; } bool closing() const { return closing_; } @@ -122,20 +64,12 @@ class Tab : public TabRenderer, // representation. Used by GetViewForPoint for hit-testing. void MakePathForTab(gfx::Path* path) const; - // An instance of a delegate object that can perform various actions based on - // user gestures. - TabDelegate* delegate_; - // True if the tab is being animated closed. bool closing_; // True if the tab is being dragged. bool dragging_; - // If non-null it means we're showing a menu for the tab. - class TabContextMenuContents; - scoped_ptr<TabContextMenuContents> context_menu_contents_; - DISALLOW_COPY_AND_ASSIGN(Tab); }; diff --git a/chrome/browser/views/tabs/tab_renderer.cc b/chrome/browser/views/tabs/tab_renderer.cc index ccf39ca..f32d718 100644 --- a/chrome/browser/views/tabs/tab_renderer.cc +++ b/chrome/browser/views/tabs/tab_renderer.cc @@ -17,9 +17,7 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_theme_provider.h" #include "chrome/browser/defaults.h" -#include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" #include "gfx/canvas.h" #include "gfx/favicon_size.h" @@ -270,14 +268,17 @@ class TabRenderer::FavIconCrashAnimation : public LinearAnimation, //////////////////////////////////////////////////////////////////////////////// // TabRenderer, public: -TabRenderer::TabRenderer() - : animation_state_(ANIMATION_NONE), +TabRenderer::TabRenderer(TabController* controller) + : BaseTabRenderer(controller), animation_frame_(0), throbber_disabled_(false), showing_icon_(false), showing_close_button_(false), fav_icon_hiding_offset_(0), close_button_color_(NULL), + render_as_new_tab_(false), + render_unselected_(false), + alpha_(1), crash_animation_(NULL), should_display_crashed_favicon_(false), theme_provider_(NULL) { @@ -321,96 +322,10 @@ ThemeProvider* TabRenderer::GetThemeProvider() { return NULL; } -void TabRenderer::UpdateData(TabContents* contents, - bool phantom, - bool loading_only) { - DCHECK(contents); - if (data_.phantom != phantom || !loading_only) { - data_.title = contents->GetTitle(); - data_.off_the_record = contents->profile()->IsOffTheRecord(); - data_.crashed = contents->is_crashed(); - data_.app = contents->is_app(); - SkBitmap* app_icon = contents->GetExtensionAppIcon(); - if (app_icon) - data_.favicon = *app_icon; - else - data_.favicon = contents->GetFavIcon(); - data_.phantom = phantom; - if (phantom) { - data_.crashed = false; // Phantom tabs can never crash. - StopMiniTabTitleAnimation(); - } - - // Sets the accessible name for the tab. - SetAccessibleName(UTF16ToWide(data_.title)); - } - - // If this is an extension app and a command line flag is set, - // then disable the throbber. - throbber_disabled_ = data_.app && - CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsNoThrob); - - // TODO(glen): Temporary hax. - theme_provider_ = contents->profile()->GetThemeProvider(); - - // Loading state also involves whether we show the favicon, since that's where - // we display the throbber. - data_.loading = contents->is_loading(); - data_.show_icon = contents->ShouldDisplayFavIcon(); -} - -void TabRenderer::UpdateFromModel() { - // Force a layout, since the tab may have grown a favicon. - Layout(); - SchedulePaint(); - - if (data_.crashed) { - if (!should_display_crashed_favicon_ && !IsPerformingCrashAnimation()) - StartCrashAnimation(); - } else { - if (IsPerformingCrashAnimation()) - StopCrashAnimation(); - ResetCrashedFavIcon(); - } -} - -void TabRenderer::set_animating_mini_change(bool value) { - data_.animating_mini_change = value; -} - bool TabRenderer::IsSelected() const { return true; } -void TabRenderer::ValidateLoadingAnimation(AnimationState animation_state) { - if (throbber_disabled_) - return; - - if (animation_state_ != animation_state) { - // The waiting animation is the reverse of the loading animation, but at a - // different rate - the following reverses and scales the animation_frame_ - // so that the frame is at an equivalent position when going from one - // animation to the other. - if (animation_state_ == ANIMATION_WAITING && - animation_state == ANIMATION_LOADING) { - animation_frame_ = loading_animation_frame_count - - (animation_frame_ / waiting_to_loading_frame_count_ratio); - } - animation_state_ = animation_state; - } - - if (animation_state_ != ANIMATION_NONE) { - animation_frame_ = ++animation_frame_ % - ((animation_state_ == ANIMATION_WAITING) ? - waiting_animation_frame_count : - loading_animation_frame_count); - } else { - animation_frame_ = 0; - } - - SchedulePaint(); -} - void TabRenderer::StartPulse() { pulse_animation_->Reset(); pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); @@ -447,7 +362,7 @@ void TabRenderer::SetAnimationContainer(AnimationContainer* container) { } void TabRenderer::PaintIcon(gfx::Canvas* canvas) { - if (animation_state_ != ANIMATION_NONE) { + if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { PaintLoadingAnimation(canvas); } else { canvas->save(); @@ -461,15 +376,15 @@ void TabRenderer::PaintIcon(gfx::Canvas* canvas) { kFavIconSize, kFavIconSize, true); } else { - if (!data_.favicon.isNull()) { + if (!data().favicon.isNull()) { // TODO(pkasting): Use code in tab_icon_view.cc:PaintIcon() (or switch // to using that class to render the favicon). int x = favicon_bounds_.x(); int y = favicon_bounds_.y() + fav_icon_hiding_offset_; - int size = data_.favicon.width(); - canvas->DrawBitmapInt(data_.favicon, 0, 0, - data_.favicon.width(), - data_.favicon.height(), + int size = data().favicon.width(); + canvas->DrawBitmapInt(data().favicon, 0, 0, + data().favicon.width(), + data().favicon.height(), x, y, size, size, true); } @@ -514,7 +429,7 @@ int TabRenderer::GetMiniWidth() { // TabRenderer, protected: std::wstring TabRenderer::GetTitle() const { - return UTF16ToWideHack(data_.title); + return UTF16ToWideHack(data().title); } void TabRenderer::OnMouseEntered(const views::MouseEvent& e) { @@ -527,11 +442,66 @@ void TabRenderer::OnMouseExited(const views::MouseEvent& e) { hover_animation_->Hide(); } +void TabRenderer::DataChanged(const TabRendererData& old) { + if (data().phantom) + StopMiniTabTitleAnimation(); + + // Sets the accessible name for the tab. + SetAccessibleName(UTF16ToWide(data().title)); + + if (data().blocked != old.blocked) { + if (data().blocked) + StartPulse(); + else + StopPulse(); + } + + // If this is an extension app and a command line flag is set, + // then disable the throbber. + throbber_disabled_ = data().app && + CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsNoThrob); + + if (data().crashed) { + if (!should_display_crashed_favicon_ && !IsPerformingCrashAnimation()) + StartCrashAnimation(); + } else { + if (IsPerformingCrashAnimation()) + StopCrashAnimation(); + ResetCrashedFavIcon(); + } +} + +void TabRenderer::AdvanceLoadingAnimation(TabRendererData::NetworkState state) { + if (throbber_disabled_) + return; + + // The waiting animation is the reverse of the loading animation, but at a + // different rate - the following reverses and scales the animation_frame_ so + // that the frame is at an equivalent position when going from one animation + // to the other. + if (state == TabRendererData::NETWORK_STATE_WAITING && + state == TabRendererData::NETWORK_STATE_LOADING) { + animation_frame_ = loading_animation_frame_count - + (animation_frame_ / waiting_to_loading_frame_count_ratio); + } + + if (state != TabRendererData::NETWORK_STATE_NONE) { + animation_frame_ = ++animation_frame_ % + ((state == TabRendererData::NETWORK_STATE_WAITING) ? + waiting_animation_frame_count : + loading_animation_frame_count); + } else { + animation_frame_ = 0; + } + + SchedulePaint(); +} + //////////////////////////////////////////////////////////////////////////////// // TabRenderer, views::View overrides: void TabRenderer::Paint(gfx::Canvas* canvas) { - if (data_.render_as_new_tab) { + if (render_as_new_tab_) { if (base::i18n::IsRTL()) { canvas->TranslateInt(width(), 0); canvas->ScaleInt(-1, 1); @@ -542,11 +512,11 @@ void TabRenderer::Paint(gfx::Canvas* canvas) { // Don't paint if we're narrower than we can render correctly. (This should // only happen during animations). - if (width() < GetMinimumUnselectedSize().width() && !mini()) + if (width() < GetMinimumUnselectedSize().width() && !data().mini) return; // See if the model changes whether the icons should be painted. - const bool show_icon = ShouldShowIcon() && !phantom(); + const bool show_icon = ShouldShowIcon() && !data().phantom; const bool show_close_button = ShouldShowCloseBox(); if (show_icon != showing_icon_ || show_close_button != showing_close_button_) @@ -559,7 +529,7 @@ void TabRenderer::Paint(gfx::Canvas* canvas) { BrowserThemeProvider::COLOR_TAB_TEXT : BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT); - if (!mini() || width() > kMiniTabRendererAsNormalTabWidth) + if (!data().mini || width() > kMiniTabRendererAsNormalTabWidth) PaintTitle(title_color, canvas); if (show_icon) @@ -589,7 +559,7 @@ void TabRenderer::Layout() { if (showing_icon_) { // Use the size of the favicon as apps use a bigger favicon size. int favicon_size = - !data_.favicon.empty() ? data_.favicon.width() : kFavIconSize; + !data().favicon.empty() ? data().favicon.width() : kFavIconSize; int favicon_top = kTopPadding + content_height / 2 - favicon_size / 2; int favicon_left = lb.x(); if (favicon_size != kFavIconSize) { @@ -598,8 +568,7 @@ void TabRenderer::Layout() { } favicon_bounds_.SetRect(favicon_left, favicon_top, favicon_size, favicon_size); - if ((mini() || data_.animating_mini_change) && - width() < kMiniTabRendererAsNormalTabWidth) { + if (data().mini && width() < kMiniTabRendererAsNormalTabWidth) { // Adjust the location of the favicon when transitioning from a normal // tab to a mini-tab. int mini_delta = kMiniTabRendererAsNormalTabWidth - GetMiniWidth(); @@ -636,7 +605,7 @@ void TabRenderer::Layout() { int title_left = favicon_bounds_.right() + kFavIconTitleSpacing; int title_top = kTopPadding + (content_height - title_font_height) / 2; // Size the Title text to fill the remaining space. - if (!mini() || width() >= kMiniTabRendererAsNormalTabWidth) { + if (!data().mini || width() >= kMiniTabRendererAsNormalTabWidth) { // If the user has big fonts, the title will appear rendered too far down // on the y-axis if we use the regular top padding, so we need to adjust it // so that the text appears centered. @@ -694,9 +663,9 @@ void TabRenderer::AnimationEnded(const Animation* animation) { void TabRenderer::PaintTitle(SkColor title_color, gfx::Canvas* canvas) { // Paint the Title. - string16 title = data_.title; + string16 title = data().title; if (title.empty()) { - title = data_.loading ? + title = data().loading ? l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : TabContents::GetDefaultTitle(); } else { @@ -787,7 +756,7 @@ void TabRenderer::PaintInactiveTabBackgroundWithTitleChange( } void TabRenderer::PaintInactiveTabBackground(gfx::Canvas* canvas) { - bool is_otr = data_.off_the_record; + bool is_otr = data().off_the_record; // The tab image needs to be lined up with the background image // so that it feels partially transparent. These offsets represent the tab @@ -807,10 +776,10 @@ void TabRenderer::PaintInactiveTabBackground(gfx::Canvas* canvas) { SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(tab_id); // App tabs are drawn slightly differently (as nano tabs). - TabImage* tab_image = data_.app ? &tab_active_nano : &tab_active; - TabImage* tab_inactive_image = data_.app ? &tab_inactive_nano : + TabImage* tab_image = data().app ? &tab_active_nano : &tab_active; + TabImage* tab_inactive_image = data().app ? &tab_inactive_nano : &tab_inactive; - TabImage* alpha = data_.app ? &tab_alpha_nano : &tab_alpha; + TabImage* alpha = data().app ? &tab_alpha_nano : &tab_alpha; // If the theme is providing a custom background image, then its top edge // should be at the top of the tab. Otherwise, we assume that the background @@ -872,8 +841,8 @@ void TabRenderer::PaintActiveTabBackground(gfx::Canvas* canvas) { SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(IDR_THEME_TOOLBAR); // App tabs are drawn slightly differently (as nano tabs). - TabImage* tab_image = data_.app ? &tab_active_nano : &tab_active; - TabImage* alpha = data_.app ? &tab_alpha_nano : &tab_alpha; + TabImage* tab_image = data().app ? &tab_active_nano : &tab_active; + TabImage* alpha = data().app ? &tab_alpha_nano : &tab_alpha; // Draw left edge. SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap( @@ -907,8 +876,9 @@ void TabRenderer::PaintActiveTabBackground(gfx::Canvas* canvas) { } void TabRenderer::PaintLoadingAnimation(gfx::Canvas* canvas) { - SkBitmap* frames = (animation_state_ == ANIMATION_WAITING) ? - waiting_animation_frames : loading_animation_frames; + SkBitmap* frames = + (data().network_state == TabRendererData::NETWORK_STATE_WAITING) ? + waiting_animation_frames : loading_animation_frames; int image_size = frames->height(); int image_offset = animation_frame_ * image_size; int dst_y = (height() - image_size) / 2; @@ -917,7 +887,7 @@ void TabRenderer::PaintLoadingAnimation(gfx::Canvas* canvas) { // loading animation also needs to be mirrored if the View's UI layout is // right-to-left. int dst_x; - if (mini()) { + if (data().mini) { dst_x = favicon_bounds_.x(); if (favicon_bounds_.width() != kFavIconSize) dst_x += (favicon_bounds_.width() - kFavIconSize) / 2; @@ -934,7 +904,7 @@ void TabRenderer::PaintLoadingAnimation(gfx::Canvas* canvas) { } void TabRenderer::PaintAsNewTab(gfx::Canvas* canvas) { - bool is_otr = data_.off_the_record; + bool is_otr = data().off_the_record; // The tab image needs to be lined up with the background image // so that it feels partially transparent. These offsets represent the tab @@ -981,9 +951,9 @@ int TabRenderer::IconCapacity() const { } bool TabRenderer::ShouldShowIcon() const { - if (mini() && height() >= GetMinimumUnselectedSize().height()) + if (data().mini && height() >= GetMinimumUnselectedSize().height()) return true; - if (!data_.show_icon) { + if (!data().show_icon) { return false; } else if (IsSelected()) { // The selected tab clips favicon before close button. @@ -995,12 +965,12 @@ bool TabRenderer::ShouldShowIcon() const { bool TabRenderer::ShouldShowCloseBox() const { // The selected tab never clips close button. - return !mini() && (IsSelected() || IconCapacity() >= 3); + return !data().mini && (IsSelected() || IconCapacity() >= 3); } double TabRenderer::GetThrobValue() { - if (data_.alpha != 1) - return data_.alpha; + if (alpha_ != 1) + return alpha_; if (pulse_animation_->is_animating()) return pulse_animation_->GetCurrentValue() * kHoverOpacity; @@ -1090,13 +1060,3 @@ void TabRenderer::LoadTabImages() { new_tab_mask = rb.GetBitmapNamed(IDR_TAB_ALPHA_NEW_TAB); new_tab_shadow = rb.GetBitmapNamed(IDR_TAB_NEW_TAB_SHADOW); } - -void TabRenderer::SetBlocked(bool blocked) { - if (data_.blocked == blocked) - return; - data_.blocked = blocked; - if (blocked) - StartPulse(); - else - StopPulse(); -} diff --git a/chrome/browser/views/tabs/tab_renderer.h b/chrome/browser/views/tabs/tab_renderer.h index 2ae8be99..7247b50 100644 --- a/chrome/browser/views/tabs/tab_renderer.h +++ b/chrome/browser/views/tabs/tab_renderer.h @@ -6,17 +6,14 @@ #define CHROME_BROWSER_VIEWS_TABS_TAB_RENDERER_H_ #include "app/animation.h" -#include "base/ref_counted.h" #include "base/scoped_ptr.h" -#include "base/string16.h" +#include "chrome/browser/views/tabs/base_tab_renderer.h" #include "gfx/point.h" #include "views/controls/button/image_button.h" -#include "views/view.h" class AnimationContainer; class MultiAnimation; class SlideAnimation; -class TabContents; class ThrobAnimation; /////////////////////////////////////////////////////////////////////////////// @@ -26,18 +23,11 @@ class ThrobAnimation; // A View that renders a Tab, either in a TabStrip or in a DraggedTabView. // /////////////////////////////////////////////////////////////////////////////// -class TabRenderer : public views::View, +class TabRenderer : public BaseTabRenderer, public views::ButtonListener, public AnimationDelegate { public: - // Possible animation states. - enum AnimationState { - ANIMATION_NONE, - ANIMATION_WAITING, - ANIMATION_LOADING - }; - - TabRenderer(); + explicit TabRenderer(TabController* controller); virtual ~TabRenderer(); // Sizes the renderer to the size of the new tab images. This is used @@ -49,53 +39,21 @@ class TabRenderer : public views::View, void ViewHierarchyChanged(bool is_add, View* parent, View* child); ThemeProvider* GetThemeProvider(); - // Updates the data the Tab uses to render itself from the specified - // TabContents. - // - // See TabStripModel::TabChangedAt documentation for what loading_only means. - void UpdateData(TabContents* contents, bool phantom, bool loading_only); - - // Sets the blocked state of the tab. - void SetBlocked(bool blocked); - bool blocked() const { return data_.blocked; } - - // Sets the mini-state of the tab. - void set_mini(bool mini) { data_.mini = mini; } - bool mini() const { return data_.mini; } - - // Sets the mini-state of the tab. - void set_app(bool app) { data_.app = app; } - bool app() const { return data_.app; } - - // Sets the phantom state of the tab. - void set_phantom(bool phantom) { data_.phantom = phantom; } - bool phantom() const { return data_.phantom; } - // Used during new tab animation to force the tab to render a new tab like // animation. - void set_render_as_new_tab(bool value) { data_.render_as_new_tab = value; } + void set_render_as_new_tab(bool value) { render_as_new_tab_ = value; } // Sets the alpha value to render the tab at. This is used during the new // tab animation. - void set_alpha(double value) { data_.alpha = value; } + void set_alpha(double value) { alpha_ = value; } // Forces the tab to render unselected even though it is selected. - void set_render_unselected(bool value) { data_.render_unselected = value; } - bool render_unselected() const { return data_.render_unselected; } - - // Are we in the process of animating a mini tab state change on this tab? - void set_animating_mini_change(bool value); - - // Updates the display to reflect the contents of this TabRenderer's model. - void UpdateFromModel(); + void set_render_unselected(bool value) { render_unselected_ = value; } + bool render_unselected() const { return render_unselected_; } // Returns true if the Tab is selected, false otherwise. virtual bool IsSelected() const; - // Advance the Loading Animation to the next frame, or hide the animation if - // the tab isn't loading. - void ValidateLoadingAnimation(AnimationState animation_state); - // Starts/Stops a pulse animation. void StartPulse(); void StopPulse(); @@ -155,6 +113,10 @@ class TabRenderer : public views::View, virtual void ButtonPressed(views::Button* sender, const views::Event& event) {} + // BaseTabRenderer overrides: + virtual void DataChanged(const TabRendererData& old); + virtual void AdvanceLoadingAnimation(TabRendererData::NetworkState state); + private: // Overridden from views::View: virtual void Paint(gfx::Canvas* canvas); @@ -210,9 +172,6 @@ class TabRenderer : public views::View, // The offset used to paint the inactive background image. gfx::Point background_offset_; - // Current state of the animation. - AnimationState animation_state_; - // The current index into the Animation image strip. int animation_frame_; @@ -233,42 +192,6 @@ class TabRenderer : public views::View, // Animation used when the title of an inactive mini tab changes. scoped_ptr<MultiAnimation> mini_title_animation_; - // Model data. We store this here so that we don't need to ask the underlying - // model, which is tricky since instances of this object can outlive the - // corresponding objects in the underlying model. - struct TabData { - TabData() - : loading(false), - crashed(false), - off_the_record(false), - show_icon(true), - mini(false), - blocked(false), - animating_mini_change(false), - phantom(false), - app(false), - render_as_new_tab(false), - render_unselected(false), - alpha(1) { - } - - SkBitmap favicon; - string16 title; - bool loading; - bool crashed; - bool off_the_record; - bool show_icon; - bool mini; - bool blocked; - bool animating_mini_change; - bool phantom; - bool app; - bool render_as_new_tab; - bool render_unselected; - double alpha; - }; - TabData data_; - struct TabImage { SkBitmap* image_l; SkBitmap* image_c; @@ -298,6 +221,15 @@ class TabRenderer : public views::View, // The current color of the close button. SkColor close_button_color_; + // See description above setter. + bool render_as_new_tab_; + + // See description above setter. + bool render_unselected_; + + // See description above setter. + double alpha_; + // The animation object used to swap the favicon with the sad tab icon. class FavIconCrashAnimation; FavIconCrashAnimation* crash_animation_; @@ -308,7 +240,6 @@ class TabRenderer : public views::View, scoped_refptr<AnimationContainer> container_; - static void InitClass(); static bool initialized_; DISALLOW_COPY_AND_ASSIGN(TabRenderer); diff --git a/chrome/browser/views/tabs/tab_renderer_data.h b/chrome/browser/views/tabs/tab_renderer_data.h new file mode 100644 index 0000000..7cbaa18 --- /dev/null +++ b/chrome/browser/views/tabs/tab_renderer_data.h @@ -0,0 +1,49 @@ +// Copyright (c) 2010 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_RENDERER_DATA_H_ +#define CHROME_BROWSER_VIEWS_TABS_TAB_RENDERER_DATA_H_ + +#include <string> + +#include "base/string16.h" +#include "third_party/skia/include/core/SkBitmap.h" + +// Wraps the state needed by the renderers. +struct TabRendererData { + // Different types of network activity for a tab. The NetworkState of a tab + // may be used to alter the UI (e.g. show different kinds of loading + // animations). + enum NetworkState { + NETWORK_STATE_NONE, // no network activity. + NETWORK_STATE_WAITING, // waiting for a connection. + NETWORK_STATE_LOADING, // connected, transferring data. + }; + + TabRendererData() + : network_state(NETWORK_STATE_NONE), + loading(false), + crashed(false), + off_the_record(false), + show_icon(true), + mini(false), + blocked(false), + phantom(false), + app(false) { + } + + SkBitmap favicon; + NetworkState network_state; + string16 title; + bool loading; + bool crashed; + bool off_the_record; + bool show_icon; + bool mini; + bool blocked; + bool phantom; + bool app; +}; + +#endif // CHROME_BROWSER_VIEWS_TABS_TAB_RENDERER_DATA_H_ diff --git a/chrome/browser/views/tabs/tab_strip.cc b/chrome/browser/views/tabs/tab_strip.cc index 5681a22..79a1399 100644 --- a/chrome/browser/views/tabs/tab_strip.cc +++ b/chrome/browser/views/tabs/tab_strip.cc @@ -7,34 +7,24 @@ #include "app/animation_container.h" #include "app/drag_drop_types.h" #include "app/l10n_util.h" -#include "app/os_exchange_data.h" #include "app/resource_bundle.h" #include "app/slide_animation.h" -#include "base/command_line.h" #include "base/compiler_specific.h" #include "base/stl_util-inl.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_theme_provider.h" #include "chrome/browser/defaults.h" -#include "chrome/browser/metrics/user_metrics.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/renderer_host/render_view_host.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/view_ids.h" -#include "chrome/browser/views/app_launcher.h" #include "chrome/browser/views/tabs/dragged_tab_controller.h" #include "chrome/browser/views/tabs/tab.h" -#include "chrome/common/chrome_switches.h" +#include "chrome/browser/views/tabs/tab_strip_controller.h" #include "chrome/common/pref_names.h" -#include "chrome/common/url_constants.h" #include "gfx/canvas.h" #include "gfx/path.h" #include "gfx/size.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/controls/image_view.h" -#include "views/painter.h" #include "views/widget/default_theme_provider.h" #include "views/widget/root_view.h" #include "views/window/non_client_view.h" @@ -267,8 +257,8 @@ const int TabStrip::mini_to_non_mini_gap_ = 3; // static const int TabStrip::extra_gap_for_nano_ = 10; -TabStrip::TabStrip(TabStripModel* model) - : model_(model), +TabStrip::TabStrip(TabStripController* controller) + : BaseTabStrip(controller), resize_layout_factory_(this), added_as_message_loop_observer_(false), needs_resize_layout_(false), @@ -289,8 +279,6 @@ TabStrip::~TabStrip() { // delete the tabs. StopAnimating(false); - model_->RemoveObserver(this); - // TODO(beng): remove this if it doesn't work to fix the TabSelectedAt bug. drag_controller_.reset(NULL); @@ -304,10 +292,6 @@ TabStrip::~TabStrip() { RemoveAllChildViews(true); } -bool TabStrip::CanProcessInputEvents() const { - return !IsAnimating(); -} - void TabStrip::DestroyDragController() { if (IsDragSessionActive()) drag_controller_.reset(NULL); @@ -319,10 +303,6 @@ gfx::Rect TabStrip::GetIdealBounds(int tab_data_index) { return tab_data_[tab_data_index].ideal_bounds; } -Tab* TabStrip::GetSelectedTab() const { - return GetTabAtModelIndex(model()->selected_index()); -} - void TabStrip::InitTabStripButtons() { newtab_button_ = new NewTabButton(this); if (browser_defaults::kSizeTabButtonToTopOfTabStrip) { @@ -334,23 +314,10 @@ void TabStrip::InitTabStripButtons() { AddChildView(newtab_button_); } -bool TabStrip::IsCompatibleWith(TabStrip* other) const { - return model_->profile() == other->model()->profile(); -} - gfx::Rect TabStrip::GetNewTabButtonBounds() { return newtab_button_->bounds(); } -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: @@ -395,23 +362,6 @@ bool TabStrip::IsDragSessionActive() const { return drag_controller_.get() != NULL; } -void TabStrip::UpdateLoadingAnimations() { - for (int i = 0, model_index = 0; i < GetTabCount(); ++i) { - Tab* current_tab = GetTabAtTabDataIndex(i); - if (!current_tab->closing()) { - TabContents* contents = model_->GetTabContentsAt(model_index); - if (!contents || !contents->is_loading()) { - current_tab->ValidateLoadingAnimation(Tab::ANIMATION_NONE); - } else if (contents->waiting_for_response()) { - current_tab->ValidateLoadingAnimation(Tab::ANIMATION_WAITING); - } else { - current_tab->ValidateLoadingAnimation(Tab::ANIMATION_LOADING); - } - model_index++; - } - } -} - bool TabStrip::IsAnimating() const { return bounds_animator_.IsAnimating() || new_tab_timer_.IsRunning(); } @@ -420,6 +370,125 @@ TabStrip* TabStrip::AsTabStrip() { return this; } +void TabStrip::AddTabAt(int model_index, + bool foreground, + const TabRendererData& data) { + Tab* tab = CreateTab(); + tab->SetAnimationContainer(animation_container_.get()); + tab->SetData(data); + + TabData d = { tab, gfx::Rect() }; + tab_data_.insert(tab_data_.begin() + + ModelIndexToTabDataIndex(model_index), d); + + AddChildView(tab); + + // Don't animate the first tab, it looks weird, and don't animate anything + // if the containing window isn't visible yet. + if (GetTabCount() > 1 && GetWindow() && GetWindow()->IsVisible()) { + if (!IsDragSessionActive() && !attaching_dragged_tab_ && + ShouldStartIntertTabAnimationAtEnd(model_index, foreground)) { + StartInsertTabAnimationAtEnd(); + } else { + StartInsertTabAnimation(model_index); + } + } else { + Layout(); + } +} + +void TabStrip::RemoveTabAt(int model_index) { + int model_count = GetModelCount(); + if (model_index != model_count && model_count > 0) { + Tab* last_tab = GetTabAtModelIndex(model_count - 1); + // Limit the width available to the TabStrip for laying out Tabs, so that + // Tabs are not resized until a later time (when the mouse pointer leaves + // the TabStrip). + available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab); + needs_resize_layout_ = true; + AddMessageLoopObserver(); + } else if (model_count) { + Tab* last_tab = GetTabAtModelIndex(model_count); + // Limit the width available to the TabStrip for laying out Tabs, so that + // Tabs are not resized until a later time (when the mouse pointer leaves + // the TabStrip). + available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab); + needs_resize_layout_ = true; + AddMessageLoopObserver(); + } + + StartRemoveTabAnimation(model_index); +} + +void TabStrip::SelectTabAt(int old_model_index, int new_model_index) { + // We have "tiny tabs" if the tabs are so tiny that the unselected ones are + // a different size to the selected ones. + bool tiny_tabs = current_unselected_width_ != current_selected_width_; + if (!IsAnimating() && (!needs_resize_layout_ || tiny_tabs)) { + Layout(); + } else { + SchedulePaint(); + } + + if (old_model_index >= 0) { + GetTabAtTabDataIndex(ModelIndexToTabDataIndex(old_model_index))-> + StopMiniTabTitleAnimation(); + } +} + +void TabStrip::MoveTab(int from_model_index, int to_model_index) { + StartMoveTabAnimation(from_model_index, to_model_index); +} + +void TabStrip::TabTitleChangedNotLoading(int model_index) { + Tab* tab = GetTabAtModelIndex(model_index); + if (tab->data().mini && !tab->IsSelected()) + tab->StartMiniTabTitleAnimation(); +} + +void TabStrip::SetTabData(int model_index, const TabRendererData& data) { + Tab* tab = GetTabAtModelIndex(model_index); + bool mini_state_changed = tab->data().mini != data.mini; + tab->SetData(data); + tab->SchedulePaint(); + + if (mini_state_changed) { + if (GetWindow() && GetWindow()->IsVisible()) + StartMiniTabAnimation(); + else + Layout(); + } +} + +void TabStrip::StartHighlight(int model_index) { + GetTabAtModelIndex(model_index)->StartPulse(); +} + +void TabStrip::StopAllHighlighting() { + for (int i = 0; i < GetTabCount(); ++i) + GetTabAtTabDataIndex(i)->StopPulse(); +} + +BaseTabRenderer* TabStrip::GetBaseTabAtModelIndex(int model_index) const { + return GetTabAtModelIndex(model_index); +} + +BaseTabRenderer* TabStrip::GetBaseTabAtTabIndex(int tab_index) const { + return GetTabAtTabDataIndex(tab_index); +} + +int TabStrip::GetModelIndexOfBaseTab(const BaseTabRenderer* tab) const { + return GetModelIndexOfTab(static_cast<const Tab*>(tab)); +} + +BaseTabRenderer* TabStrip::CreateTabForDragging() { + Tab* tab = new Tab(NULL); + // Make sure the dragged tab shares our theme provider. We need to explicitly + // do this as during dragging there isn't a theme provider. + tab->SetThemeProvider(GetThemeProvider()); + return tab; +} + /////////////////////////////////////////////////////////////////////////////// // TabStrip, views::View overrides: @@ -437,7 +506,7 @@ void TabStrip::PaintChildren(gfx::Canvas* canvas) { canvas->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode); for (int i = tab_count - 1; i >= 0; --i) { Tab* tab = GetTabAtTabDataIndex(i); - if (tab->phantom()) + if (tab->data().phantom) tab->ProcessPaint(canvas); } canvas->restore(); @@ -447,7 +516,7 @@ void TabStrip::PaintChildren(gfx::Canvas* canvas) { canvas->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode); for (int i = tab_count - 1; i >= 0; --i) { Tab* tab = GetTabAtTabDataIndex(i); - if (tab->phantom()) { + if (tab->data().phantom) { canvas->save(); canvas->ClipRectInt(tab->MirroredX(), tab->y(), tab->width(), tab->height()); @@ -463,19 +532,21 @@ void TabStrip::PaintChildren(gfx::Canvas* canvas) { Tab* dragging_tab = NULL; + int model_count = GetModelCount(); + for (int i = tab_count - 1; i >= 0; --i) { Tab* tab = GetTabAtTabDataIndex(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 (!tab->phantom()) { + if (!tab->data().phantom) { if (tab->dragging()) { dragging_tab = tab; } else if (!tab->IsSelected()) { - if (tab->render_unselected() && model_->count() > 1) { + if (tab->render_unselected() && model_count > 1) { // See comment above kNetTabAnimationSelectedOffset as to why we do // this. - Tab* last_tab = GetTabAtModelIndex(model_->count() - 2); + Tab* last_tab = GetTabAtModelIndex(model_count - 2); canvas->save(); int clip_x = last_tab->bounds().right() + kNetTabSelectedOffset; int clip_width = width() - clip_x; @@ -596,25 +667,7 @@ int TabStrip::OnPerformDrop(const DropTargetEvent& event) { if (!event.GetData().GetURLAndTitle(&url, &title) || !url.is_valid()) return DragDropTypes::DRAG_NONE; - if (drop_before) { - UserMetrics::RecordAction(UserMetricsAction("Tab_DropURLBetweenTabs"), - model_->profile()); - - // Insert a new tab. - TabContents* contents = - model_->delegate()->CreateTabContentsForURL( - url, GURL(), model_->profile(), PageTransition::TYPED, false, - NULL); - model_->AddTabContents(contents, drop_index, false, - PageTransition::GENERATED, true); - } else { - UserMetrics::RecordAction(UserMetricsAction("Tab_DropURLOnTab"), - model_->profile()); - - model_->GetTabContentsAt(drop_index)->controller().LoadURL( - url, GURL(), PageTransition::GENERATED); - model_->SelectTabContentsAt(drop_index, true); - } + controller()->PerformDrop(drop_before, drop_index, url); return GetDropEffect(event); } @@ -655,6 +708,15 @@ void TabStrip::ThemeChanged() { LoadNewTabButtonImage(); } +void TabStrip::OnBoundsAnimatorDone(views::BoundsAnimator* animator) { + AnimationType last_type = animation_type_; + + ResetAnimationState(false); + + if (!cancelling_animation_ && last_type == ANIMATION_NEW_TAB_2) + NewTabAnimation2Done(); +} + Tab* TabStrip::CreateTab() { return new Tab(this); } @@ -678,233 +740,27 @@ void TabStrip::OnMouseReleased(const views::MouseEvent& event, } /////////////////////////////////////////////////////////////////////////////// -// TabStrip, TabStripModelObserver implementation: - -void TabStrip::TabInsertedAt(TabContents* contents, - int model_index, - bool foreground) { - DCHECK(contents); - DCHECK(model_index == TabStripModel::kNoTab || - model_->ContainsIndex(model_index)); - // This tab may be attached to another browser window, we should notify - // renderer. - contents->render_view_host()->UpdateBrowserWindowId( - contents->controller().window_id().id()); - - Tab* tab = CreateTab(); - - TabData d = { tab, gfx::Rect() }; - tab_data_.insert(tab_data_.begin() + - ModelIndexToTabDataIndex(model_index), d); - tab->UpdateData(contents, model_->IsPhantomTab(model_index), false); - tab->set_mini(model_->IsMiniTab(model_index)); - tab->set_app(model_->IsAppTab(model_index)); - tab->SetBlocked(model_->IsTabBlocked(model_index)); - tab->SetAnimationContainer(animation_container_.get()); - - AddChildView(tab); - - // Don't animate the first tab, it looks weird, and don't animate anything - // if the containing window isn't visible yet. - if (GetTabCount() > 1 && GetWindow() && GetWindow()->IsVisible()) { - if (!IsDragSessionActive() && !attaching_dragged_tab_ && - ShouldStartIntertTabAnimationAtEnd(model_index, foreground)) { - StartInsertTabAnimationAtEnd(); - } else { - StartInsertTabAnimation(model_index); - } - } else { - Layout(); - } -} - -void TabStrip::TabDetachedAt(TabContents* contents, int model_index) { - StartRemoveTabAnimation(model_index); -} - -void TabStrip::TabSelectedAt(TabContents* old_contents, - TabContents* new_contents, - int model_index, - bool user_gesture) { - // We have "tiny tabs" if the tabs are so tiny that the unselected ones are - // a different size to the selected ones. - bool tiny_tabs = current_unselected_width_ != current_selected_width_; - if (!IsAnimating() && (!needs_resize_layout_ || tiny_tabs)) { - Layout(); - } else { - SchedulePaint(); - } - - int old_model_index = model_->GetIndexOfTabContents(old_contents); - if (old_model_index >= 0) { - GetTabAtTabDataIndex(ModelIndexToTabDataIndex(old_model_index))-> - StopMiniTabTitleAnimation(); - } -} - -void TabStrip::TabMoved(TabContents* contents, - int from_model_index, - int to_model_index) { - StartMoveTabAnimation(from_model_index, to_model_index); -} - -void TabStrip::TabChangedAt(TabContents* contents, - int model_index, - TabChangeType change_type) { - // Index is in terms of the model. Need to make sure we adjust that index in - // case we have an animation going. - Tab* tab = GetTabAtModelIndex(model_index); - if (change_type == TITLE_NOT_LOADING) { - if (tab->mini() && !tab->IsSelected()) - tab->StartMiniTabTitleAnimation(); - // We'll receive another notification of the change asynchronously. - return; - } - tab->UpdateData(contents, model_->IsPhantomTab(model_index), - change_type == LOADING_ONLY); - tab->UpdateFromModel(); -} - -void TabStrip::TabReplacedAt(TabContents* old_contents, - TabContents* new_contents, - int model_index) { - TabChangedAt(new_contents, model_index, ALL); -} - -void TabStrip::TabMiniStateChanged(TabContents* contents, int model_index) { - GetTabAtModelIndex(model_index)->set_mini( - model_->IsMiniTab(model_index)); - // Don't animate if the window isn't visible yet. The window won't be visible - // when dragging a mini-tab to a new window. - if (GetWindow() && GetWindow()->IsVisible()) - StartMiniTabAnimation(); - else - Layout(); -} - -void TabStrip::TabBlockedStateChanged(TabContents* contents, int model_index) { - GetTabAtModelIndex(model_index)->SetBlocked( - model_->IsTabBlocked(model_index)); -} - -/////////////////////////////////////////////////////////////////////////////// // TabStrip, Tab::Delegate implementation: -bool TabStrip::IsTabSelected(const Tab* tab) const { +bool TabStrip::IsTabSelected(const BaseTabRenderer* btr) const { + const Tab* tab = static_cast<const Tab*>(btr); if (tab->closing() || tab->render_unselected()) return false; - return GetModelIndexOfTab(tab) == model_->selected_index(); + return BaseTabStrip::IsTabSelected(btr); } -bool TabStrip::IsTabPinned(const Tab* tab) const { +bool TabStrip::IsTabPinned(const BaseTabRenderer* btr) const { + const Tab* tab = static_cast<const Tab*>(btr); if (tab->closing()) return false; - return model_->IsTabPinned(GetModelIndexOfTab(tab)); -} - -void TabStrip::SelectTab(Tab* tab) { - int model_index = GetModelIndexOfTab(tab); - if (model_->ContainsIndex(model_index)) - model_->SelectTabContentsAt(model_index, true); -} - -void TabStrip::CloseTab(Tab* tab) { - int model_index = GetModelIndexOfTab(tab); - if (model_->ContainsIndex(model_index)) { - TabContents* contents = model_->GetTabContentsAt(model_index); - if (contents) - UserMetrics::RecordAction(UserMetricsAction("CloseTab_Mouse"), - contents->profile()); - if (model_index + 1 != model_->count() && model_->count() > 1) { - Tab* last_tab = GetTabAtModelIndex(model_->count() - 2); - // Limit the width available to the TabStrip for laying out Tabs, so that - // Tabs are not resized until a later time (when the mouse pointer leaves - // the TabStrip). - available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab); - needs_resize_layout_ = true; - AddMessageLoopObserver(); - } else if (model_->count() > 1) { - Tab* last_tab = GetTabAtModelIndex(model_->count() - 1); - // Limit the width available to the TabStrip for laying out Tabs, so that - // Tabs are not resized until a later time (when the mouse pointer leaves - // the TabStrip). - available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab); - needs_resize_layout_ = true; - AddMessageLoopObserver(); - } - // Note that the next call might not close the tab (because of unload - // hanlders or if the delegate veto the close). - model_->CloseTabContentsAt(model_index); - } -} - -bool TabStrip::IsCommandEnabledForTab( - TabStripModel::ContextMenuCommand command_id, const Tab* tab) const { - int model_index = GetModelIndexOfTab(tab); - if (model_->ContainsIndex(model_index)) - return model_->IsContextMenuCommandEnabled(model_index, command_id); - return false; -} - -bool TabStrip::IsCommandCheckedForTab( - TabStripModel::ContextMenuCommand command_id, const Tab* tab) const { - int model_index = GetModelIndexOfTab(tab); - if (model_->ContainsIndex(model_index)) - return model_->IsContextMenuCommandChecked(model_index, command_id); - return false; -} - -void TabStrip::ExecuteCommandForTab( - TabStripModel::ContextMenuCommand command_id, Tab* tab) { - int model_index = GetModelIndexOfTab(tab); - if (model_->ContainsIndex(model_index)) - model_->ExecuteContextMenuCommand(model_index, command_id); + return BaseTabStrip::IsTabPinned(tab); } -void TabStrip::OnBoundsAnimatorDone(views::BoundsAnimator* animator) { - AnimationType last_type = animation_type_; - - ResetAnimationState(false); - - if (!cancelling_animation_ && last_type == ANIMATION_NEW_TAB_2) - NewTabAnimation2Done(); -} - -void TabStrip::StartHighlightTabsForCommand( - TabStripModel::ContextMenuCommand command_id, Tab* tab) { - if (command_id == TabStripModel::CommandCloseTabsOpenedBy || - command_id == TabStripModel::CommandCloseOtherTabs || - command_id == TabStripModel::CommandCloseTabsToRight) { - int model_index = GetModelIndexOfTab(tab); - if (model_->ContainsIndex(model_index)) { - std::vector<int> indices = - model_->GetIndicesClosedByCommand(model_index, command_id); - for (std::vector<int>::const_iterator i = indices.begin(); - i != indices.end(); ++i) { - GetTabAtModelIndex(*i)->StartPulse(); - } - } - } -} - -void TabStrip::StopHighlightTabsForCommand( - TabStripModel::ContextMenuCommand command_id, Tab* tab) { - if (command_id == TabStripModel::CommandCloseTabsOpenedBy || - command_id == TabStripModel::CommandCloseTabsToRight || - command_id == TabStripModel::CommandCloseOtherTabs) { - // Just tell all Tabs to stop pulsing - it's safe. - StopAllHighlighting(); - } -} - -void TabStrip::StopAllHighlighting() { - for (int i = 0; i < GetTabCount(); ++i) - GetTabAtTabDataIndex(i)->StopPulse(); -} - -void TabStrip::MaybeStartDrag(Tab* tab, const views::MouseEvent& event) { +void TabStrip::MaybeStartDrag(BaseTabRenderer* btr, + const views::MouseEvent& event) { + Tab* tab = static_cast<Tab*>(btr); // Don't accidentally start any drag operations during animations if the // mouse is down... during an animation tabs are being resized automatically, // so the View system can misinterpret this easily if the mouse is down that @@ -912,7 +768,7 @@ void TabStrip::MaybeStartDrag(Tab* tab, const views::MouseEvent& event) { if (IsAnimating() || tab->closing() || !HasAvailableDragActions()) return; int model_index = GetModelIndexOfTab(tab); - if (!model_->ContainsIndex(model_index)) { + if (!IsValidModelIndex(model_index)) { CHECK(false); return; } @@ -944,28 +800,15 @@ bool TabStrip::EndDrag(bool canceled) { } bool TabStrip::HasAvailableDragActions() const { - return model_->delegate()->GetDragActions() != 0; + return controller()->HasAvailableDragActions() != 0; } /////////////////////////////////////////////////////////////////////////////// // TabStrip, views::BaseButton::ButtonListener implementation: void TabStrip::ButtonPressed(views::Button* sender, const views::Event& event) { - if (sender == newtab_button_) { - // TODO(jcampan): if we decide to keep the app launcher as the default - // behavior for the new tab button, we should add a method - // on the TabStripDelegate to do so. - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsPanel)) { - NavigationController& controller = - model_->GetSelectedTabContents()->controller(); - AppLauncher::ShowForNewTab( - Browser::GetBrowserForController(&controller, NULL), std::string()); - return; - } - UserMetrics::RecordAction(UserMetricsAction("NewTab_Button"), - model_->profile()); - model_->delegate()->AddBlankTab(true); - } + if (sender == newtab_button_) + controller()->CreateNewTab(); } /////////////////////////////////////////////////////////////////////////////// @@ -1029,7 +872,6 @@ void TabStrip::DidProcessEvent(GdkEvent* event) { void TabStrip::Init() { SetID(VIEW_ID_TAB_STRIP); - model_->AddObserver(this); newtab_button_bounds_.SetRect(0, 0, kNewTabButtonWidth, kNewTabButtonHeight); if (browser_defaults::kSizeTabButtonToTopOfTabStrip) { newtab_button_bounds_.set_height( @@ -1412,9 +1254,9 @@ void TabStrip::GenerateIdealBounds() { for (int i = 0; i < tab_count; ++i) { if (!tab_data_[i].tab->closing()) { ++non_closing_tab_count; - if (tab_data_[i].tab->mini()) + if (tab_data_[i].tab->data().mini) mini_tab_count++; - if (tab_data_[i].tab->app()) + if (tab_data_[i].tab->data().app) nano_tab_count++; } } @@ -1435,7 +1277,7 @@ void TabStrip::GenerateIdealBounds() { if (!tab_data_[i].tab->closing()) { Tab* tab = GetTabAtTabDataIndex(i); double tab_width = unselected; - if (tab->mini()) { + if (tab->data().mini) { tab_width = Tab::GetMiniWidth(); } else { if (last_was_mini) { @@ -1453,7 +1295,7 @@ void TabStrip::GenerateIdealBounds() { gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x, tab_height); tab_x = end_of_tab + kTabHOffset; - last_was_mini = tab->mini(); + last_was_mini = tab->data().mini; } } @@ -1534,9 +1376,8 @@ void TabStrip::AnimateToIdealBounds() { bool TabStrip::ShouldStartIntertTabAnimationAtEnd(int model_index, bool foreground) { - return foreground && (model_index + 1 == model_->count()) && - (model_->GetTabContentsAt(model_index)->GetURL() == - GURL(chrome::kChromeUINewTabURL)); + return foreground && (model_index + 1 == GetModelCount()) && + controller()->IsNewTabPage(model_index); } void TabStrip::StartResizeLayoutAnimation() { @@ -1555,7 +1396,7 @@ void TabStrip::StartInsertTabAnimationAtEnd() { GenerateIdealBounds(); - int tab_data_index = ModelIndexToTabDataIndex(model_->count() - 1); + int tab_data_index = ModelIndexToTabDataIndex(GetModelCount() - 1); Tab* tab = tab_data_[tab_data_index].tab; tab->SizeToNewTabButtonImages(); tab->SetBounds(newtab_button_->x() + @@ -1624,15 +1465,10 @@ void TabStrip::StartMoveTabAnimation(int from_model_index, tab_data_.erase(tab_data_.begin() + from_tab_data_index); TabData data = {tab, gfx::Rect()}; - tab->set_mini(model_->IsMiniTab(to_model_index)); - tab->set_app(model_->IsAppTab(to_model_index)); - tab->SetBlocked(model_->IsTabBlocked(to_model_index)); int to_tab_data_index = ModelIndexToTabDataIndex(to_model_index); tab_data_.insert(tab_data_.begin() + to_tab_data_index, data); - if (tab->phantom() != model_->IsPhantomTab(to_model_index)) - tab->set_phantom(!tab->phantom()); GenerateIdealBounds(); AnimateToIdealBounds(); @@ -1679,7 +1515,6 @@ void TabStrip::ResetAnimationState(bool stop_new_tab_timer) { // Reset the animation state of each tab. for (int i = 0, count = GetTabCount(); i < count; ++i) { Tab* tab = GetTabAtTabDataIndex(i); - tab->set_animating_mini_change(false); tab->set_render_as_new_tab(false); tab->set_render_unselected(false); tab->set_alpha(1); @@ -1689,7 +1524,7 @@ void TabStrip::ResetAnimationState(bool stop_new_tab_timer) { int TabStrip::GetMiniTabCount() const { int mini_count = 0; for (size_t i = 0; i < tab_data_.size(); ++i) { - if (tab_data_[i].tab->mini()) + if (tab_data_[i].tab->data().mini) mini_count++; else return mini_count; @@ -1700,7 +1535,7 @@ int TabStrip::GetMiniTabCount() const { int TabStrip::GetNanoTabCount() const { int nano_count = 0; for (size_t i = 0; i < tab_data_.size(); ++i) { - if (tab_data_[i].tab->app()) + if (tab_data_[i].tab->data().app) nano_count++; else return nano_count; @@ -1750,7 +1585,7 @@ void TabStrip::HandleGlobalMouseMoveEvent() { bool TabStrip::HasPhantomTabs() const { for (int i = 0; i < GetTabCount(); ++i) { - if (GetTabAtTabDataIndex(i)->phantom()) + if (GetTabAtTabDataIndex(i)->data().phantom) return true; } return false; diff --git a/chrome/browser/views/tabs/tab_strip.h b/chrome/browser/views/tabs/tab_strip.h index 5279dd0..d803a25 100644 --- a/chrome/browser/views/tabs/tab_strip.h +++ b/chrome/browser/views/tabs/tab_strip.h @@ -11,7 +11,6 @@ #include "base/message_loop.h" #include "base/ref_counted.h" #include "base/timer.h" -#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/views/tabs/base_tab_strip.h" #include "chrome/browser/views/tabs/tab.h" #include "gfx/point.h" @@ -20,8 +19,6 @@ #include "views/controls/button/image_button.h" class DraggedTabController; -class ScopedMouseCloseWidthCalculator; -class TabStripModel; namespace views { class ImageView; @@ -46,23 +43,13 @@ class WidgetWin; // /////////////////////////////////////////////////////////////////////////////// class TabStrip : public BaseTabStrip, - public TabStripModelObserver, - public Tab::TabDelegate, public views::ButtonListener, public MessageLoopForUI::Observer, public views::BoundsAnimatorObserver { public: - explicit TabStrip(TabStripModel* model); + explicit TabStrip(TabStripController* controller); virtual ~TabStrip(); - // Returns true if the TabStrip can accept input events. This returns false - // when the TabStrip is animating to a new state and as such the user should - // not be allowed to interact with the TabStrip. - bool CanProcessInputEvents() const; - - // Accessors for the model and individual Tabs. - TabStripModel* model() const { return model_; } - // Set whether the new tab button is enabled. void set_new_tab_button_enabled(bool enabled) { new_tab_button_enabled_ = enabled; @@ -79,24 +66,12 @@ class TabStrip : public BaseTabStrip, // Retrieves the ideal bounds for the Tab at the specified index. gfx::Rect GetIdealBounds(int tab_data_index); - // Returns the currently selected tab. - Tab* GetSelectedTab() const; - // Creates the new tab button. void InitTabStripButtons(); - // Return true if this tab strip is compatible with the provided tab strip. - // Compatible tab strips can transfer tabs during drag and drop. - bool IsCompatibleWith(TabStrip* other) const; - // Returns the bounds of the new tab button. gfx::Rect GetNewTabButtonBounds(); - // 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); @@ -104,9 +79,22 @@ class TabStrip : public BaseTabStrip, virtual void SetDraggedTabBounds(int tab_index, const gfx::Rect& tab_bounds); virtual bool IsDragSessionActive() const; - virtual void UpdateLoadingAnimations(); virtual bool IsAnimating() const; virtual TabStrip* AsTabStrip(); + virtual void AddTabAt(int model_index, + bool foreground, + const TabRendererData& data); + virtual void RemoveTabAt(int model_index); + virtual void SelectTabAt(int old_model_index, int new_model_index); + virtual void MoveTab(int from_model_index, int to_model_index); + virtual void TabTitleChangedNotLoading(int model_index); + virtual void SetTabData(int model_index, const TabRendererData& data); + virtual void StartHighlight(int model_index); + virtual void StopAllHighlighting(); + virtual BaseTabRenderer* GetBaseTabAtModelIndex(int model_index) const; + virtual BaseTabRenderer* GetBaseTabAtTabIndex(int tab_index) const; + virtual int GetModelIndexOfBaseTab(const BaseTabRenderer* tab) const; + virtual BaseTabRenderer* CreateTabForDragging(); // views::View overrides: virtual void PaintChildren(gfx::Canvas* canvas); @@ -138,44 +126,11 @@ class TabStrip : public BaseTabStrip, virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled); - // TabStripModelObserver implementation: - virtual void TabInsertedAt(TabContents* contents, - int model_index, - bool foreground); - virtual void TabDetachedAt(TabContents* contents, int model_index); - virtual void TabSelectedAt(TabContents* old_contents, - TabContents* contents, - int model_index, - bool user_gesture); - virtual void TabMoved(TabContents* contents, - int from_model_index, - int to_model_index); - virtual void TabChangedAt(TabContents* contents, - int model_index, - TabChangeType change_type); - virtual void TabReplacedAt(TabContents* old_contents, - TabContents* new_contents, - int model_index); - virtual void TabMiniStateChanged(TabContents* contents, int model_index); - virtual void TabBlockedStateChanged(TabContents* contents, int model_index); - - // Tab::Delegate implementation: - virtual bool IsTabSelected(const Tab* tab) const; - virtual bool IsTabPinned(const Tab* tab) const; - virtual void SelectTab(Tab* tab); - 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( - TabStripModel::ContextMenuCommand command_id, Tab* tab); - virtual void StopHighlightTabsForCommand( - TabStripModel::ContextMenuCommand command_id, Tab* tab); - virtual void StopAllHighlighting(); - virtual void MaybeStartDrag(Tab* tab, const views::MouseEvent& event); + // TabController overrides. + virtual bool IsTabSelected(const BaseTabRenderer* btr) const; + virtual bool IsTabPinned(const BaseTabRenderer* btr) const; + virtual void MaybeStartDrag(BaseTabRenderer* btr, + const views::MouseEvent& event); virtual void ContinueDrag(const views::MouseEvent& event); virtual bool EndDrag(bool canceled); virtual bool HasAvailableDragActions() const; @@ -265,7 +220,6 @@ class TabStrip : public BaseTabStrip, gfx::Rect ideal_bounds; }; - TabStrip(); void Init(); // Set the images for the new tab button. @@ -288,7 +242,7 @@ class TabStrip : public BaseTabStrip, // WARNING: this is the number of tabs displayed by the tabstrip, which if // an animation is ongoing is not necessarily the same as the number of tabs // in the model. - int GetTabCount() const; + virtual int GetTabCount() const; // Returns the number of mini-tabs. int GetMiniTabCount() const; @@ -428,9 +382,6 @@ class TabStrip : public BaseTabStrip, // -- Member Variables ------------------------------------------------------ - // Our model. - TabStripModel* model_; - // A factory that is used to construct a delayed callback to the // ResizeLayoutTabsNow method. ScopedRunnableMethodFactory<TabStrip> resize_layout_factory_; diff --git a/chrome/browser/views/tabs/tab_strip_controller.h b/chrome/browser/views/tabs/tab_strip_controller.h new file mode 100644 index 0000000..90cd87c --- /dev/null +++ b/chrome/browser/views/tabs/tab_strip_controller.h @@ -0,0 +1,71 @@ +// Copyright (c) 2010 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_CONTROLLER_H_ +#define CHROME_BROWSER_VIEWS_TABS_TAB_STRIP_CONTROLLER_H_ + +#include <vector> + +class BaseTabRenderer; +class BaseTabStrip; +class GURL; + +namespace gfx { +class Point; +} + +// Model/Controller for the TabStrip. +// NOTE: All indices used by this class are in model coordinates. +class TabStripController { + public: + virtual ~TabStripController() {} + + // Returns the number of tabs in the model. + virtual int GetCount() const = 0; + + // Returns true if |index| is a valid model index. + virtual bool IsValidIndex(int index) const = 0; + + // Returns the selected index, in terms of the model. + virtual int GetSelectedIndex() const = 0; + + // Returns true if the selected index is selected. + virtual bool IsTabSelected(int index) const = 0; + + // Returns true if the selected index is pinned. + virtual bool IsTabPinned(int index) const = 0; + + // Returns true if the selected index is the new tab page. + virtual bool IsNewTabPage(int index) const = 0; + + // Select the tab at the specified index in the model. + virtual void SelectTab(int index) = 0; + + // 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 point in screen coords. + virtual void ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p) = 0; + + // Updates the loading animations of all the tabs. + virtual void UpdateLoadingAnimations() = 0; + + // Returns true if the associated TabStrip's delegate supports tab moving or + // detaching. Used by the Frame to determine if dragging on the Tab + // itself should move the window in cases where there's only one + // non drag-able Tab. + virtual int HasAvailableDragActions() const = 0; + + // Performans a drop at the specified location. + virtual void PerformDrop(bool drop_before, int index, const GURL& url) = 0; + + // Return true if this tab strip is compatible with the provided tab strip. + // Compatible tab strips can transfer tabs during drag and drop. + virtual bool IsCompatibleWith(BaseTabStrip* other) const = 0; + + // Creates the new tab. + virtual void CreateNewTab() = 0; +}; + +#endif // CHROME_BROWSER_VIEWS_TABS_TAB_STRIP_CONTROLLER_H_ |