PageRenderTime 1875ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/chrome/browser/ui/views/toolbar/toolbar_action_view.cc

https://gitlab.com/0072016/Facebook-SDK-
C++ | 335 lines | 237 code | 55 blank | 43 comment | 24 complexity | 814fd894117170d86a1d4526c5dca8ea MD5 | raw file
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
  5. #include <string>
  6. #include "base/auto_reset.h"
  7. #include "base/bind.h"
  8. #include "chrome/browser/chrome_notification_types.h"
  9. #include "chrome/browser/sessions/session_tab_helper.h"
  10. #include "chrome/browser/themes/theme_properties.h"
  11. #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
  12. #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
  13. #include "chrome/browser/ui/view_ids.h"
  14. #include "chrome/grit/generated_resources.h"
  15. #include "content/public/browser/notification_source.h"
  16. #include "grit/theme_resources.h"
  17. #include "ui/accessibility/ax_view_state.h"
  18. #include "ui/base/resource/resource_bundle.h"
  19. #include "ui/base/theme_provider.h"
  20. #include "ui/compositor/paint_recorder.h"
  21. #include "ui/events/event.h"
  22. #include "ui/gfx/image/image_skia.h"
  23. #include "ui/gfx/image/image_skia_operations.h"
  24. #include "ui/gfx/image/image_skia_source.h"
  25. #include "ui/resources/grit/ui_resources.h"
  26. #include "ui/views/animation/button_ink_drop_delegate.h"
  27. #include "ui/views/controls/button/label_button_border.h"
  28. #include "ui/views/controls/menu/menu_controller.h"
  29. #include "ui/views/controls/menu/menu_model_adapter.h"
  30. #include "ui/views/controls/menu/menu_runner.h"
  31. #include "ui/views/mouse_constants.h"
  32. #include "ui/views/resources/grit/views_resources.h"
  33. using views::LabelButtonBorder;
  34. namespace {
  35. // Toolbar action buttons have no insets because the badges are drawn right at
  36. // the edge of the view's area. Other badding (such as centering the icon) is
  37. // handled directly by the Image.
  38. const int kBorderInset = 0;
  39. // The callback to call directly before showing the context menu.
  40. ToolbarActionView::ContextMenuCallback* context_menu_callback_for_test =
  41. nullptr;
  42. } // namespace
  43. ////////////////////////////////////////////////////////////////////////////////
  44. // ToolbarActionView
  45. ToolbarActionView::ToolbarActionView(
  46. ToolbarActionViewController* view_controller,
  47. ToolbarActionView::Delegate* delegate)
  48. : MenuButton(base::string16(), this, false),
  49. view_controller_(view_controller),
  50. delegate_(delegate),
  51. called_register_command_(false),
  52. wants_to_run_(false),
  53. menu_(nullptr),
  54. ink_drop_delegate_(new views::ButtonInkDropDelegate(this, this)),
  55. weak_factory_(this) {
  56. set_ink_drop_delegate(ink_drop_delegate_.get());
  57. set_has_ink_drop_action_on_click(true);
  58. set_id(VIEW_ID_BROWSER_ACTION);
  59. view_controller_->SetDelegate(this);
  60. SetHorizontalAlignment(gfx::ALIGN_CENTER);
  61. set_drag_controller(delegate_);
  62. set_context_menu_controller(this);
  63. // If the button is within a menu, we need to make it focusable in order to
  64. // have it accessible via keyboard navigation, but it shouldn't request focus
  65. // (because that would close the menu).
  66. if (delegate_->ShownInsideMenu()) {
  67. set_request_focus_on_press(false);
  68. SetFocusable(true);
  69. }
  70. UpdateState();
  71. }
  72. ToolbarActionView::~ToolbarActionView() {
  73. // Avoid access to a destroyed InkDropDelegate when the |pressed_lock_| is
  74. // destroyed.
  75. set_ink_drop_delegate(nullptr);
  76. view_controller_->SetDelegate(nullptr);
  77. }
  78. void ToolbarActionView::GetAccessibleState(ui::AXViewState* state) {
  79. views::MenuButton::GetAccessibleState(state);
  80. state->role = ui::AX_ROLE_BUTTON;
  81. }
  82. std::unique_ptr<LabelButtonBorder> ToolbarActionView::CreateDefaultBorder()
  83. const {
  84. std::unique_ptr<LabelButtonBorder> border =
  85. LabelButton::CreateDefaultBorder();
  86. border->set_insets(gfx::Insets(kBorderInset, kBorderInset,
  87. kBorderInset, kBorderInset));
  88. return border;
  89. }
  90. bool ToolbarActionView::IsTriggerableEvent(const ui::Event& event) {
  91. return views::MenuButton::IsTriggerableEvent(event) &&
  92. (base::TimeTicks::Now() - popup_closed_time_).InMilliseconds() >
  93. views::kMinimumMsBetweenButtonClicks;
  94. }
  95. SkColor ToolbarActionView::GetInkDropBaseColor() const {
  96. if (delegate_->ShownInsideMenu()) {
  97. return GetNativeTheme()->GetSystemColor(
  98. ui::NativeTheme::kColorId_HoverMenuItemBackgroundColor);
  99. }
  100. return GetThemeProvider()->GetColor(
  101. ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
  102. }
  103. bool ToolbarActionView::ShouldShowInkDropHover() const {
  104. return !delegate_->ShownInsideMenu() &&
  105. views::MenuButton::ShouldShowInkDropHover();
  106. }
  107. content::WebContents* ToolbarActionView::GetCurrentWebContents() const {
  108. return delegate_->GetCurrentWebContents();
  109. }
  110. void ToolbarActionView::UpdateState() {
  111. content::WebContents* web_contents = GetCurrentWebContents();
  112. if (SessionTabHelper::IdForTab(web_contents) < 0)
  113. return;
  114. if (!view_controller_->IsEnabled(web_contents) &&
  115. !view_controller_->DisabledClickOpensMenu()) {
  116. SetState(views::CustomButton::STATE_DISABLED);
  117. } else if (state() == views::CustomButton::STATE_DISABLED) {
  118. SetState(views::CustomButton::STATE_NORMAL);
  119. }
  120. wants_to_run_ = view_controller_->WantsToRun(web_contents);
  121. gfx::ImageSkia icon(
  122. view_controller_->GetIcon(web_contents,
  123. GetPreferredSize()).AsImageSkia());
  124. if (!icon.isNull())
  125. SetImage(views::Button::STATE_NORMAL, icon);
  126. SetTooltipText(view_controller_->GetTooltip(web_contents));
  127. SetAccessibleName(view_controller_->GetAccessibleName(web_contents));
  128. Layout(); // We need to layout since we may have added an icon as a result.
  129. SchedulePaint();
  130. }
  131. void ToolbarActionView::OnMenuButtonClicked(views::MenuButton* sender,
  132. const gfx::Point& point,
  133. const ui::Event* event) {
  134. if (!view_controller_->IsEnabled(GetCurrentWebContents())) {
  135. // We should only get a button pressed event with a non-enabled action if
  136. // the left-click behavior should open the menu.
  137. DCHECK(view_controller_->DisabledClickOpensMenu());
  138. context_menu_controller()->ShowContextMenuForView(this, point,
  139. ui::MENU_SOURCE_NONE);
  140. } else {
  141. view_controller_->ExecuteAction(true);
  142. }
  143. }
  144. void ToolbarActionView::OnMenuClosed() {
  145. menu_runner_.reset();
  146. menu_ = nullptr;
  147. view_controller_->OnContextMenuClosed();
  148. menu_adapter_.reset();
  149. }
  150. gfx::ImageSkia ToolbarActionView::GetIconForTest() {
  151. return GetImage(views::Button::STATE_NORMAL);
  152. }
  153. void ToolbarActionView::set_context_menu_callback_for_testing(
  154. base::Callback<void(ToolbarActionView*)>* callback) {
  155. context_menu_callback_for_test = callback;
  156. }
  157. gfx::Size ToolbarActionView::GetPreferredSize() const {
  158. return gfx::Size(ToolbarActionsBar::IconWidth(false),
  159. ToolbarActionsBar::IconHeight());
  160. }
  161. bool ToolbarActionView::OnMousePressed(const ui::MouseEvent& event) {
  162. // views::MenuButton actions are only triggered by left mouse clicks.
  163. if (event.IsOnlyLeftMouseButton() && !pressed_lock_) {
  164. // TODO(bruthig): The ACTION_PENDING triggering logic should be in
  165. // MenuButton::OnPressed() however there is a bug with the pressed state
  166. // logic in MenuButton. See http://crbug.com/567252.
  167. ink_drop_delegate()->OnAction(views::InkDropState::ACTION_PENDING);
  168. }
  169. return MenuButton::OnMousePressed(event);
  170. }
  171. void ToolbarActionView::OnGestureEvent(ui::GestureEvent* event) {
  172. // While the dropdown menu is showing, the button should not handle gestures.
  173. if (menu_)
  174. event->StopPropagation();
  175. else
  176. MenuButton::OnGestureEvent(event);
  177. }
  178. void ToolbarActionView::OnDragDone() {
  179. views::MenuButton::OnDragDone();
  180. delegate_->OnToolbarActionViewDragDone();
  181. }
  182. void ToolbarActionView::ViewHierarchyChanged(
  183. const ViewHierarchyChangedDetails& details) {
  184. if (details.is_add && !called_register_command_ && GetFocusManager()) {
  185. view_controller_->RegisterCommand();
  186. called_register_command_ = true;
  187. }
  188. MenuButton::ViewHierarchyChanged(details);
  189. }
  190. views::View* ToolbarActionView::GetAsView() {
  191. return this;
  192. }
  193. views::FocusManager* ToolbarActionView::GetFocusManagerForAccelerator() {
  194. return GetFocusManager();
  195. }
  196. views::View* ToolbarActionView::GetReferenceViewForPopup() {
  197. // Browser actions in the overflow menu can still show popups, so we may need
  198. // a reference view other than this button's parent. If so, use the overflow
  199. // view.
  200. return visible() ? this : delegate_->GetOverflowReferenceView();
  201. }
  202. bool ToolbarActionView::IsMenuRunning() const {
  203. return menu_ != nullptr;
  204. }
  205. void ToolbarActionView::OnPopupShown(bool by_user) {
  206. // If this was through direct user action, we press the menu button.
  207. if (by_user) {
  208. // We set the state of the menu button we're using as a reference view,
  209. // which is either this or the overflow reference view.
  210. // This cast is safe because GetReferenceViewForPopup returns either |this|
  211. // or delegate_->GetOverflowReferenceView(), which returns a MenuButton.
  212. views::MenuButton* reference_view =
  213. static_cast<views::MenuButton*>(GetReferenceViewForPopup());
  214. pressed_lock_.reset(new views::MenuButton::PressedLock(reference_view));
  215. }
  216. }
  217. void ToolbarActionView::OnPopupClosed() {
  218. popup_closed_time_ = base::TimeTicks::Now();
  219. pressed_lock_.reset(); // Unpress the menu button if it was pressed.
  220. }
  221. void ToolbarActionView::ShowContextMenuForView(
  222. views::View* source,
  223. const gfx::Point& point,
  224. ui::MenuSourceType source_type) {
  225. if (CloseActiveMenuIfNeeded())
  226. return;
  227. // Otherwise, no other menu is showing, and we can proceed normally.
  228. DoShowContextMenu(source_type);
  229. }
  230. void ToolbarActionView::DoShowContextMenu(
  231. ui::MenuSourceType source_type) {
  232. ui::MenuModel* context_menu_model = view_controller_->GetContextMenu();
  233. // It's possible the action doesn't have a context menu.
  234. if (!context_menu_model)
  235. return;
  236. DCHECK(visible()); // We should never show a context menu for a hidden item.
  237. gfx::Point screen_loc;
  238. ConvertPointToScreen(this, &screen_loc);
  239. int run_types = views::MenuRunner::HAS_MNEMONICS |
  240. views::MenuRunner::CONTEXT_MENU | views::MenuRunner::ASYNC;
  241. if (delegate_->ShownInsideMenu())
  242. run_types |= views::MenuRunner::IS_NESTED;
  243. // RunMenuAt expects a nested menu to be parented by the same widget as the
  244. // already visible menu, in this case the Chrome menu.
  245. views::Widget* parent = delegate_->ShownInsideMenu() ?
  246. delegate_->GetOverflowReferenceView()->GetWidget() :
  247. GetWidget();
  248. // Unretained() is safe here as ToolbarActionView will always outlive the
  249. // menu. Any action that would lead to the deletion of |this| first triggers
  250. // the closing of the menu through lost capture.
  251. menu_adapter_.reset(new views::MenuModelAdapter(
  252. context_menu_model,
  253. base::Bind(&ToolbarActionView::OnMenuClosed, base::Unretained(this))));
  254. menu_ = menu_adapter_->CreateMenu();
  255. menu_runner_.reset(new views::MenuRunner(menu_, run_types));
  256. if (context_menu_callback_for_test)
  257. context_menu_callback_for_test->Run(this);
  258. ignore_result(
  259. menu_runner_->RunMenuAt(parent, this, gfx::Rect(screen_loc, size()),
  260. views::MENU_ANCHOR_TOPLEFT, source_type));
  261. }
  262. bool ToolbarActionView::CloseActiveMenuIfNeeded() {
  263. // If this view is shown inside another menu, there's a possibility that there
  264. // is another context menu showing that we have to close before we can
  265. // activate a different menu.
  266. if (delegate_->ShownInsideMenu()) {
  267. views::MenuController* menu_controller =
  268. views::MenuController::GetActiveInstance();
  269. // If this is shown inside a menu, then there should always be an active
  270. // menu controller.
  271. DCHECK(menu_controller);
  272. if (menu_controller->in_nested_run()) {
  273. // There is another menu showing. Close the outermost menu (since we are
  274. // shown in the same menu, we don't want to close the whole thing).
  275. menu_controller->Cancel(views::MenuController::EXIT_OUTERMOST);
  276. return true;
  277. }
  278. }
  279. return false;
  280. }