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

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

Issue 597783002: Make the chevron menu button responsible for legacy overflow menu logic (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Changed filenames Created 6 years, 2 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
« no previous file with comments | « chrome/browser/ui/views/toolbar/chevron_menu_button.h ('k') | chrome/chrome_browser_ui.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
Devlin 2014/09/24 18:39:49 Much of this file is similar to browser_action_ove
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 "base/memory/scoped_vector.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/extensions/extension_action.h"
11 #include "chrome/browser/extensions/extension_context_menu_model.h"
12 #include "chrome/browser/extensions/extension_toolbar_model.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
16 #include "chrome/browser/ui/views/toolbar/browser_action_view.h"
17 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/common/extension.h"
20 #include "extensions/common/extension_set.h"
21 #include "ui/views/border.h"
22 #include "ui/views/controls/button/label_button_border.h"
23 #include "ui/views/controls/menu/menu_delegate.h"
24 #include "ui/views/controls/menu/menu_item_view.h"
25 #include "ui/views/controls/menu/menu_runner.h"
26 #include "ui/views/metrics.h"
27
28 namespace {
29
30 // In the browser actions container's chevron menu, a menu item view's icon
31 // comes from BrowserActionView::GetIconWithBadge() when the menu item view is
32 // created. But, the browser action's icon may not be loaded in time because it
33 // is read from file system in another thread.
34 // The IconUpdater will update the menu item view's icon when the browser
35 // action's icon has been updated.
36 class IconUpdater : public BrowserActionView::IconObserver {
37 public:
38 IconUpdater(views::MenuItemView* menu_item_view, BrowserActionView* view)
39 : menu_item_view_(menu_item_view),
40 view_(view) {
41 DCHECK(menu_item_view);
42 DCHECK(view);
43 view->set_icon_observer(this);
44 }
45 virtual ~IconUpdater() {
46 view_->set_icon_observer(NULL);
47 }
48
49 // BrowserActionView::IconObserver:
50 virtual void OnIconUpdated(const gfx::ImageSkia& icon) OVERRIDE {
51 menu_item_view_->SetIcon(icon);
52 }
53
54 private:
55 // The menu item view whose icon might be updated.
56 views::MenuItemView* menu_item_view_;
57
58 // The view to be observed. When its icon changes, update the corresponding
59 // menu item view's icon.
60 BrowserActionView* view_;
61
62 DISALLOW_COPY_AND_ASSIGN(IconUpdater);
63 };
64
65 } // namespace
66
67 // This class handles the overflow menu for browser actions (showing the menu,
68 // drag and drop, etc). This class manages its own lifetime.
Finnur 2014/09/25 09:58:49 Oh yeah? Why is it a scoped_ptr then? :) This look
Devlin 2014/09/25 15:56:49 Whoops, done.
69 class ChevronMenuButton::MenuController : public views::MenuDelegate {
70 public:
71 MenuController(ChevronMenuButton* owner,
72 BrowserActionsContainer* browser_actions_container,
73 bool for_drop);
74 virtual ~MenuController();
75
76 // Shows the overflow menu.
77 void RunMenu(views::Widget* widget);
78
79 // Closes the overflow menu (and its context menu if open as well).
80 void CloseMenu();
81
82 private:
83 // Overridden from views::MenuDelegate:
84 virtual bool IsCommandEnabled(int id) const OVERRIDE;
85 virtual void ExecuteCommand(int id) OVERRIDE;
86 virtual bool ShowContextMenu(views::MenuItemView* source,
87 int id,
88 const gfx::Point& p,
89 ui::MenuSourceType source_type) OVERRIDE;
90 virtual void DropMenuClosed(views::MenuItemView* menu) OVERRIDE;
91 // These drag functions offer support for dragging icons into the overflow
92 // menu.
93 virtual bool GetDropFormats(
94 views::MenuItemView* menu,
95 int* formats,
96 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) OVERRIDE;
97 virtual bool AreDropTypesRequired(views::MenuItemView* menu) OVERRIDE;
98 virtual bool CanDrop(views::MenuItemView* menu,
99 const ui::OSExchangeData& data) OVERRIDE;
100 virtual int GetDropOperation(views::MenuItemView* item,
101 const ui::DropTargetEvent& event,
102 DropPosition* position) OVERRIDE;
103 virtual int OnPerformDrop(views::MenuItemView* menu,
104 DropPosition position,
105 const ui::DropTargetEvent& event) OVERRIDE;
106 // These three drag functions offer support for dragging icons out of the
107 // overflow menu.
108 virtual bool CanDrag(views::MenuItemView* menu) OVERRIDE;
109 virtual void WriteDragData(views::MenuItemView* sender,
110 ui::OSExchangeData* data) OVERRIDE;
111 virtual int GetDragOperations(views::MenuItemView* sender) OVERRIDE;
112
113 // Returns the offset into |views_| for the given |id|.
114 size_t IndexForId(int id) const;
115
116 // The owning ChevronMenuButton.
117 ChevronMenuButton* owner_;
118
119 // A pointer to the browser action container.
120 BrowserActionsContainer* browser_actions_container_;
121
122 // The overflow menu for the menu button. Owned by |menu_runner_|.
123 views::MenuItemView* menu_;
124
125 // Resposible for running the menu.
126 scoped_ptr<views::MenuRunner> menu_runner_;
127
128 // The index into the BrowserActionView vector, indicating where to start
129 // picking browser actions to draw.
130 int start_index_;
131
132 // Whether this controller is being used for drop.
133 bool for_drop_;
134
135 // The vector keeps all icon updaters associated with menu item views in the
136 // controller. The icon updater will update the menu item view's icon when
137 // the browser action view's icon has been updated.
138 ScopedVector<IconUpdater> icon_updaters_;
139
140 DISALLOW_COPY_AND_ASSIGN(MenuController);
141 };
142
143 ChevronMenuButton::MenuController::MenuController(
144 ChevronMenuButton* owner,
145 BrowserActionsContainer* browser_actions_container,
146 bool for_drop)
147 : owner_(owner),
148 browser_actions_container_(browser_actions_container),
149 menu_(NULL),
150 start_index_(
151 browser_actions_container_->VisibleBrowserActionsAfterAnimation()),
152 for_drop_(for_drop) {
153 menu_ = new views::MenuItemView(this);
154 menu_runner_.reset(new views::MenuRunner(
155 menu_, for_drop_ ? views::MenuRunner::FOR_DROP : 0));
156 menu_->set_has_icons(true);
157
158 size_t command_id = 1; // Menu id 0 is reserved, start with 1.
159 for (size_t i = start_index_;
160 i < browser_actions_container_->num_browser_actions(); ++i) {
161 BrowserActionView* view =
162 browser_actions_container_->GetBrowserActionViewAt(i);
163 views::MenuItemView* menu_item = menu_->AppendMenuItemWithIcon(
164 command_id,
165 base::UTF8ToUTF16(view->extension()->name()),
166 view->GetIconWithBadge());
167
168 // Set the tooltip for this item.
169 base::string16 tooltip = base::UTF8ToUTF16(
170 view->extension_action()->GetTitle(
171 view->view_controller()->GetCurrentTabId()));
172 menu_->SetTooltip(tooltip, command_id);
173
174 icon_updaters_.push_back(new IconUpdater(menu_item, view));
175
176 ++command_id;
177 }
178 }
179
180 ChevronMenuButton::MenuController::~MenuController() {
181 }
182
183 void ChevronMenuButton::MenuController::RunMenu(views::Widget* window) {
184 gfx::Rect bounds = owner_->bounds();
185 gfx::Point screen_loc;
186 views::View::ConvertPointToScreen(owner_, &screen_loc);
187 bounds.set_x(screen_loc.x());
188 bounds.set_y(screen_loc.y());
189
190 if (menu_runner_->RunMenuAt(window,
191 owner_,
192 bounds,
193 views::MENU_ANCHOR_TOPRIGHT,
194 ui::MENU_SOURCE_NONE) ==
195 views::MenuRunner::MENU_DELETED)
196 return;
197
198 if (!for_drop_) {
199 // Give the context menu (if any) a chance to execute the user-selected
200 // command.
201 base::MessageLoop::current()->PostTask(
202 FROM_HERE,
203 base::Bind(&ChevronMenuButton::MenuDone,
204 owner_->weak_factory_.GetWeakPtr()));
205 }
206 }
207
208 void ChevronMenuButton::MenuController::CloseMenu() {
209 menu_->Cancel();
210 }
211
212 bool ChevronMenuButton::MenuController::IsCommandEnabled(int id) const {
213 BrowserActionView* view =
214 browser_actions_container_->GetBrowserActionViewAt(start_index_ + id - 1);
215 return view->IsEnabled(view->view_controller()->GetCurrentTabId());
216 }
217
218 void ChevronMenuButton::MenuController::ExecuteCommand(int id) {
219 browser_actions_container_->GetBrowserActionViewAt(start_index_ + id - 1)->
220 view_controller()->ExecuteActionByUser();
221 }
222
223 bool ChevronMenuButton::MenuController::ShowContextMenu(
224 views::MenuItemView* source,
225 int id,
226 const gfx::Point& p,
227 ui::MenuSourceType source_type) {
228 BrowserActionView* view = browser_actions_container_->GetBrowserActionViewAt(
229 start_index_ + id - 1);
230 if (!view->extension()->ShowConfigureContextMenus())
231 return false;
232
233 scoped_refptr<ExtensionContextMenuModel> context_menu_contents =
234 new ExtensionContextMenuModel(view->extension(),
235 view->view_controller()->browser(),
236 view->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 void ChevronMenuButton::MenuController::DropMenuClosed(
260 views::MenuItemView* menu) {
261 owner_->MenuDone();
262 }
263
264 bool ChevronMenuButton::MenuController::GetDropFormats(
265 views::MenuItemView* menu,
266 int* formats,
267 std::set<OSExchangeData::CustomFormat>* custom_formats) {
268 return BrowserActionDragData::GetDropFormats(custom_formats);
269 }
270
271 bool ChevronMenuButton::MenuController::AreDropTypesRequired(
272 views::MenuItemView* menu) {
273 return BrowserActionDragData::AreDropTypesRequired();
274 }
275
276 bool ChevronMenuButton::MenuController::CanDrop(
277 views::MenuItemView* menu, const OSExchangeData& data) {
278 return BrowserActionDragData::CanDrop(data,
279 browser_actions_container_->profile());
280 }
281
282 int ChevronMenuButton::MenuController::GetDropOperation(
283 views::MenuItemView* item,
284 const ui::DropTargetEvent& event,
285 DropPosition* position) {
286 // Don't allow dropping from the BrowserActionContainer into slot 0 of the
287 // overflow menu since once the move has taken place the item you are dragging
288 // falls right out of the menu again once the user releases the button
289 // (because we don't shrink the BrowserActionContainer when you do this).
290 if ((item->GetCommand() == 0) && (*position == DROP_BEFORE)) {
291 BrowserActionDragData drop_data;
292 if (!drop_data.Read(event.data()))
293 return ui::DragDropTypes::DRAG_NONE;
294
295 if (drop_data.index() < browser_actions_container_->VisibleBrowserActions())
296 return ui::DragDropTypes::DRAG_NONE;
297 }
298
299 return ui::DragDropTypes::DRAG_MOVE;
300 }
301
302 int ChevronMenuButton::MenuController::OnPerformDrop(
303 views::MenuItemView* menu,
304 DropPosition position,
305 const ui::DropTargetEvent& event) {
306 BrowserActionDragData drop_data;
307 if (!drop_data.Read(event.data()))
308 return ui::DragDropTypes::DRAG_NONE;
309
310 size_t drop_index = IndexForId(menu->GetCommand());
311
312 // When not dragging within the overflow menu (dragging an icon into the menu)
313 // subtract one to get the right index.
314 if (position == DROP_BEFORE &&
315 drop_data.index() < browser_actions_container_->VisibleBrowserActions())
316 --drop_index;
317
318 Profile* profile = browser_actions_container_->profile();
319 // Move the extension in the model.
320 const extensions::Extension* extension =
321 extensions::ExtensionRegistry::Get(profile)->
322 enabled_extensions().GetByID(drop_data.id());
323 extensions::ExtensionToolbarModel* toolbar_model =
324 extensions::ExtensionToolbarModel::Get(profile);
325 if (profile->IsOffTheRecord())
326 drop_index = toolbar_model->IncognitoIndexToOriginal(drop_index);
327 toolbar_model->MoveExtensionIcon(extension, drop_index);
328
329 // If the extension was moved to the overflow menu from the main bar, notify
330 // the owner.
331 if (drop_data.index() < browser_actions_container_->VisibleBrowserActions())
332 browser_actions_container_->NotifyActionMovedToOverflow();
333
334 if (for_drop_)
335 owner_->MenuDone();
336 return ui::DragDropTypes::DRAG_MOVE;
337 }
338
339 bool ChevronMenuButton::MenuController::CanDrag(views::MenuItemView* menu) {
340 return true;
341 }
342
343 void ChevronMenuButton::MenuController::WriteDragData(
344 views::MenuItemView* sender, OSExchangeData* data) {
345 size_t drag_index = IndexForId(sender->GetCommand());
346 const extensions::Extension* extension =
347 browser_actions_container_->GetBrowserActionViewAt(drag_index)->
348 extension();
349 BrowserActionDragData drag_data(extension->id(), drag_index);
350 drag_data.Write(browser_actions_container_->profile(), data);
351 }
352
353 int ChevronMenuButton::MenuController::GetDragOperations(
354 views::MenuItemView* sender) {
355 return ui::DragDropTypes::DRAG_MOVE;
356 }
357
358 size_t ChevronMenuButton::MenuController::IndexForId(int id) const {
359 // The index of the view being dragged (GetCommand gives a 1-based index into
360 // the overflow menu).
361 DCHECK_GT(browser_actions_container_->VisibleBrowserActions() + id, 0u);
362 return browser_actions_container_->VisibleBrowserActions() + id - 1;
363 }
364
365 ChevronMenuButton::ChevronMenuButton(
366 BrowserActionsContainer* browser_actions_container)
367 : views::MenuButton(NULL, base::string16(), this, false),
368 browser_actions_container_(browser_actions_container),
369 weak_factory_(this) {
370 }
371
372 ChevronMenuButton::~ChevronMenuButton() {
373 }
374
375 void ChevronMenuButton::CloseMenu() {
376 if (menu_controller_.get())
377 menu_controller_->CloseMenu();
378 }
379
380 scoped_ptr<views::LabelButtonBorder> ChevronMenuButton::CreateDefaultBorder()
381 const {
382 // The chevron resource was designed to not have any insets.
383 scoped_ptr<views::LabelButtonBorder> border =
384 views::MenuButton::CreateDefaultBorder();
385 border->set_insets(gfx::Insets());
386 return border.Pass();
387 }
388
389 bool ChevronMenuButton::GetDropFormats(
390 int* formats,
391 std::set<OSExchangeData::CustomFormat>* custom_formats) {
392 return BrowserActionDragData::GetDropFormats(custom_formats);
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_->profile());
402 }
403
404 void ChevronMenuButton::OnDragEntered(const ui::DropTargetEvent& event) {
405 DCHECK(!weak_factory_.HasWeakPtrs());
406 base::MessageLoop::current()->PostDelayedTask(
407 FROM_HERE,
408 base::Bind(&ChevronMenuButton::ShowOverflowMenu,
409 weak_factory_.GetWeakPtr(),
410 true),
411 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
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 return ui::DragDropTypes::DRAG_MOVE;
424 }
425
426 void ChevronMenuButton::OnMenuButtonClicked(views::View* source,
427 const gfx::Point& point) {
428 DCHECK_EQ(this, source);
429 ShowOverflowMenu(false);
430 }
431
432 void ChevronMenuButton::ShowOverflowMenu(bool for_drop) {
433 DCHECK(!menu_controller_.get());
434 menu_controller_.reset(new MenuController(
435 this, browser_actions_container_, for_drop));
436 menu_controller_->RunMenu(GetWidget());
437 }
438
439 void ChevronMenuButton::MenuDone() {
440 menu_controller_.reset();
441 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/toolbar/chevron_menu_button.h ('k') | chrome/chrome_browser_ui.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698