blob: 441929a43a160890254e0bafde89270ef0655c27 [file] [log] [blame]
// 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 "chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/ui/views/frame/app_menu_button.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/toolbar/app_menu.h"
#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/view_properties.h"
namespace {
// The delay before we close the app menu if this was opened for a drop so that
// the user can see a browser action if one was moved.
// This can be changed for tests.
int g_close_menu_delay = 300;
}
ExtensionToolbarMenuView::ExtensionToolbarMenuView(
Browser* browser,
views::MenuItemView* menu_item)
: browser_(browser), menu_item_(menu_item) {
auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
auto* toolbar_button_provider = browser_view->toolbar_button_provider();
auto* app_menu_button = toolbar_button_provider->GetAppMenuButton();
app_menu_ = app_menu_button->app_menu();
// Use a transparent background so that the menu's background shows through.
// None of the children use layers, so this should be ok.
SetBackgroundColor(SK_ColorTRANSPARENT);
BrowserActionsContainer* main =
toolbar_button_provider->GetBrowserActionsContainer();
container_ = new BrowserActionsContainer(browser_, main, main->delegate());
SetContents(container_);
// Listen for the drop to finish so we can close the app menu, if necessary.
toolbar_actions_bar_observer_.Add(main->toolbar_actions_bar());
// Observe app menu so we know when RunMenu() is called.
app_menu_button_observer_.Add(app_menu_button);
// In *very* extreme cases, it's possible that there are so many overflowed
// actions, we won't be able to show them all. Cap the height so that the
// overflow won't be excessively tall (at 8 icons per row, this allows for
// 104 total extensions).
constexpr int kMaxOverflowRows = 13;
max_height_ = container_->GetToolbarActionSize().height() * kMaxOverflowRows;
ClipHeightTo(0, max_height_);
}
ExtensionToolbarMenuView::~ExtensionToolbarMenuView() {
}
gfx::Size ExtensionToolbarMenuView::CalculatePreferredSize() const {
gfx::Size s = views::ScrollView::CalculatePreferredSize();
// views::ScrollView::CalculatePreferredSize() includes the contents' size,
// but not the scrollbar width. Add it in if necessary.
if (container_->GetPreferredSize().height() > max_height_)
s.Enlarge(GetScrollBarLayoutWidth(), 0);
return s;
}
int ExtensionToolbarMenuView::GetHeightForWidth(int width) const {
// The width passed in here includes the full width of the menu, so we need
// to omit the necessary padding.
const views::MenuConfig& menu_config = views::MenuConfig::instance();
int end_padding = menu_config.arrow_to_edge_padding -
container_->toolbar_actions_bar()->platform_settings().item_spacing;
width -= start_padding() + end_padding;
return views::ScrollView::GetHeightForWidth(width);
}
void ExtensionToolbarMenuView::OnBoundsChanged(
const gfx::Rect& previous_bounds) {
menu_item_->GetParentMenuItem()->ChildrenChanged();
}
void ExtensionToolbarMenuView::set_close_menu_delay_for_testing(int delay) {
g_close_menu_delay = delay;
}
void ExtensionToolbarMenuView::OnToolbarActionsBarDestroyed() {
toolbar_actions_bar_observer_.RemoveAll();
}
void ExtensionToolbarMenuView::OnToolbarActionDragDone() {
// In the case of a drag-and-drop, the bounds of the container may have
// changed (in the case of removing an icon that was the last in a row).
UpdateMargins();
// We need to close the app menu if it was just opened for the drag and drop,
// or if there are no more extensions in the overflow menu after a drag and
// drop.
if (app_menu_->for_drop() ||
container_->toolbar_actions_bar()->GetIconCount() == 0) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExtensionToolbarMenuView::CloseAppMenu,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(g_close_menu_delay));
}
}
void ExtensionToolbarMenuView::AppMenuShown() {
// Set the margins and flag for re-layout. This must be done here since
// start_padding() depends on views::MenuItemView::label_start() which is
// initialized upon menu running.
//
// TODO(crbug.com/918741): fix MenuItemView so MenuItemView::label_start()
// returns valid data before menu run time.
UpdateMargins();
}
void ExtensionToolbarMenuView::CloseAppMenu() {
app_menu_->CloseMenu();
}
void ExtensionToolbarMenuView::UpdateMargins() {
SetProperty(views::kMarginsKey, new gfx::Insets(0, start_padding()));
menu_item_->Layout();
}
int ExtensionToolbarMenuView::start_padding() const {
// We pad enough on the left so that the first icon starts at the same point
// as the labels. We subtract kItemSpacing because there needs to be padding
// so we can see the drop indicator.
return views::MenuItemView::label_start() -
container_->toolbar_actions_bar()->platform_settings().item_spacing;
}