Open the WrenchMenu on mouseover when dragging a browser action

Enable dragging from the main container to the overflow container by opening the
wrench menu when hovering over the button while dragging a browser action. This
also requires keeping the menu open while the drag is happening.

Note: this doesn't fix drag calculations for the overflow menu.
In an effort to keep these patches small, that will come later.

BUG=393038

Review URL: https://codereview.chromium.org/399143004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@285142 0039d316-1c4b-4281-b951-d872f2087c98
parent 0cc68a24
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "chrome/app/chrome_command_ids.h" #include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/defaults.h" #include "chrome/browser/defaults.h"
#include "chrome/browser/extensions/extension_toolbar_model.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/search/search.h" #include "chrome/browser/search/search.h"
...@@ -740,8 +741,12 @@ void WrenchMenuModel::AddGlobalErrorMenuItems() { ...@@ -740,8 +741,12 @@ void WrenchMenuModel::AddGlobalErrorMenuItems() {
void WrenchMenuModel::CreateExtensionToolbarOverflowMenu() { void WrenchMenuModel::CreateExtensionToolbarOverflowMenu() {
#if defined(TOOLKIT_VIEWS) #if defined(TOOLKIT_VIEWS)
AddItem(IDC_EXTENSIONS_OVERFLOW_MENU, base::string16()); AddItem(IDC_EXTENSIONS_OVERFLOW_MENU, base::string16());
// TODO(devlin): Add the separator only if there are > 0 icons to show. // We only add the separator if there are > 0 items to show in the overflow.
AddSeparator(ui::UPPER_SEPARATOR); extensions::ExtensionToolbarModel* toolbar_model =
extensions::ExtensionToolbarModel::Get(browser_->profile());
// A count of -1 means all actions are visible.
if (toolbar_model->GetVisibleIconCount() != -1)
AddSeparator(ui::UPPER_SEPARATOR);
#endif // defined(TOOLKIT_VIEWS) #endif // defined(TOOLKIT_VIEWS)
} }
......
...@@ -10,8 +10,12 @@ ...@@ -10,8 +10,12 @@
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/clipboard.h"
const char* BrowserActionDragData::kClipboardFormatString = namespace {
"chromium/x-browser-actions";
// The MIME type for the clipboard format for BrowserActionDragData.
const char kClipboardFormatString[] = "chromium/x-browser-actions";
}
BrowserActionDragData::BrowserActionDragData() BrowserActionDragData::BrowserActionDragData()
: profile_(NULL), index_(static_cast<size_t>(-1)) { : profile_(NULL), index_(static_cast<size_t>(-1)) {
...@@ -22,7 +26,23 @@ BrowserActionDragData::BrowserActionDragData( ...@@ -22,7 +26,23 @@ BrowserActionDragData::BrowserActionDragData(
: profile_(NULL), id_(id), index_(index) { : profile_(NULL), id_(id), index_(index) {
} }
bool BrowserActionDragData::IsFromProfile(Profile* profile) const { bool BrowserActionDragData::GetDropFormats(
std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
custom_formats->insert(GetBrowserActionCustomFormat());
return true;
}
bool BrowserActionDragData::AreDropTypesRequired() {
return true;
}
bool BrowserActionDragData::CanDrop(const ui::OSExchangeData& data,
const Profile* profile) {
BrowserActionDragData drop_data;
return drop_data.Read(data) && drop_data.IsFromProfile(profile);
}
bool BrowserActionDragData::IsFromProfile(const Profile* profile) const {
return profile_ == profile; return profile_ == profile;
} }
......
...@@ -21,12 +21,20 @@ class BrowserActionDragData { ...@@ -21,12 +21,20 @@ class BrowserActionDragData {
BrowserActionDragData(); BrowserActionDragData();
BrowserActionDragData(const std::string& id, int index); BrowserActionDragData(const std::string& id, int index);
// These mirror the views::View and views::MenuDelegate methods for dropping,
// and return the appropriate results for being able to drop an extension's
// BrowserAction view.
static bool GetDropFormats(
std::set<ui::OSExchangeData::CustomFormat>* custom_formats);
static bool AreDropTypesRequired();
static bool CanDrop(const ui::OSExchangeData& data, const Profile* profile);
const std::string& id() const { return id_; } const std::string& id() const { return id_; }
size_t index() const { return index_; } size_t index() const { return index_; }
// Returns true if this data is from the specified profile. // Returns true if this data is from the specified profile.
bool IsFromProfile(Profile* profile) const; bool IsFromProfile(const Profile* profile) const;
#if defined(TOOLKIT_VIEWS) #if defined(TOOLKIT_VIEWS)
void Write(Profile* profile, ui::OSExchangeData* data) const; void Write(Profile* profile, ui::OSExchangeData* data) const;
...@@ -51,9 +59,6 @@ class BrowserActionDragData { ...@@ -51,9 +59,6 @@ class BrowserActionDragData {
// The index of the view being dragged. // The index of the view being dragged.
size_t index_; size_t index_;
// The MIME type for the clipboard format for BrowserActionDragData.
static const char* kClipboardFormatString;
DISALLOW_COPY_AND_ASSIGN(BrowserActionDragData); DISALLOW_COPY_AND_ASSIGN(BrowserActionDragData);
}; };
......
...@@ -181,21 +181,17 @@ bool BrowserActionOverflowMenuController::GetDropFormats( ...@@ -181,21 +181,17 @@ bool BrowserActionOverflowMenuController::GetDropFormats(
views::MenuItemView* menu, views::MenuItemView* menu,
int* formats, int* formats,
std::set<OSExchangeData::CustomFormat>* custom_formats) { std::set<OSExchangeData::CustomFormat>* custom_formats) {
custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat()); return BrowserActionDragData::GetDropFormats(custom_formats);
return true;
} }
bool BrowserActionOverflowMenuController::AreDropTypesRequired( bool BrowserActionOverflowMenuController::AreDropTypesRequired(
views::MenuItemView* menu) { views::MenuItemView* menu) {
return true; return BrowserActionDragData::AreDropTypesRequired();
} }
bool BrowserActionOverflowMenuController::CanDrop( bool BrowserActionOverflowMenuController::CanDrop(
views::MenuItemView* menu, const OSExchangeData& data) { views::MenuItemView* menu, const OSExchangeData& data) {
BrowserActionDragData drop_data; return BrowserActionDragData::CanDrop(data, owner_->profile());
if (!drop_data.Read(data))
return false;
return drop_data.IsFromProfile(owner_->profile());
} }
int BrowserActionOverflowMenuController::GetDropOperation( int BrowserActionOverflowMenuController::GetDropOperation(
......
...@@ -37,18 +37,26 @@ void MenuTestBase::KeyPress(ui::KeyboardCode keycode, ...@@ -37,18 +37,26 @@ void MenuTestBase::KeyPress(ui::KeyboardCode keycode,
false, false, next); false, false, next);
} }
int MenuTestBase::GetMenuRunnerFlags() {
return views::MenuRunner::HAS_MNEMONICS;
}
void MenuTestBase::SetUp() { void MenuTestBase::SetUp() {
button_ = new views::MenuButton( button_ = new views::MenuButton(
NULL, base::ASCIIToUTF16("Menu Test"), this, true); NULL, base::ASCIIToUTF16("Menu Test"), this, true);
menu_ = new views::MenuItemView(this); menu_ = new views::MenuItemView(this);
BuildMenu(menu_); BuildMenu(menu_);
menu_runner_.reset( menu_runner_.reset(new views::MenuRunner(menu_, GetMenuRunnerFlags()));
new views::MenuRunner(menu_, views::MenuRunner::HAS_MNEMONICS));
ViewEventTestBase::SetUp(); ViewEventTestBase::SetUp();
} }
void MenuTestBase::TearDown() { void MenuTestBase::TearDown() {
// We cancel the menu first because certain operations (like a menu opened
// with views::MenuRunner::FOR_DROP) don't take kindly to simply pulling the
// runner out from under them.
menu_runner_->Cancel();
menu_runner_.reset(); menu_runner_.reset();
menu_ = NULL; menu_ = NULL;
ViewEventTestBase::TearDown(); ViewEventTestBase::TearDown();
......
...@@ -60,6 +60,10 @@ class MenuTestBase : public ViewEventTestBase, ...@@ -60,6 +60,10 @@ class MenuTestBase : public ViewEventTestBase,
// Called once the menu is open. // Called once the menu is open.
virtual void DoTestWithMenuOpen() = 0; virtual void DoTestWithMenuOpen() = 0;
// Returns a bitmask of flags to use when creating the |menu_runner_|. By
// default, this is only views::MenuRunner::HAS_MNEMONICS.
virtual int GetMenuRunnerFlags();
// ViewEventTestBase implementation. // ViewEventTestBase implementation.
virtual void SetUp() OVERRIDE; virtual void SetUp() OVERRIDE;
virtual void TearDown() OVERRIDE; virtual void TearDown() OVERRIDE;
......
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
#include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/interactive_test_utils.h"
#include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/menu/submenu_view.h" #include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/view.h" #include "ui/views/view.h"
...@@ -343,6 +345,9 @@ void MenuViewDragAndDropTestTestInMenuDrag::Step4() { ...@@ -343,6 +345,9 @@ void MenuViewDragAndDropTestTestInMenuDrag::Step4() {
Done(); Done();
} }
// Test that an in-menu (i.e., entirely implemented in the menu code) closes the
// menu automatically once the drag is complete, and does not ask the delegate
// to stay open.
VIEW_TEST(MenuViewDragAndDropTestTestInMenuDrag, MAYBE(TestInMenuDrag)) VIEW_TEST(MenuViewDragAndDropTestTestInMenuDrag, MAYBE(TestInMenuDrag))
class MenuViewDragAndDropTestNestedDrag : public MenuViewDragAndDropTest { class MenuViewDragAndDropTestNestedDrag : public MenuViewDragAndDropTest {
...@@ -438,5 +443,72 @@ void MenuViewDragAndDropTestNestedDrag::Step4() { ...@@ -438,5 +443,72 @@ void MenuViewDragAndDropTestNestedDrag::Step4() {
Done(); Done();
} }
// Test that a nested drag (i.e. one via a child view, and not entirely
// implemented in menu code) will consult the delegate before closing the view
// after the drag.
VIEW_TEST(MenuViewDragAndDropTestNestedDrag, VIEW_TEST(MenuViewDragAndDropTestNestedDrag,
MAYBE(MenuViewDragAndDropNestedDrag)) MAYBE(MenuViewDragAndDropNestedDrag))
class MenuViewDragAndDropForDropStayOpen : public MenuViewDragAndDropTest {
public:
MenuViewDragAndDropForDropStayOpen() {}
virtual ~MenuViewDragAndDropForDropStayOpen() {}
private:
// MenuViewDragAndDropTest:
virtual int GetMenuRunnerFlags() OVERRIDE;
virtual void DoTestWithMenuOpen() OVERRIDE;
};
int MenuViewDragAndDropForDropStayOpen::GetMenuRunnerFlags() {
return views::MenuRunner::HAS_MNEMONICS |
views::MenuRunner::NESTED_DRAG |
views::MenuRunner::FOR_DROP;
}
void MenuViewDragAndDropForDropStayOpen::DoTestWithMenuOpen() {
views::SubmenuView* submenu = menu()->GetSubmenu();
ASSERT_TRUE(submenu);
ASSERT_TRUE(submenu->IsShowing());
views::MenuController* controller = menu()->GetMenuController();
ASSERT_TRUE(controller);
EXPECT_FALSE(controller->IsCancelAllTimerRunningForTest());
Done();
}
// Test that if a menu is opened for a drop which is handled by a child view
// that the menu does not immediately try to close.
VIEW_TEST(MenuViewDragAndDropForDropStayOpen, MenuViewStaysOpenForNestedDrag)
class MenuViewDragAndDropForDropCancel : public MenuViewDragAndDropTest {
public:
MenuViewDragAndDropForDropCancel() {}
virtual ~MenuViewDragAndDropForDropCancel() {}
private:
// MenuViewDragAndDropTest:
virtual int GetMenuRunnerFlags() OVERRIDE;
virtual void DoTestWithMenuOpen() OVERRIDE;
};
int MenuViewDragAndDropForDropCancel::GetMenuRunnerFlags() {
return views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::FOR_DROP;
}
void MenuViewDragAndDropForDropCancel::DoTestWithMenuOpen() {
views::SubmenuView* submenu = menu()->GetSubmenu();
ASSERT_TRUE(submenu);
ASSERT_TRUE(submenu->IsShowing());
views::MenuController* controller = menu()->GetMenuController();
ASSERT_TRUE(controller);
EXPECT_TRUE(controller->IsCancelAllTimerRunningForTest());
Done();
}
// Test that if a menu is opened for a drop handled entirely by menu code, the
// menu will try to close if it does not receive any drag updates.
VIEW_TEST(MenuViewDragAndDropForDropCancel, MenuViewCancelsForOwnDrag)
...@@ -158,6 +158,10 @@ void BrowserActionButton::ViewHierarchyChanged( ...@@ -158,6 +158,10 @@ void BrowserActionButton::ViewHierarchyChanged(
MenuButton::ViewHierarchyChanged(details); MenuButton::ViewHierarchyChanged(details);
} }
void BrowserActionButton::OnDragDone() {
delegate_->OnBrowserActionViewDragDone();
}
bool BrowserActionButton::CanHandleAccelerators() const { bool BrowserActionButton::CanHandleAccelerators() const {
// View::CanHandleAccelerators() checks to see if the view is visible before // View::CanHandleAccelerators() checks to see if the view is visible before
// allowing it to process accelerators. This is not appropriate for browser // allowing it to process accelerators. This is not appropriate for browser
......
...@@ -57,6 +57,12 @@ class BrowserActionView : public views::View { ...@@ -57,6 +57,12 @@ class BrowserActionView : public views::View {
// Whether the container for this button is shown inside a menu. // Whether the container for this button is shown inside a menu.
virtual bool ShownInsideMenu() const = 0; virtual bool ShownInsideMenu() const = 0;
// Notifies that a drag completed. Note this will only happen if the view
// wasn't removed during the drag-and-drop process (i.e., not when there
// was a move in the browser actions, since we re-create the views each
// time we re-order the browser actions).
virtual void OnBrowserActionViewDragDone() = 0;
protected: protected:
virtual ~Delegate() {} virtual ~Delegate() {}
}; };
...@@ -201,6 +207,7 @@ class BrowserActionButton : public views::MenuButton, ...@@ -201,6 +207,7 @@ class BrowserActionButton : public views::MenuButton,
// Overridden from views::View: // Overridden from views::View:
virtual void ViewHierarchyChanged( virtual void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) OVERRIDE; const ViewHierarchyChangedDetails& details) OVERRIDE;
virtual void OnDragDone() OVERRIDE;
private: private:
virtual ~BrowserActionButton(); virtual ~BrowserActionButton();
......
...@@ -265,6 +265,15 @@ bool BrowserActionsContainer::ShownInsideMenu() const { ...@@ -265,6 +265,15 @@ bool BrowserActionsContainer::ShownInsideMenu() const {
return in_overflow_mode(); return in_overflow_mode();
} }
void BrowserActionsContainer::OnBrowserActionViewDragDone() {
// We notify here as well as in OnPerformDrop because the dragged view is
// removed in OnPerformDrop, so it will never get its OnDragDone() call.
// TODO(devlin): we should see about fixing that.
FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
observers_,
OnBrowserActionDragDone());
}
void BrowserActionsContainer::AddObserver( void BrowserActionsContainer::AddObserver(
BrowserActionsContainerObserver* observer) { BrowserActionsContainerObserver* observer) {
observers_.AddObserver(observer); observers_.AddObserver(observer);
...@@ -373,18 +382,15 @@ void BrowserActionsContainer::Layout() { ...@@ -373,18 +382,15 @@ void BrowserActionsContainer::Layout() {
bool BrowserActionsContainer::GetDropFormats( bool BrowserActionsContainer::GetDropFormats(
int* formats, int* formats,
std::set<OSExchangeData::CustomFormat>* custom_formats) { std::set<OSExchangeData::CustomFormat>* custom_formats) {
custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat()); return BrowserActionDragData::GetDropFormats(custom_formats);
return true;
} }
bool BrowserActionsContainer::AreDropTypesRequired() { bool BrowserActionsContainer::AreDropTypesRequired() {
return true; return BrowserActionDragData::AreDropTypesRequired();
} }
bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) { bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) {
BrowserActionDragData drop_data; return BrowserActionDragData::CanDrop(data, profile_);
return drop_data.Read(data) ? drop_data.IsFromProfile(profile_) : false;
} }
void BrowserActionsContainer::OnDragEntered( void BrowserActionsContainer::OnDragEntered(
...@@ -499,6 +505,9 @@ int BrowserActionsContainer::OnPerformDrop( ...@@ -499,6 +505,9 @@ int BrowserActionsContainer::OnPerformDrop(
browser_action_views_[data.index()]->button()->extension(), i); browser_action_views_[data.index()]->button()->extension(), i);
OnDragExited(); // Perform clean up after dragging. OnDragExited(); // Perform clean up after dragging.
FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
observers_,
OnBrowserActionDragDone());
return ui::DragDropTypes::DRAG_MOVE; return ui::DragDropTypes::DRAG_MOVE;
} }
......
...@@ -245,6 +245,7 @@ class BrowserActionsContainer ...@@ -245,6 +245,7 @@ class BrowserActionsContainer
virtual void OnBrowserActionExecuted(BrowserActionButton* button) OVERRIDE; virtual void OnBrowserActionExecuted(BrowserActionButton* button) OVERRIDE;
virtual void OnBrowserActionVisibilityChanged() OVERRIDE; virtual void OnBrowserActionVisibilityChanged() OVERRIDE;
virtual bool ShownInsideMenu() const OVERRIDE; virtual bool ShownInsideMenu() const OVERRIDE;
virtual void OnBrowserActionViewDragDone() OVERRIDE;
// Overridden from extension::ExtensionKeybindingRegistry::Delegate: // Overridden from extension::ExtensionKeybindingRegistry::Delegate:
virtual extensions::ActiveTabPermissionGranter* virtual extensions::ActiveTabPermissionGranter*
......
...@@ -9,6 +9,7 @@ class BrowserActionsContainerObserver { ...@@ -9,6 +9,7 @@ class BrowserActionsContainerObserver {
public: public:
virtual void OnBrowserActionsContainerAnimationEnded() {} virtual void OnBrowserActionsContainerAnimationEnded() {}
virtual void OnBrowserActionsContainerDestroyed() {} virtual void OnBrowserActionsContainerDestroyed() {}
virtual void OnBrowserActionDragDone() {}
protected: protected:
virtual ~BrowserActionsContainerObserver() {} virtual ~BrowserActionsContainerObserver() {}
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar/browser_actions_container.h" #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/browser/ui/views/toolbar/wrench_menu.h"
#include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_item_view.h"
namespace { namespace {
...@@ -18,15 +19,28 @@ const int kVerticalPadding = 8; ...@@ -18,15 +19,28 @@ const int kVerticalPadding = 8;
} // namespace } // namespace
ExtensionToolbarMenuView::ExtensionToolbarMenuView(Browser* browser) ExtensionToolbarMenuView::ExtensionToolbarMenuView(Browser* browser,
: browser_(browser) { WrenchMenu* wrench_menu)
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); : browser_(browser),
wrench_menu_(wrench_menu),
container_(NULL),
browser_actions_container_observer_(this) {
BrowserActionsContainer* main =
BrowserView::GetBrowserViewForBrowser(browser_)
->toolbar()->browser_actions();
container_ = new BrowserActionsContainer( container_ = new BrowserActionsContainer(
browser_, browser_,
NULL, // No owner view, means no extra keybindings are registered. NULL, // No owner view, means no extra keybindings are registered.
browser_view->GetToolbarView()->browser_actions()); main);
container_->Init(); container_->Init();
AddChildView(container_); AddChildView(container_);
// If we were opened for a drop command, we have to wait for the drop to
// finish so we can close the wrench menu.
if (wrench_menu_->for_drop()) {
browser_actions_container_observer_.Add(container_);
browser_actions_container_observer_.Add(main);
}
} }
ExtensionToolbarMenuView::~ExtensionToolbarMenuView() { ExtensionToolbarMenuView::~ExtensionToolbarMenuView() {
...@@ -48,3 +62,8 @@ void ExtensionToolbarMenuView::Layout() { ...@@ -48,3 +62,8 @@ void ExtensionToolbarMenuView::Layout() {
SetBounds(views::MenuItemView::label_start(), 0, sz.width(), height); SetBounds(views::MenuItemView::label_start(), 0, sz.width(), height);
container_->SetBounds(0, 0, sz.width(), height); container_->SetBounds(0, 0, sz.width(), height);
} }
void ExtensionToolbarMenuView::OnBrowserActionDragDone() {
DCHECK(wrench_menu_->for_drop());
wrench_menu_->CloseMenu();
}
...@@ -5,17 +5,24 @@ ...@@ -5,17 +5,24 @@
#ifndef CHROME_BROWSER_UI_VIEWS_TOOLBAR_EXTENSION_TOOLBAR_MENU_VIEW_H_ #ifndef CHROME_BROWSER_UI_VIEWS_TOOLBAR_EXTENSION_TOOLBAR_MENU_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_EXTENSION_TOOLBAR_MENU_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_TOOLBAR_EXTENSION_TOOLBAR_MENU_VIEW_H_
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
#include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h"
#include "ui/views/view.h" #include "ui/views/view.h"
class Browser; class Browser;
class BrowserActionsContainer; class BrowserActionsContainer;
class WrenchMenu;
// ExtensionToolbarMenuView is the view containing the extension actions that // ExtensionToolbarMenuView is the view containing the extension actions that
// overflowed from the BrowserActionsContainer, and is contained in and owned by // overflowed from the BrowserActionsContainer, and is contained in and owned by
// the wrench menu. // the wrench menu.
class ExtensionToolbarMenuView : public views::View { // In the event that the WrenchMenu was opened for an Extension Action drag-and-
// drop, this will also close the menu upon completion.
class ExtensionToolbarMenuView : public views::View,
public BrowserActionsContainerObserver {
public: public:
explicit ExtensionToolbarMenuView(Browser* browser); ExtensionToolbarMenuView(Browser* browser, WrenchMenu* wrench_menu);
virtual ~ExtensionToolbarMenuView(); virtual ~ExtensionToolbarMenuView();
// views::View: // views::View:
...@@ -23,9 +30,21 @@ class ExtensionToolbarMenuView : public views::View { ...@@ -23,9 +30,21 @@ class ExtensionToolbarMenuView : public views::View {
virtual void Layout() OVERRIDE; virtual void Layout() OVERRIDE;
private: private:
// BrowserActionsContainerObserver:
virtual void OnBrowserActionDragDone() OVERRIDE;
// The associated browser.
Browser* browser_; Browser* browser_;
// The WrenchMenu, which may need to be closed after a drag-and-drop.
WrenchMenu* wrench_menu_;
// The overflow BrowserActionsContainer which is nested in this view.
BrowserActionsContainer* container_; BrowserActionsContainer* container_;
ScopedObserver<BrowserActionsContainer, BrowserActionsContainerObserver>
browser_actions_container_observer_;
DISALLOW_COPY_AND_ASSIGN(ExtensionToolbarMenuView); DISALLOW_COPY_AND_ASSIGN(ExtensionToolbarMenuView);
}; };
......
...@@ -341,6 +341,37 @@ void ToolbarView::ShowBrowserActionPopup( ...@@ -341,6 +341,37 @@ void ToolbarView::ShowBrowserActionPopup(
browser_actions_->ShowPopup(extension, true); browser_actions_->ShowPopup(extension, true);
} }
void ToolbarView::ShowAppMenu(bool for_drop) {
if (wrench_menu_.get() && wrench_menu_->IsShowing())
return;
int run_flags = 0;
bool use_new_menu = false;
// TODO: remove this.
#if !defined(OS_LINUX) || defined(OS_CHROMEOS)
if (GetNativeTheme() == ui::NativeThemeAura::instance()) {
use_new_menu = true;
run_flags |= WrenchMenu::SUPPORTS_NEW_SEPARATORS | WrenchMenu::USE_NEW_MENU;
}
#endif
if (keyboard::KeyboardController::GetInstance() &&
keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
keyboard::KeyboardController::GetInstance()->HideKeyboard(
keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
}
if (for_drop)
run_flags |= WrenchMenu::FOR_DROP;
wrench_menu_.reset(new WrenchMenu(browser_, run_flags));
wrench_menu_model_.reset(new WrenchMenuModel(this, browser_, use_new_menu));
wrench_menu_->Init(wrench_menu_model_.get());
FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
wrench_menu_->RunMenu(app_menu_);
}
views::MenuButton* ToolbarView::app_menu() const { views::MenuButton* ToolbarView::app_menu() const {
return app_menu_; return app_menu_;
} }
...@@ -375,30 +406,7 @@ void ToolbarView::OnMenuButtonClicked(views::View* source, ...@@ -375,30 +406,7 @@ void ToolbarView::OnMenuButtonClicked(views::View* source,
const gfx::Point& point) { const gfx::Point& point) {
TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked"); TRACE_EVENT0("views", "ToolbarView::OnMenuButtonClicked");
DCHECK_EQ(VIEW_ID_APP_MENU, source->id()); DCHECK_EQ(VIEW_ID_APP_MENU, source->id());
ShowAppMenu(false); // Not for drop.
bool use_new_menu = false;
bool supports_new_separators = false;
// TODO: remove this.
#if !defined(OS_LINUX) || defined(OS_CHROMEOS)
supports_new_separators =
GetNativeTheme() == ui::NativeThemeAura::instance();
use_new_menu = supports_new_separators;
#endif
if (keyboard::KeyboardController::GetInstance() &&
keyboard::KeyboardController::GetInstance()->keyboard_visible()) {
keyboard::KeyboardController::GetInstance()->HideKeyboard(
keyboard::KeyboardController::HIDE_REASON_AUTOMATIC);
}
wrench_menu_.reset(new WrenchMenu(browser_, use_new_menu,
supports_new_separators));
wrench_menu_model_.reset(new WrenchMenuModel(this, browser_, use_new_menu));
wrench_menu_->Init(wrench_menu_model_.get());
FOR_EACH_OBSERVER(views::MenuListener, menu_listeners_, OnMenuOpened());
wrench_menu_->RunMenu(app_menu_);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
......
...@@ -100,6 +100,10 @@ class ToolbarView : public views::AccessiblePaneView, ...@@ -100,6 +100,10 @@ class ToolbarView : public views::AccessiblePaneView,
// Shows the extension's browser action, if present. // Shows the extension's browser action, if present.
void ShowBrowserActionPopup(const extensions::Extension* extension); void ShowBrowserActionPopup(const extensions::Extension* extension);
// Shows the app (wrench) menu. |for_drop| indicates whether the menu is
// opened for a drag-and-drop operation.
void ShowAppMenu(bool for_drop);
// Accessors. // Accessors.
Browser* browser() const { return browser_; } Browser* browser() const { return browser_; }
BrowserActionsContainer* browser_actions() const { return browser_actions_; } BrowserActionsContainer* browser_actions() const { return browser_actions_; }
......
// Copyright 2014 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 "base/run_loop.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
#include "chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h"
#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "extensions/common/feature_switch.h"
// Borrowed from chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc,
// since these are also disabled on Linux for drag and drop.
// TODO(erg): Fix DND tests on linux_aura. crbug.com/163931
#if defined(OS_LINUX) && defined(USE_AURA)
#define MAYBE(x) DISABLED_##x
#else
#define MAYBE(x) x
#endif
class ToolbarViewInteractiveUITest : public ExtensionBrowserTest {
public:
ToolbarViewInteractiveUITest();
virtual ~ToolbarViewInteractiveUITest();
protected:
ToolbarView* toolbar_view() { return toolbar_view_; }
BrowserActionsContainer* browser_actions() { return browser_actions_; }
// Performs a drag-and-drop operation by moving the mouse to |start|, clicking
// the left button, moving the mouse to |end|, and releasing the left button.
// TestWhileInDragOperation() is called after the mouse has moved to |end|,
// but before the click is released.
void DoDragAndDrop(const gfx::Point& start, const gfx::Point& end);
// A function to perform testing actions while in the drag operation from
// DoDragAndDrop.
void TestWhileInDragOperation();
private:
// Finishes the drag-and-drop operation started in DoDragAndDrop().
void FinishDragAndDrop(const base::Closure& quit_closure);
// InProcessBrowserTest:
virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE;
virtual void SetUpOnMainThread() OVERRIDE;
ToolbarView* toolbar_view_;
BrowserActionsContainer* browser_actions_;
// The drag-and-drop background thread.
scoped_ptr<base::Thread> dnd_thread_;
// Override the extensions-action-redesign switch.
scoped_ptr<extensions::FeatureSwitch::ScopedOverride> feature_override_;
};
ToolbarViewInteractiveUITest::ToolbarViewInteractiveUITest()
: toolbar_view_(NULL),
browser_actions_(NULL) {
}
ToolbarViewInteractiveUITest::~ToolbarViewInteractiveUITest() {
}
void ToolbarViewInteractiveUITest::DoDragAndDrop(const gfx::Point& start,
const gfx::Point& end) {
// Much of this function is modeled after methods in ViewEventTestBase (in
// particular, the |dnd_thread_|, but it's easier to move that here than try
// to make ViewEventTestBase play nice with a BrowserView (for the toolbar).
// TODO(devlin): In a perfect world, this would be factored better.
// Send the mouse to |start|, and click.
ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(start));
ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
ui_controls::LEFT, ui_controls::DOWN));
scoped_refptr<content::MessageLoopRunner> runner =
new content::MessageLoopRunner();
ui_controls::SendMouseMoveNotifyWhenDone(
end.x() + 10,
end.y(),
base::Bind(&ToolbarViewInteractiveUITest::FinishDragAndDrop,
base::Unretained(this),
runner->QuitClosure()));
// Also post a move task to the drag and drop thread.
if (!dnd_thread_.get()) {
dnd_thread_.reset(new base::Thread("mouse_move_thread"));
dnd_thread_->Start();
}
dnd_thread_->message_loop()->PostDelayedTask(
FROM_HERE,
base::Bind(base::IgnoreResult(&ui_controls::SendMouseMove),
end.x(),
end.y()),
base::TimeDelta::FromMilliseconds(200));
runner->Run();
}
void ToolbarViewInteractiveUITest::TestWhileInDragOperation() {
EXPECT_TRUE(toolbar_view()->IsWrenchMenuShowing());
}
void ToolbarViewInteractiveUITest::FinishDragAndDrop(
const base::Closure& quit_closure) {
dnd_thread_.reset();
TestWhileInDragOperation();
ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP);
ui_controls::RunClosureAfterAllPendingUIEvents(
quit_closure);
}
void ToolbarViewInteractiveUITest::SetUpCommandLine(
base::CommandLine* command_line) {
ExtensionBrowserTest::SetUpCommandLine(command_line);
// We do this before the rest of the setup because it can affect how the views
// are constructed.
feature_override_.reset(new extensions::FeatureSwitch::ScopedOverride(
extensions::FeatureSwitch::extension_action_redesign(), true));
}
void ToolbarViewInteractiveUITest::SetUpOnMainThread() {
ExtensionBrowserTest::SetUpOnMainThread();
toolbar_view_ = BrowserView::GetBrowserViewForBrowser(browser())->toolbar();
browser_actions_ = toolbar_view_->browser_actions();
}
IN_PROC_BROWSER_TEST_F(ToolbarViewInteractiveUITest,
MAYBE(TestWrenchMenuOpensOnDrag)) {
WrenchToolbarButton::g_open_wrench_immediately_for_testing = true;
// Load an extension that has a browser action.
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
.AppendASCII("browser_action")
.AppendASCII("basics")));
ASSERT_EQ(1u, browser_actions()->VisibleBrowserActions());
BrowserActionView* view = browser_actions()->GetBrowserActionViewAt(0);
ASSERT_TRUE(view);
gfx::Point browser_action_view_loc = test::GetCenterInScreenCoordinates(view);
gfx::Point wrench_button_loc =
test::GetCenterInScreenCoordinates(toolbar_view()->app_menu());
// Perform a drag and drop from the browser action view to the wrench button,
// which should open the wrench menu.
DoDragAndDrop(browser_action_view_loc, wrench_button_loc);
}
...@@ -572,7 +572,7 @@ class WrenchMenu::CutCopyPasteView : public WrenchMenuView { ...@@ -572,7 +572,7 @@ class WrenchMenu::CutCopyPasteView : public WrenchMenuView {
copy_index); copy_index);
InMenuButton* paste = CreateAndConfigureButton( InMenuButton* paste = CreateAndConfigureButton(
IDS_PASTE, IDS_PASTE,
menu->use_new_menu() && menu->supports_new_separators_ ? menu->use_new_menu() && menu->supports_new_separators() ?
InMenuButtonBackground::CENTER_BUTTON : InMenuButtonBackground::CENTER_BUTTON :
InMenuButtonBackground::RIGHT_BUTTON, InMenuButtonBackground::RIGHT_BUTTON,
paste_index); paste_index);
...@@ -654,7 +654,7 @@ class WrenchMenu::ZoomView : public WrenchMenuView { ...@@ -654,7 +654,7 @@ class WrenchMenu::ZoomView : public WrenchMenuView {
zoom_label_->SetHorizontalAlignment(gfx::ALIGN_RIGHT); zoom_label_->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
InMenuButtonBackground* center_bg = new InMenuButtonBackground( InMenuButtonBackground* center_bg = new InMenuButtonBackground(
menu->use_new_menu() && menu->supports_new_separators_ ? menu->use_new_menu() && menu->supports_new_separators() ?
InMenuButtonBackground::RIGHT_BUTTON : InMenuButtonBackground::RIGHT_BUTTON :
InMenuButtonBackground::CENTER_BUTTON, InMenuButtonBackground::CENTER_BUTTON,
menu->use_new_menu()); menu->use_new_menu());
...@@ -946,17 +946,14 @@ class WrenchMenu::RecentTabsMenuModelDelegate : public ui::MenuModelDelegate { ...@@ -946,17 +946,14 @@ class WrenchMenu::RecentTabsMenuModelDelegate : public ui::MenuModelDelegate {
// WrenchMenu ------------------------------------------------------------------ // WrenchMenu ------------------------------------------------------------------
WrenchMenu::WrenchMenu(Browser* browser, WrenchMenu::WrenchMenu(Browser* browser, int run_flags)
bool use_new_menu,
bool supports_new_separators)
: root_(NULL), : root_(NULL),
browser_(browser), browser_(browser),
selected_menu_model_(NULL), selected_menu_model_(NULL),
selected_index_(0), selected_index_(0),
bookmark_menu_(NULL), bookmark_menu_(NULL),
feedback_menu_item_(NULL), feedback_menu_item_(NULL),
use_new_menu_(use_new_menu), run_flags_(run_flags) {
supports_new_separators_(supports_new_separators) {
registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED, registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
content::Source<Profile>(browser_->profile())); content::Source<Profile>(browser_->profile()));
} }
...@@ -985,8 +982,14 @@ void WrenchMenu::Init(ui::MenuModel* model) { ...@@ -985,8 +982,14 @@ void WrenchMenu::Init(ui::MenuModel* model) {
DCHECK(command_id_to_entry_.find(i) == command_id_to_entry_.end()); DCHECK(command_id_to_entry_.find(i) == command_id_to_entry_.end());
#endif // defined(DEBUG) #endif // defined(DEBUG)
menu_runner_.reset( int32 types = views::MenuRunner::HAS_MNEMONICS;
new views::MenuRunner(root_, views::MenuRunner::HAS_MNEMONICS)); if (for_drop()) {
// We add NESTED_DRAG since currently the only operation to open the wrench
// menu for is an extension action drag, which is controlled by the child
// BrowserActionsContainer view.
types |= views::MenuRunner::FOR_DROP | views::MenuRunner::NESTED_DRAG;
}
menu_runner_.reset(new views::MenuRunner(root_, types));
} }
void WrenchMenu::RunMenu(views::MenuButton* host) { void WrenchMenu::RunMenu(views::MenuButton* host) {
...@@ -1011,6 +1014,11 @@ void WrenchMenu::RunMenu(views::MenuButton* host) { ...@@ -1011,6 +1014,11 @@ void WrenchMenu::RunMenu(views::MenuButton* host) {
selected_menu_model_->ActivatedAt(selected_index_); selected_menu_model_->ActivatedAt(selected_index_);
} }
void WrenchMenu::CloseMenu() {
if (menu_runner_.get())
menu_runner_->Cancel();
}
bool WrenchMenu::IsShowing() { bool WrenchMenu::IsShowing() {
return menu_runner_.get() && menu_runner_->IsRunning(); return menu_runner_.get() && menu_runner_->IsRunning();
} }
...@@ -1252,14 +1260,15 @@ void WrenchMenu::PopulateMenu(MenuItemView* parent, ...@@ -1252,14 +1260,15 @@ void WrenchMenu::PopulateMenu(MenuItemView* parent,
// The button container menu items have a special height which we have to // The button container menu items have a special height which we have to
// use instead of the normal height. // use instead of the normal height.
int height = 0; int height = 0;
if (use_new_menu_ && if (use_new_menu() &&
(model->GetCommandIdAt(i) == IDC_CUT || (model->GetCommandIdAt(i) == IDC_CUT ||
model->GetCommandIdAt(i) == IDC_ZOOM_MINUS)) model->GetCommandIdAt(i) == IDC_ZOOM_MINUS))
height = kMenuItemContainingButtonsHeight; height = kMenuItemContainingButtonsHeight;
scoped_ptr<ExtensionToolbarMenuView> extension_toolbar_menu_view; scoped_ptr<ExtensionToolbarMenuView> extension_toolbar_menu_view;
if (model->GetCommandIdAt(i) == IDC_EXTENSIONS_OVERFLOW_MENU) { if (model->GetCommandIdAt(i) == IDC_EXTENSIONS_OVERFLOW_MENU) {
extension_toolbar_menu_view.reset(new ExtensionToolbarMenuView(browser_)); extension_toolbar_menu_view.reset(
new ExtensionToolbarMenuView(browser_, this));
height = extension_toolbar_menu_view->GetPreferredSize().height(); height = extension_toolbar_menu_view->GetPreferredSize().height();
} }
...@@ -1363,7 +1372,7 @@ MenuItemView* WrenchMenu::AddMenuItem(MenuItemView* parent, ...@@ -1363,7 +1372,7 @@ MenuItemView* WrenchMenu::AddMenuItem(MenuItemView* parent,
if (menu_item) { if (menu_item) {
// Flush all buttons to the right side of the menu for the new menu type. // Flush all buttons to the right side of the menu for the new menu type.
menu_item->set_use_right_margin(!use_new_menu_); menu_item->set_use_right_margin(!use_new_menu());
menu_item->SetVisible(model->IsVisibleAt(model_index)); menu_item->SetVisible(model->IsVisibleAt(model_index));
if (menu_type == MenuModel::TYPE_COMMAND && model->HasIcons()) { if (menu_type == MenuModel::TYPE_COMMAND && model->HasIcons()) {
......
...@@ -37,10 +37,16 @@ class WrenchMenu : public views::MenuDelegate, ...@@ -37,10 +37,16 @@ class WrenchMenu : public views::MenuDelegate,
public BaseBookmarkModelObserver, public BaseBookmarkModelObserver,
public content::NotificationObserver { public content::NotificationObserver {
public: public:
// TODO: remove |use_new_menu| and |supports_new_separators|. enum RunFlags {
WrenchMenu(Browser* browser, // TODO: remove |USE_NEW_MENU| and |SUPPORTS_NEW_SEPARATORS|.
bool use_new_menu, USE_NEW_MENU = 1 << 0,
bool supports_new_separators); SUPPORTS_NEW_SEPARATORS = 1 << 1,
// Indicates that the menu was opened for a drag-and-drop operation.
FOR_DROP = 1 << 2,
};
WrenchMenu(Browser* browser, int run_flags);
virtual ~WrenchMenu(); virtual ~WrenchMenu();
void Init(ui::MenuModel* model); void Init(ui::MenuModel* model);
...@@ -48,10 +54,14 @@ class WrenchMenu : public views::MenuDelegate, ...@@ -48,10 +54,14 @@ class WrenchMenu : public views::MenuDelegate,
// Shows the menu relative to the specified view. // Shows the menu relative to the specified view.
void RunMenu(views::MenuButton* host); void RunMenu(views::MenuButton* host);
// Closes the menu if it is open, otherwise does nothing.
void CloseMenu();
// Whether the menu is currently visible to the user. // Whether the menu is currently visible to the user.
bool IsShowing(); bool IsShowing();
bool use_new_menu() const { return use_new_menu_; } bool use_new_menu() const { return (run_flags_ & USE_NEW_MENU) != 0; }
bool for_drop() const { return (run_flags_ & FOR_DROP) != 0; }
void AddObserver(WrenchMenuObserver* observer); void AddObserver(WrenchMenuObserver* observer);
void RemoveObserver(WrenchMenuObserver* observer); void RemoveObserver(WrenchMenuObserver* observer);
...@@ -111,6 +121,10 @@ class WrenchMenu : public views::MenuDelegate, ...@@ -111,6 +121,10 @@ class WrenchMenu : public views::MenuDelegate,
typedef std::pair<ui::MenuModel*,int> Entry; typedef std::pair<ui::MenuModel*,int> Entry;
typedef std::map<int,Entry> CommandIDToEntry; typedef std::map<int,Entry> CommandIDToEntry;
bool supports_new_separators() const {
return (run_flags_ & SUPPORTS_NEW_SEPARATORS) != 0;
}
// Populates |parent| with all the child menus in |model|. Recursively invokes // Populates |parent| with all the child menus in |model|. Recursively invokes
// |PopulateMenu| for any submenu. // |PopulateMenu| for any submenu.
void PopulateMenu(views::MenuItemView* parent, void PopulateMenu(views::MenuItemView* parent,
...@@ -177,9 +191,8 @@ class WrenchMenu : public views::MenuDelegate, ...@@ -177,9 +191,8 @@ class WrenchMenu : public views::MenuDelegate,
content::NotificationRegistrar registrar_; content::NotificationRegistrar registrar_;
const bool use_new_menu_; // The bit mask of RunFlags.
const int run_flags_;
const bool supports_new_separators_;
ObserverList<WrenchMenuObserver> observer_list_; ObserverList<WrenchMenuObserver> observer_list_;
......
...@@ -4,14 +4,28 @@ ...@@ -4,14 +4,28 @@
#include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h" #include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
#include "base/message_loop/message_loop.h"
#include "base/time/time.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "extensions/common/feature_switch.h"
#include "grit/theme_resources.h" #include "grit/theme_resources.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/base/theme_provider.h" #include "ui/base/theme_provider.h"
#include "ui/views/metrics.h"
#include "ui/views/painter.h" #include "ui/views/painter.h"
WrenchToolbarButton::WrenchToolbarButton(views::MenuButtonListener* listener) // static
: views::MenuButton(NULL, base::string16(), listener, false) { bool WrenchToolbarButton::g_open_wrench_immediately_for_testing = false;
wrench_icon_painter_.reset(new WrenchIconPainter(this));
WrenchToolbarButton::WrenchToolbarButton(ToolbarView* toolbar_view)
: views::MenuButton(NULL, base::string16(), toolbar_view, false),
wrench_icon_painter_(new WrenchIconPainter(this)),
toolbar_view_(toolbar_view),
allow_extension_dragging_(
extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()),
weak_factory_(this) {
} }
WrenchToolbarButton::~WrenchToolbarButton() { WrenchToolbarButton::~WrenchToolbarButton() {
...@@ -39,3 +53,54 @@ void WrenchToolbarButton::OnPaint(gfx::Canvas* canvas) { ...@@ -39,3 +53,54 @@ void WrenchToolbarButton::OnPaint(gfx::Canvas* canvas) {
void WrenchToolbarButton::ScheduleWrenchIconPaint() { void WrenchToolbarButton::ScheduleWrenchIconPaint() {
SchedulePaint(); SchedulePaint();
} }
bool WrenchToolbarButton::GetDropFormats(
int* formats, std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
return allow_extension_dragging_ ?
BrowserActionDragData::GetDropFormats(custom_formats) :
views::View::GetDropFormats(formats, custom_formats);
}
bool WrenchToolbarButton::AreDropTypesRequired() {
return allow_extension_dragging_ ?
BrowserActionDragData::AreDropTypesRequired() :
views::View::AreDropTypesRequired();
}
bool WrenchToolbarButton::CanDrop(const ui::OSExchangeData& data) {
return allow_extension_dragging_ ?
BrowserActionDragData::CanDrop(data,
toolbar_view_->browser()->profile()) :
views::View::CanDrop(data);
}
void WrenchToolbarButton::OnDragEntered(const ui::DropTargetEvent& event) {
DCHECK(allow_extension_dragging_);
DCHECK(!weak_factory_.HasWeakPtrs());
if (!g_open_wrench_immediately_for_testing) {
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&WrenchToolbarButton::ShowOverflowMenu,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
} else {
ShowOverflowMenu();
}
}
int WrenchToolbarButton::OnDragUpdated(const ui::DropTargetEvent& event) {
DCHECK(allow_extension_dragging_);
return ui::DragDropTypes::DRAG_MOVE;
}
void WrenchToolbarButton::OnDragExited() {
DCHECK(allow_extension_dragging_);
weak_factory_.InvalidateWeakPtrs();
}
int WrenchToolbarButton::OnPerformDrop(const ui::DropTargetEvent& event) {
DCHECK(allow_extension_dragging_);
return ui::DragDropTypes::DRAG_MOVE;
}
void WrenchToolbarButton::ShowOverflowMenu() {
toolbar_view_->ShowAppMenu(true); // For drop.
}
...@@ -5,14 +5,19 @@ ...@@ -5,14 +5,19 @@
#ifndef CHROME_BROWSER_UI_VIEWS_TOOLBAR_WRENCH_TOOLBAR_BUTTON_H_ #ifndef CHROME_BROWSER_UI_VIEWS_TOOLBAR_WRENCH_TOOLBAR_BUTTON_H_
#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_WRENCH_TOOLBAR_BUTTON_H_ #define CHROME_BROWSER_UI_VIEWS_TOOLBAR_WRENCH_TOOLBAR_BUTTON_H_
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/toolbar/wrench_icon_painter.h" #include "chrome/browser/ui/toolbar/wrench_icon_painter.h"
#include "ui/views/controls/button/menu_button.h" #include "ui/views/controls/button/menu_button.h"
class ToolbarView;
// TODO(gbillock): Rename this? No longer a wrench. // TODO(gbillock): Rename this? No longer a wrench.
class WrenchToolbarButton : public views::MenuButton, class WrenchToolbarButton : public views::MenuButton,
public WrenchIconPainter::Delegate { public WrenchIconPainter::Delegate {
public: public:
explicit WrenchToolbarButton(views::MenuButtonListener* menu_button_listener); explicit WrenchToolbarButton(ToolbarView* toolbar_view);
virtual ~WrenchToolbarButton(); virtual ~WrenchToolbarButton();
void SetSeverity(WrenchIconPainter::Severity severity, bool animate); void SetSeverity(WrenchIconPainter::Severity severity, bool animate);
...@@ -24,9 +29,36 @@ class WrenchToolbarButton : public views::MenuButton, ...@@ -24,9 +29,36 @@ class WrenchToolbarButton : public views::MenuButton,
// WrenchIconPainter::Delegate: // WrenchIconPainter::Delegate:
virtual void ScheduleWrenchIconPaint() OVERRIDE; virtual void ScheduleWrenchIconPaint() OVERRIDE;
// Opens the wrench menu immediately during a drag-and-drop operation.
// Used only in testing.
static bool g_open_wrench_immediately_for_testing;
private: private:
// views::View:
virtual bool GetDropFormats(int* formats,
std::set<ui::OSExchangeData::CustomFormat>* custom_formats) OVERRIDE;
virtual bool AreDropTypesRequired() OVERRIDE;
virtual bool CanDrop(const ui::OSExchangeData& data) OVERRIDE;
virtual void OnDragEntered(const ui::DropTargetEvent& event) OVERRIDE;
virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE;
virtual void OnDragExited() OVERRIDE;
virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE;
// Show the extension action overflow menu (which is in the app menu).
void ShowOverflowMenu();
scoped_ptr<WrenchIconPainter> wrench_icon_painter_; scoped_ptr<WrenchIconPainter> wrench_icon_painter_;
// Our owning toolbar view.
ToolbarView* toolbar_view_;
// Whether or not we should allow dragging extension icons onto this button
// (in order to open the overflow in the app/wrench menu).
bool allow_extension_dragging_;
// Used to spawn weak pointers for delayed tasks to open the overflow menu.
base::WeakPtrFactory<WrenchToolbarButton> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(WrenchToolbarButton); DISALLOW_COPY_AND_ASSIGN(WrenchToolbarButton);
}; };
......
...@@ -143,6 +143,7 @@ ...@@ -143,6 +143,7 @@
'browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h', 'browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h',
'browser/ui/views/tabs/tab_drag_controller_interactive_uitest_win.cc', 'browser/ui/views/tabs/tab_drag_controller_interactive_uitest_win.cc',
'browser/ui/views/toolbar/toolbar_button_test.cc', 'browser/ui/views/toolbar/toolbar_button_test.cc',
'browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc',
'test/base/interactive_test_utils.cc', 'test/base/interactive_test_utils.cc',
'test/base/interactive_test_utils.h', 'test/base/interactive_test_utils.h',
'test/base/interactive_test_utils_aura.cc', 'test/base/interactive_test_utils_aura.cc',
......
...@@ -290,6 +290,7 @@ MenuItemView* MenuController::Run(Widget* parent, ...@@ -290,6 +290,7 @@ MenuItemView* MenuController::Run(Widget* parent,
const gfx::Rect& bounds, const gfx::Rect& bounds,
MenuAnchorPosition position, MenuAnchorPosition position,
bool context_menu, bool context_menu,
bool is_nested_drag,
int* result_event_flags) { int* result_event_flags) {
exit_type_ = EXIT_NONE; exit_type_ = EXIT_NONE;
possible_drag_ = false; possible_drag_ = false;
...@@ -348,9 +349,11 @@ MenuItemView* MenuController::Run(Widget* parent, ...@@ -348,9 +349,11 @@ MenuItemView* MenuController::Run(Widget* parent,
SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY); SetSelection(root, SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
if (!blocking_run_) { if (!blocking_run_) {
// Start the timer to hide the menu. This is needed as we get no if (!is_nested_drag) {
// notification when the drag has finished. // Start the timer to hide the menu. This is needed as we get no
StartCancelAllTimer(); // notification when the drag has finished.
StartCancelAllTimer();
}
return NULL; return NULL;
} }
...@@ -815,6 +818,10 @@ void MenuController::OnWidgetDestroying(Widget* widget) { ...@@ -815,6 +818,10 @@ void MenuController::OnWidgetDestroying(Widget* widget) {
message_loop_->ClearOwner(); message_loop_->ClearOwner();
} }
bool MenuController::IsCancelAllTimerRunningForTest() {
return cancel_all_timer_.IsRunning();
}
// static // static
void MenuController::TurnOffMenuSelectionHoldForTest() { void MenuController::TurnOffMenuSelectionHoldForTest() {
menu_selection_hold_time_ms = -1; menu_selection_hold_time_ms = -1;
......
...@@ -84,6 +84,7 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { ...@@ -84,6 +84,7 @@ class VIEWS_EXPORT MenuController : public WidgetObserver {
const gfx::Rect& bounds, const gfx::Rect& bounds,
MenuAnchorPosition position, MenuAnchorPosition position,
bool context_menu, bool context_menu,
bool is_nested_drag,
int* event_flags); int* event_flags);
// Whether or not Run blocks. // Whether or not Run blocks.
...@@ -161,6 +162,9 @@ class VIEWS_EXPORT MenuController : public WidgetObserver { ...@@ -161,6 +162,9 @@ class VIEWS_EXPORT MenuController : public WidgetObserver {
// WidgetObserver overrides: // WidgetObserver overrides:
virtual void OnWidgetDestroying(Widget* widget) OVERRIDE; virtual void OnWidgetDestroying(Widget* widget) OVERRIDE;
// Only used for testing.
bool IsCancelAllTimerRunningForTest();
// Only used for testing. // Only used for testing.
static void TurnOffMenuSelectionHoldForTest(); static void TurnOffMenuSelectionHoldForTest();
......
...@@ -82,6 +82,12 @@ class VIEWS_EXPORT MenuRunner { ...@@ -82,6 +82,12 @@ class VIEWS_EXPORT MenuRunner {
// The menu should behave like a Windows native Combobox dropdow menu. // The menu should behave like a Windows native Combobox dropdow menu.
// This behavior includes accepting the pending item and closing on F4. // This behavior includes accepting the pending item and closing on F4.
COMBOBOX = 1 << 4, COMBOBOX = 1 << 4,
// A child view is performing a drag-and-drop operation, so the menu should
// stay open (even if it doesn't receive drag updated events). In this case,
// the caller is responsible for closing the menu upon completion of the
// drag-and-drop.
NESTED_DRAG = 1 << 5,
}; };
enum RunResult { enum RunResult {
......
...@@ -105,8 +105,7 @@ MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(Widget* parent, ...@@ -105,8 +105,7 @@ MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(Widget* parent,
running_ = true; running_ = true;
for_drop_ = (run_types & MenuRunner::FOR_DROP) != 0; for_drop_ = (run_types & MenuRunner::FOR_DROP) != 0;
bool has_mnemonics = bool has_mnemonics = (run_types & MenuRunner::HAS_MNEMONICS) != 0;
(run_types & MenuRunner::HAS_MNEMONICS) != 0 && !for_drop_;
owns_controller_ = false; owns_controller_ = false;
if (!controller) { if (!controller) {
// No menus are showing, show one. // No menus are showing, show one.
...@@ -131,6 +130,7 @@ MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(Widget* parent, ...@@ -131,6 +130,7 @@ MenuRunner::RunResult MenuRunnerImpl::RunMenuAt(Widget* parent,
bounds, bounds,
anchor, anchor,
(run_types & MenuRunner::CONTEXT_MENU) != 0, (run_types & MenuRunner::CONTEXT_MENU) != 0,
(run_types & MenuRunner::NESTED_DRAG) != 0,
&mouse_event_flags); &mouse_event_flags);
// Get the time of the event which closed this menu. // Get the time of the event which closed this menu.
closing_event_time_ = controller->closing_event_time(); closing_event_time_ = controller->closing_event_time();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment