blob: de143fe045538bb30cce0e8ff0205fac8b4c2b0c [file] [log] [blame]
// Copyright 2019 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/ui/views/extensions/extensions_menu_button.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_view.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/view_class_properties.h"
const char ExtensionsMenuButton::kClassName[] = "ExtensionsMenuButton";
namespace {
constexpr int EXTENSION_CONTEXT_MENU = 13;
} // namespace
ExtensionsMenuButton::ExtensionsMenuButton(
Browser* browser,
std::unique_ptr<ToolbarActionViewController> controller)
: HoverButton(this,
ExtensionsMenuView::CreateFixedSizeIconView(),
base::string16(),
base::string16(),
std::make_unique<views::View>(),
true,
true),
browser_(browser),
controller_(std::move(controller)) {
ConfigureSecondaryView();
set_auto_compute_tooltip(false);
set_context_menu_controller(this);
controller_->SetDelegate(this);
UpdateState();
}
ExtensionsMenuButton::~ExtensionsMenuButton() = default;
const char* ExtensionsMenuButton::GetClassName() const {
return kClassName;
}
void ExtensionsMenuButton::ButtonPressed(Button* sender,
const ui::Event& event) {
if (sender->id() == EXTENSION_CONTEXT_MENU) {
ShowContextMenu(gfx::Point(), ui::MENU_SOURCE_MOUSE);
return;
}
DCHECK_EQ(this, sender);
controller_->ExecuteAction(true);
}
// ToolbarActionViewDelegateViews:
views::View* ExtensionsMenuButton::GetAsView() {
return this;
}
views::FocusManager* ExtensionsMenuButton::GetFocusManagerForAccelerator() {
return GetFocusManager();
}
views::View* ExtensionsMenuButton::GetReferenceViewForPopup() {
return BrowserView::GetBrowserViewForBrowser(browser_)
->toolbar()
->extensions_button();
}
content::WebContents* ExtensionsMenuButton::GetCurrentWebContents() const {
return browser_->tab_strip_model()->GetActiveWebContents();
}
void ExtensionsMenuButton::UpdateState() {
DCHECK_EQ(views::ImageView::kViewClassName, icon_view()->GetClassName());
static_cast<views::ImageView*>(icon_view())
->SetImage(controller_
->GetIcon(GetCurrentWebContents(),
icon_view()->GetPreferredSize())
.AsImageSkia());
SetTitleTextWithHintRange(controller_->GetActionName(),
gfx::Range::InvalidRange());
SetTooltipText(controller_->GetTooltip(GetCurrentWebContents()));
}
bool ExtensionsMenuButton::IsMenuRunning() const {
return menu_runner_ && menu_runner_->IsRunning();
}
// views::ContextMenuController:
void ExtensionsMenuButton::ShowContextMenuForViewImpl(
views::View* source,
const gfx::Point& point,
ui::MenuSourceType source_type) {
ui::MenuModel* model = controller_->GetContextMenu();
if (!model)
return;
// Unretained() is safe here as ExtensionsMenuButton will always outlive the
// menu. Any action that would lead to the deletion of |this| first triggers
// the closing of the menu through lost capture.
menu_adapter_ = std::make_unique<views::MenuModelAdapter>(
model, base::BindRepeating(&ExtensionsMenuButton::OnMenuClosed,
base::Unretained(this)));
menu_runner_ = std::make_unique<views::MenuRunner>(
model, views::MenuRunner::HAS_MNEMONICS);
menu_runner_->RunMenuAt(GetWidget(), context_menu_button_,
context_menu_button_->GetAnchorBoundsInScreen(),
views::MenuAnchorPosition::kTopRight, source_type);
}
void ExtensionsMenuButton::OnMenuClosed() {
menu_runner_.reset();
controller_->OnContextMenuClosed();
menu_adapter_.reset();
}
void ExtensionsMenuButton::ConfigureSecondaryView() {
views::View* container = secondary_view();
DCHECK(container->children().empty());
container->SetLayoutManager(
std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal));
const SkColor icon_color =
ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled()
? gfx::kGoogleGrey500
: gfx::kChromeIconGrey;
auto context_menu_button =
std::make_unique<views::MenuButton>(base::string16(), this);
context_menu_button->set_id(EXTENSION_CONTEXT_MENU);
context_menu_button->SetTooltipText(
l10n_util::GetStringUTF16(IDS_EXTENSIONS_MENU_CONTEXT_MENU_TOOLTIP));
context_menu_button->SetImage(
views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(kBrowserToolsIcon, 16, icon_color));
context_menu_button->set_ink_drop_base_color(icon_color);
context_menu_button->SetBorder(
views::CreateEmptyBorder(views::LayoutProvider::Get()->GetInsetsMetric(
views::INSETS_VECTOR_IMAGE_BUTTON)));
context_menu_button->SizeToPreferredSize();
context_menu_button->SetInkDropMode(InkDropMode::ON);
context_menu_button->set_has_ink_drop_action_on_click(true);
auto highlight_path = std::make_unique<SkPath>();
highlight_path->addOval(
gfx::RectToSkRect(gfx::Rect(context_menu_button->size())));
context_menu_button->SetProperty(views::kHighlightPathKey,
highlight_path.release());
context_menu_button_ = context_menu_button.get();
container->AddChildView(std::move(context_menu_button));
}