Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(306)

Side by Side Diff: chrome/browser/ui/views/toolbar/chevron_menu_button.cc

Issue 1971463002: Get rid of some lingering MD-specific raster assets. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix another test Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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
5 #include "chrome/browser/ui/views/toolbar/chevron_menu_button.h"
6
7 #include <stddef.h>
8
9 #include "base/location.h"
10 #include "base/macros.h"
11 #include "base/memory/scoped_vector.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "chrome/browser/extensions/extension_action.h"
16 #include "chrome/browser/extensions/extension_action_icon_factory.h"
17 #include "chrome/browser/extensions/extension_context_menu_model.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/extensions/extension_action_view_controller.h"
21 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
22 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
23 #include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
24 #include "extensions/common/extension.h"
25 #include "ui/views/border.h"
26 #include "ui/views/controls/button/label_button_border.h"
27 #include "ui/views/controls/menu/menu_delegate.h"
28 #include "ui/views/controls/menu/menu_item_view.h"
29 #include "ui/views/controls/menu/menu_runner.h"
30 #include "ui/views/metrics.h"
31
32 namespace {
33
34 // In the browser actions container's chevron menu, a menu item view's icon
35 // comes from ToolbarActionView::GetIconWithBadge() when the menu item view is
36 // created. But, the browser action's icon may not be loaded in time because it
37 // is read from file system in another thread.
38 // The IconUpdater will update the menu item view's icon when the browser
39 // action's icon has been updated.
40 class IconUpdater : public ExtensionActionIconFactory::Observer {
41 public:
42 IconUpdater(views::MenuItemView* menu_item_view,
43 ToolbarActionView* represented_view)
44 : menu_item_view_(menu_item_view),
45 represented_view_(represented_view) {
46 DCHECK(menu_item_view);
47 DCHECK(represented_view);
48 view_controller()->set_icon_observer(this);
Devlin 2016/05/11 00:01:02 I think ExtensionActionViewController::set_icon_ob
Evan Stade 2016/05/11 21:05:35 Done.
49 }
50 ~IconUpdater() override { view_controller()->set_icon_observer(nullptr); }
51
52 // ExtensionActionIconFactory::Observer:
53 void OnIconUpdated() override {
54 menu_item_view_->SetIcon(
55 represented_view_->GetImage(views::Button::STATE_NORMAL));
56 }
57
58 private:
59 ExtensionActionViewController* view_controller() {
60 // Since the chevron overflow menu is only used in a world where toolbar
61 // actions are only extensions, this cast is safe.
62 return static_cast<ExtensionActionViewController*>(
63 represented_view_->view_controller());
64 }
65
66 // The menu item view whose icon might be updated.
67 views::MenuItemView* menu_item_view_;
68
69 // The view this icon updater is helping represent in the chevron overflow
70 // menu. When its icon changes, this updates the corresponding menu item
71 // view's icon.
72 ToolbarActionView* represented_view_;
73
74 DISALLOW_COPY_AND_ASSIGN(IconUpdater);
75 };
76
77 } // namespace
78
79 // This class handles the overflow menu for browser actions.
80 class ChevronMenuButton::MenuController : public views::MenuDelegate {
81 public:
82 MenuController(ChevronMenuButton* owner,
83 BrowserActionsContainer* browser_actions_container,
84 bool for_drop);
85 ~MenuController() override;
86
87 // Shows the overflow menu.
88 void RunMenu(views::Widget* widget);
89
90 // Closes the overflow menu (and its context menu if open as well).
91 void CloseMenu();
92
93 private:
94 // views::MenuDelegate:
95 bool IsCommandEnabled(int id) const override;
96 void ExecuteCommand(int id) override;
97 bool ShowContextMenu(views::MenuItemView* source,
98 int id,
99 const gfx::Point& p,
100 ui::MenuSourceType source_type) override;
101 // These drag functions offer support for dragging icons into the overflow
102 // menu.
103 bool GetDropFormats(
104 views::MenuItemView* menu,
105 int* formats,
106 std::set<ui::Clipboard::FormatType>* format_types) override;
107 bool AreDropTypesRequired(views::MenuItemView* menu) override;
108 bool CanDrop(views::MenuItemView* menu,
109 const ui::OSExchangeData& data) override;
110 int GetDropOperation(views::MenuItemView* item,
111 const ui::DropTargetEvent& event,
112 DropPosition* position) override;
113 int OnPerformDrop(views::MenuItemView* menu,
114 DropPosition position,
115 const ui::DropTargetEvent& event) override;
116 void OnMenuClosed(views::MenuItemView* menu,
117 views::MenuRunner::RunResult result) override;
118 // These three drag functions offer support for dragging icons out of the
119 // overflow menu.
120 bool CanDrag(views::MenuItemView* menu) override;
121 void WriteDragData(views::MenuItemView* sender,
122 ui::OSExchangeData* data) override;
123 int GetDragOperations(views::MenuItemView* sender) override;
124
125 // Returns the offset into |views_| for the given |id|.
126 size_t IndexForId(int id) const;
127
128 // The owning ChevronMenuButton.
129 ChevronMenuButton* owner_;
130
131 // A pointer to the browser action container.
132 BrowserActionsContainer* browser_actions_container_;
133
134 // The overflow menu for the menu button. Owned by |menu_runner_|.
135 views::MenuItemView* menu_;
136
137 // Resposible for running the menu.
138 std::unique_ptr<views::MenuRunner> menu_runner_;
139
140 // The index into the ToolbarActionView vector, indicating where to start
141 // picking browser actions to draw.
142 int start_index_;
143
144 // Whether this controller is being used for drop.
145 bool for_drop_;
146
147 // The vector keeps all icon updaters associated with menu item views in the
148 // controller. The icon updater will update the menu item view's icon when
149 // the browser action view's icon has been updated.
150 ScopedVector<IconUpdater> icon_updaters_;
151
152 DISALLOW_COPY_AND_ASSIGN(MenuController);
153 };
154
155 ChevronMenuButton::MenuController::MenuController(
156 ChevronMenuButton* owner,
157 BrowserActionsContainer* browser_actions_container,
158 bool for_drop)
159 : owner_(owner),
160 browser_actions_container_(browser_actions_container),
161 menu_(NULL),
162 start_index_(
163 browser_actions_container_->VisibleBrowserActionsAfterAnimation()),
164 for_drop_(for_drop) {
165 menu_ = new views::MenuItemView(this);
166 menu_runner_.reset(new views::MenuRunner(
167 menu_, for_drop_ ? views::MenuRunner::FOR_DROP : 0));
168 menu_->set_has_icons(true);
169
170 size_t command_id = 1; // Menu id 0 is reserved, start with 1.
171 for (size_t i = start_index_;
172 i < browser_actions_container_->num_toolbar_actions(); ++i) {
173 ToolbarActionView* view =
174 browser_actions_container_->GetToolbarActionViewAt(i);
175 views::MenuItemView* menu_item = menu_->AppendMenuItemWithIcon(
176 command_id,
177 view->view_controller()->GetActionName(),
178 view->GetImage(views::Button::STATE_NORMAL));
179
180 // Set the tooltip for this item.
181 menu_->SetTooltip(
182 view->view_controller()->GetTooltip(view->GetCurrentWebContents()),
183 command_id);
184
185 icon_updaters_.push_back(new IconUpdater(menu_item, view));
186
187 ++command_id;
188 }
189 }
190
191 ChevronMenuButton::MenuController::~MenuController() {
192 }
193
194 void ChevronMenuButton::MenuController::RunMenu(views::Widget* window) {
195 gfx::Rect bounds = owner_->bounds();
196 gfx::Point screen_loc;
197 views::View::ConvertPointToScreen(owner_, &screen_loc);
198 bounds.set_x(screen_loc.x());
199 bounds.set_y(screen_loc.y());
200 ignore_result(menu_runner_->RunMenuAt(window, owner_, bounds,
201 views::MENU_ANCHOR_TOPRIGHT,
202 ui::MENU_SOURCE_NONE));
203 }
204
205 void ChevronMenuButton::MenuController::CloseMenu() {
206 icon_updaters_.clear();
207 menu_->Cancel();
208 }
209
210 bool ChevronMenuButton::MenuController::IsCommandEnabled(int id) const {
211 ToolbarActionView* view =
212 browser_actions_container_->GetToolbarActionViewAt(start_index_ + id - 1);
213 return view->view_controller()->IsEnabled(view->GetCurrentWebContents());
214 }
215
216 void ChevronMenuButton::MenuController::ExecuteCommand(int id) {
217 browser_actions_container_->GetToolbarActionViewAt(start_index_ + id - 1)->
218 view_controller()->ExecuteAction(true);
219 }
220
221 bool ChevronMenuButton::MenuController::ShowContextMenu(
222 views::MenuItemView* source,
223 int id,
224 const gfx::Point& p,
225 ui::MenuSourceType source_type) {
226 ToolbarActionView* view = browser_actions_container_->GetToolbarActionViewAt(
227 start_index_ + id - 1);
228 ExtensionActionViewController* view_controller =
229 static_cast<ExtensionActionViewController*>(view->view_controller());
230 if (!view_controller->extension()->ShowConfigureContextMenus())
231 return false;
232
233 std::unique_ptr<extensions::ExtensionContextMenuModel> context_menu_contents(
234 new extensions::ExtensionContextMenuModel(
235 view_controller->extension(), view_controller->browser(),
236 extensions::ExtensionContextMenuModel::OVERFLOWED, view_controller));
237 views::MenuRunner context_menu_runner(context_menu_contents.get(),
238 views::MenuRunner::HAS_MNEMONICS |
239 views::MenuRunner::IS_NESTED |
240 views::MenuRunner::CONTEXT_MENU);
241
242 // We can ignore the result as we delete ourself.
243 // This blocks until the user chooses something or dismisses the menu.
244 if (context_menu_runner.RunMenuAt(owner_->GetWidget(),
245 NULL,
246 gfx::Rect(p, gfx::Size()),
247 views::MENU_ANCHOR_TOPLEFT,
248 source_type) ==
249 views::MenuRunner::MENU_DELETED)
250 return true;
251
252 // The user is done with the context menu, so we can close the underlying
253 // menu.
254 menu_->Cancel();
255
256 return true;
257 }
258
259 bool ChevronMenuButton::MenuController::GetDropFormats(
260 views::MenuItemView* menu,
261 int* formats,
262 std::set<ui::Clipboard::FormatType>* format_types) {
263 return BrowserActionDragData::GetDropFormats(format_types);
264 }
265
266 bool ChevronMenuButton::MenuController::AreDropTypesRequired(
267 views::MenuItemView* menu) {
268 return BrowserActionDragData::AreDropTypesRequired();
269 }
270
271 bool ChevronMenuButton::MenuController::CanDrop(
272 views::MenuItemView* menu, const OSExchangeData& data) {
273 return BrowserActionDragData::CanDrop(
274 data, browser_actions_container_->browser()->profile());
275 }
276
277 int ChevronMenuButton::MenuController::GetDropOperation(
278 views::MenuItemView* item,
279 const ui::DropTargetEvent& event,
280 DropPosition* position) {
281 // Don't allow dropping from the BrowserActionContainer into slot 0 of the
282 // overflow menu since once the move has taken place the item you are dragging
283 // falls right out of the menu again once the user releases the button
284 // (because we don't shrink the BrowserActionContainer when you do this).
285 if ((item->GetCommand() == 0) && (*position == DROP_BEFORE)) {
286 BrowserActionDragData drop_data;
287 if (!drop_data.Read(event.data()))
288 return ui::DragDropTypes::DRAG_NONE;
289
290 if (drop_data.index() < browser_actions_container_->VisibleBrowserActions())
291 return ui::DragDropTypes::DRAG_NONE;
292 }
293
294 return ui::DragDropTypes::DRAG_MOVE;
295 }
296
297 int ChevronMenuButton::MenuController::OnPerformDrop(
298 views::MenuItemView* menu,
299 DropPosition position,
300 const ui::DropTargetEvent& event) {
301 BrowserActionDragData drop_data;
302 if (!drop_data.Read(event.data()))
303 return ui::DragDropTypes::DRAG_NONE;
304
305 size_t drop_index = IndexForId(menu->GetCommand());
306
307 // When not dragging within the overflow menu (dragging an icon into the menu)
308 // subtract one to get the right index.
309 if (position == DROP_BEFORE &&
310 drop_data.index() < browser_actions_container_->VisibleBrowserActions())
311 --drop_index;
312
313 ToolbarActionsBar::DragType drag_type =
314 drop_data.index() < browser_actions_container_->VisibleBrowserActions() ?
315 ToolbarActionsBar::DRAG_TO_OVERFLOW :
316 ToolbarActionsBar::DRAG_TO_SAME;
317 browser_actions_container_->toolbar_actions_bar()->OnDragDrop(
318 drop_data.index(), drop_index, drag_type);
319
320 if (for_drop_)
321 owner_->MenuDone();
322 return ui::DragDropTypes::DRAG_MOVE;
323 }
324
325 void ChevronMenuButton::MenuController::OnMenuClosed(
326 views::MenuItemView* menu,
327 views::MenuRunner::RunResult result) {
328 if (result == views::MenuRunner::MENU_DELETED)
329 return;
330
331 // Give the context menu (if any) a chance to execute the user-selected
332 // command.
333 base::ThreadTaskRunnerHandle::Get()->PostTask(
334 FROM_HERE, base::Bind(&ChevronMenuButton::MenuDone,
335 owner_->weak_factory_.GetWeakPtr()));
336 }
337
338 bool ChevronMenuButton::MenuController::CanDrag(views::MenuItemView* menu) {
339 return true;
340 }
341
342 void ChevronMenuButton::MenuController::WriteDragData(
343 views::MenuItemView* sender, OSExchangeData* data) {
344 size_t drag_index = IndexForId(sender->GetCommand());
345 BrowserActionDragData drag_data(
346 browser_actions_container_->GetIdAt(drag_index), drag_index);
347 drag_data.Write(browser_actions_container_->browser()->profile(), data);
348 }
349
350 int ChevronMenuButton::MenuController::GetDragOperations(
351 views::MenuItemView* sender) {
352 return ui::DragDropTypes::DRAG_MOVE;
353 }
354
355 size_t ChevronMenuButton::MenuController::IndexForId(int id) const {
356 // The index of the view being dragged (GetCommand gives a 1-based index into
357 // the overflow menu).
358 DCHECK_GT(browser_actions_container_->VisibleBrowserActions() + id, 0u);
359 return browser_actions_container_->VisibleBrowserActions() + id - 1;
360 }
361
362 ChevronMenuButton::ChevronMenuButton(
363 BrowserActionsContainer* browser_actions_container)
364 : views::MenuButton(base::string16(), this, false),
365 browser_actions_container_(browser_actions_container),
366 weak_factory_(this) {
367 // Set the border explicitly, because otherwise the native theme manager takes
368 // over and reassigns the insets we set in CreateDefaultBorder().
369 SetBorder(CreateDefaultBorder());
370 }
371
372 ChevronMenuButton::~ChevronMenuButton() {
373 }
374
375 void ChevronMenuButton::CloseMenu() {
376 if (menu_controller_)
377 menu_controller_->CloseMenu();
378 }
379
380 std::unique_ptr<views::LabelButtonBorder>
381 ChevronMenuButton::CreateDefaultBorder() const {
382 // The chevron resource was designed to not have any insets.
383 std::unique_ptr<views::LabelButtonBorder> border =
384 views::MenuButton::CreateDefaultBorder();
385 border->set_insets(gfx::Insets());
386 return border;
387 }
388
389 bool ChevronMenuButton::GetDropFormats(
390 int* formats,
391 std::set<ui::Clipboard::FormatType>* format_types) {
392 return BrowserActionDragData::GetDropFormats(format_types);
393 }
394
395 bool ChevronMenuButton::AreDropTypesRequired() {
396 return BrowserActionDragData::AreDropTypesRequired();
397 }
398
399 bool ChevronMenuButton::CanDrop(const OSExchangeData& data) {
400 return BrowserActionDragData::CanDrop(
401 data, browser_actions_container_->browser()->profile());
402 }
403
404 void ChevronMenuButton::OnDragEntered(const ui::DropTargetEvent& event) {
405 DCHECK(!weak_factory_.HasWeakPtrs());
406 if (!menu_controller_) {
407 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
408 FROM_HERE, base::Bind(&ChevronMenuButton::ShowOverflowMenu,
409 weak_factory_.GetWeakPtr(), true),
410 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
411 }
412 }
413
414 int ChevronMenuButton::OnDragUpdated(const ui::DropTargetEvent& event) {
415 return ui::DragDropTypes::DRAG_MOVE;
416 }
417
418 void ChevronMenuButton::OnDragExited() {
419 weak_factory_.InvalidateWeakPtrs();
420 }
421
422 int ChevronMenuButton::OnPerformDrop(const ui::DropTargetEvent& event) {
423 weak_factory_.InvalidateWeakPtrs();
424 return ui::DragDropTypes::DRAG_MOVE;
425 }
426
427 void ChevronMenuButton::OnMenuButtonClicked(views::MenuButton* source,
428 const gfx::Point& point,
429 const ui::Event* event) {
430 DCHECK_EQ(this, source);
431 // The menu could already be open if a user dragged an item over it but
432 // ultimately dropped elsewhere (as in that case the menu will close on a
433 // timer). In this case, the click should close the open menu.
434 if (menu_controller_)
435 menu_controller_->CloseMenu();
436 else
437 ShowOverflowMenu(false);
438 }
439
440 void ChevronMenuButton::ShowOverflowMenu(bool for_drop) {
441 // We should never try to show an overflow menu when one is already visible.
442 DCHECK(!menu_controller_);
443 menu_controller_.reset(new MenuController(
444 this, browser_actions_container_, for_drop));
445 menu_controller_->RunMenu(GetWidget());
446 }
447
448 void ChevronMenuButton::MenuDone() {
449 menu_controller_.reset();
450 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698