Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / views / controls / menu / menu_controller.h
blobb1db03d63820d6be5023bee0107af0c22ca2ed8c
1 // Copyright (c) 2012 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.
5 #ifndef UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_
6 #define UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_
8 #include "build/build_config.h"
10 #include <list>
11 #include <set>
12 #include <vector>
14 #include "base/compiler_specific.h"
15 #include "base/memory/linked_ptr.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/timer/timer.h"
18 #include "ui/events/event.h"
19 #include "ui/events/event_constants.h"
20 #include "ui/events/platform/platform_event_dispatcher.h"
21 #include "ui/views/controls/button/menu_button.h"
22 #include "ui/views/controls/menu/menu_config.h"
23 #include "ui/views/controls/menu/menu_delegate.h"
24 #include "ui/views/widget/widget_observer.h"
26 namespace base {
27 class MessagePumpDispatcher;
29 namespace gfx {
30 class Screen;
32 namespace ui {
33 class NativeTheme;
34 class OSExchangeData;
35 class ScopedEventDispatcher;
37 namespace views {
39 class MenuButton;
40 class MenuHostRootView;
41 class MenuItemView;
42 class MenuMessageLoop;
43 class MouseEvent;
44 class SubmenuView;
45 class View;
47 namespace internal {
48 class MenuControllerDelegate;
49 class MenuEventDispatcher;
50 class MenuMessagePumpDispatcher;
51 class MenuRunnerImpl;
54 namespace test {
55 class MenuControllerTest;
58 // MenuController -------------------------------------------------------------
60 // MenuController is used internally by the various menu classes to manage
61 // showing, selecting and drag/drop for menus. All relevant events are
62 // forwarded to the MenuController from SubmenuView and MenuHost.
63 class VIEWS_EXPORT MenuController : public WidgetObserver {
64 public:
65 // Enumeration of how the menu should exit.
66 enum ExitType {
67 // Don't exit.
68 EXIT_NONE,
70 // All menus, including nested, should be exited.
71 EXIT_ALL,
73 // Only the outermost menu should be exited.
74 EXIT_OUTERMOST,
76 // This is set if the menu is being closed as the result of one of the menus
77 // being destroyed.
78 EXIT_DESTROYED
81 // If a menu is currently active, this returns the controller for it.
82 static MenuController* GetActiveInstance();
84 // Runs the menu at the specified location. If the menu was configured to
85 // block, the selected item is returned. If the menu does not block this
86 // returns NULL immediately.
87 MenuItemView* Run(Widget* parent,
88 MenuButton* button,
89 MenuItemView* root,
90 const gfx::Rect& bounds,
91 MenuAnchorPosition position,
92 bool context_menu,
93 bool is_nested_drag,
94 int* event_flags);
96 // Whether or not Run blocks.
97 bool IsBlockingRun() const { return blocking_run_; }
99 bool in_nested_run() const { return !menu_stack_.empty(); }
101 // Whether or not drag operation is in progress.
102 bool drag_in_progress() const { return drag_in_progress_; }
104 // Whether the MenuController initiated the drag in progress. False if there
105 // is no drag in progress.
106 bool did_initiate_drag() const { return did_initiate_drag_; }
108 // Returns the owner of child windows.
109 // WARNING: this may be NULL.
110 Widget* owner() { return owner_; }
112 // Get the anchor position wich is used to show this menu.
113 MenuAnchorPosition GetAnchorPosition() { return state_.anchor; }
115 // Cancels the current Run. See ExitType for a description of what happens
116 // with the various parameters.
117 void Cancel(ExitType type);
119 // An alternative to Cancel(EXIT_ALL) that can be used with a OneShotTimer.
120 void CancelAll() { Cancel(EXIT_ALL); }
122 // Returns the current exit type. This returns a value other than EXIT_NONE if
123 // the menu is being canceled.
124 ExitType exit_type() const { return exit_type_; }
126 // Returns the time from the event which closed the menu - or 0.
127 base::TimeDelta closing_event_time() const { return closing_event_time_; }
129 void set_is_combobox(bool is_combobox) { is_combobox_ = is_combobox; }
131 // Various events, forwarded from the submenu.
133 // NOTE: the coordinates of the events are in that of the
134 // MenuScrollViewContainer.
135 void OnMousePressed(SubmenuView* source, const ui::MouseEvent& event);
136 void OnMouseDragged(SubmenuView* source, const ui::MouseEvent& event);
137 void OnMouseReleased(SubmenuView* source, const ui::MouseEvent& event);
138 void OnMouseMoved(SubmenuView* source, const ui::MouseEvent& event);
139 void OnMouseEntered(SubmenuView* source, const ui::MouseEvent& event);
140 bool OnMouseWheel(SubmenuView* source, const ui::MouseWheelEvent& event);
141 void OnGestureEvent(SubmenuView* source, ui::GestureEvent* event);
143 bool GetDropFormats(
144 SubmenuView* source,
145 int* formats,
146 std::set<ui::OSExchangeData::CustomFormat>* custom_formats);
147 bool AreDropTypesRequired(SubmenuView* source);
148 bool CanDrop(SubmenuView* source, const ui::OSExchangeData& data);
149 void OnDragEntered(SubmenuView* source, const ui::DropTargetEvent& event);
150 int OnDragUpdated(SubmenuView* source, const ui::DropTargetEvent& event);
151 void OnDragExited(SubmenuView* source);
152 int OnPerformDrop(SubmenuView* source, const ui::DropTargetEvent& event);
154 // Invoked from the scroll buttons of the MenuScrollViewContainer.
155 void OnDragEnteredScrollButton(SubmenuView* source, bool is_up);
156 void OnDragExitedScrollButton(SubmenuView* source);
158 // Called by the Widget when a drag is about to start on a child view. This
159 // could be initiated by one of our MenuItemViews, or could be through another
160 // child View.
161 void OnDragWillStart();
163 // Called by the Widget when the drag has completed. |should_close|
164 // corresponds to whether or not the menu should close.
165 void OnDragComplete(bool should_close);
167 // Called while dispatching messages to intercept key events.
168 // If |character| is other than 0, it is handled as a mnemonic.
169 // Otherwise, |key_code| is handled as a menu navigation command.
170 // Returns ui::POST_DISPATCH_NONE if the event was swallowed by the menu.
171 ui::PostDispatchAction OnWillDispatchKeyEvent(base::char16 character,
172 ui::KeyboardCode key_code);
174 // Update the submenu's selection based on the current mouse location
175 void UpdateSubmenuSelection(SubmenuView* source);
177 // WidgetObserver overrides:
178 void OnWidgetDestroying(Widget* widget) override;
180 // Only used for testing.
181 bool IsCancelAllTimerRunningForTest();
183 // Only used for testing.
184 static void TurnOffMenuSelectionHoldForTest();
186 private:
187 friend class internal::MenuEventDispatcher;
188 friend class internal::MenuMessagePumpDispatcher;
189 friend class internal::MenuRunnerImpl;
190 friend class test::MenuControllerTest;
191 friend class MenuKeyEventHandler;
192 friend class MenuHostRootView;
193 friend class MenuItemView;
194 friend class SubmenuView;
196 class MenuScrollTask;
198 struct SelectByCharDetails;
200 // Values supplied to SetSelection.
201 enum SetSelectionTypes {
202 SELECTION_DEFAULT = 0,
204 // If set submenus are opened immediately, otherwise submenus are only
205 // openned after a timer fires.
206 SELECTION_UPDATE_IMMEDIATELY = 1 << 0,
208 // If set and the menu_item has a submenu, the submenu is shown.
209 SELECTION_OPEN_SUBMENU = 1 << 1,
211 // SetSelection is being invoked as the result exiting or cancelling the
212 // menu. This is used for debugging.
213 SELECTION_EXIT = 1 << 2,
216 // Direction for IncrementSelection and FindInitialSelectableMenuItem.
217 enum SelectionIncrementDirectionType {
218 // Navigate the menu up.
219 INCREMENT_SELECTION_UP,
221 // Navigate the menu down.
222 INCREMENT_SELECTION_DOWN,
225 // Tracks selection information.
226 struct State {
227 State();
228 ~State();
230 // The selected menu item.
231 MenuItemView* item;
233 // If item has a submenu this indicates if the submenu is showing.
234 bool submenu_open;
236 // Bounds passed to the run menu. Used for positioning the first menu.
237 gfx::Rect initial_bounds;
239 // Position of the initial menu.
240 MenuAnchorPosition anchor;
242 // The direction child menus have opened in.
243 std::list<bool> open_leading;
245 // Bounds for the monitor we're showing on.
246 gfx::Rect monitor_bounds;
248 // Is the current menu a context menu.
249 bool context_menu;
252 // Used by GetMenuPart to indicate the menu part at a particular location.
253 struct MenuPart {
254 // Type of part.
255 enum Type {
256 NONE,
257 MENU_ITEM,
258 SCROLL_UP,
259 SCROLL_DOWN
262 MenuPart() : type(NONE), menu(NULL), parent(NULL), submenu(NULL) {}
264 // Convenience for testing type == SCROLL_DOWN or type == SCROLL_UP.
265 bool is_scroll() const { return type == SCROLL_DOWN || type == SCROLL_UP; }
267 // Type of part.
268 Type type;
270 // If type is MENU_ITEM, this is the menu item the mouse is over, otherwise
271 // this is NULL.
272 // NOTE: if type is MENU_ITEM and the mouse is not over a valid menu item
273 // but is over a menu (for example, the mouse is over a separator or
274 // empty menu), this is NULL and parent is the menu the mouse was
275 // clicked on.
276 MenuItemView* menu;
278 // If type is MENU_ITEM but the mouse is not over a menu item this is the
279 // parent of the menu item the user clicked on. Otherwise this is NULL.
280 MenuItemView* parent;
282 // This is the submenu the mouse is over.
283 SubmenuView* submenu;
286 // Sets the selection to |menu_item|. A value of NULL unselects
287 // everything. |types| is a bitmask of |SetSelectionTypes|.
289 // Internally this updates pending_state_ immediatley. state_ is only updated
290 // immediately if SELECTION_UPDATE_IMMEDIATELY is set. If
291 // SELECTION_UPDATE_IMMEDIATELY is not set CommitPendingSelection is invoked
292 // to show/hide submenus and update state_.
293 void SetSelection(MenuItemView* menu_item, int types);
295 void SetSelectionOnPointerDown(SubmenuView* source,
296 const ui::LocatedEvent& event);
297 void StartDrag(SubmenuView* source, const gfx::Point& location);
299 // Key processing.
300 void OnKeyDown(ui::KeyboardCode key_code);
302 // Creates a MenuController. If |blocking| is true a nested message loop is
303 // started in |Run|.
304 MenuController(ui::NativeTheme* theme,
305 bool blocking,
306 internal::MenuControllerDelegate* delegate);
308 ~MenuController() override;
310 // Runs the platform specific bits of the message loop. If |nested_menu| is
311 // true we're being asked to run a menu from within a menu (eg a context
312 // menu).
313 void RunMessageLoop(bool nested_menu);
315 // Invokes AcceleratorPressed() on the hot tracked view if there is one.
316 // Returns true if AcceleratorPressed() was invoked.
317 bool SendAcceleratorToHotTrackedView();
319 void UpdateInitialLocation(const gfx::Rect& bounds,
320 MenuAnchorPosition position,
321 bool context_menu);
323 // Invoked when the user accepts the selected item. This is only used
324 // when blocking. This schedules the loop to quit.
325 void Accept(MenuItemView* item, int event_flags);
327 bool ShowSiblingMenu(SubmenuView* source, const gfx::Point& mouse_location);
329 // Shows a context menu for |menu_item| as a result of an event if
330 // appropriate, using the given |screen_location|. This is invoked on long
331 // press, releasing the right mouse button, and pressing the "app" key.
332 // Returns whether a context menu was shown.
333 bool ShowContextMenu(MenuItemView* menu_item,
334 const gfx::Point& screen_location,
335 ui::MenuSourceType source_type);
337 // Closes all menus, including any menus of nested invocations of Run.
338 void CloseAllNestedMenus();
340 // Gets the enabled menu item at the specified location.
341 // If over_any_menu is non-null it is set to indicate whether the location
342 // is over any menu. It is possible for this to return NULL, but
343 // over_any_menu to be true. For example, the user clicked on a separator.
344 MenuItemView* GetMenuItemAt(View* menu, int x, int y);
346 // If there is an empty menu item at the specified location, it is returned.
347 MenuItemView* GetEmptyMenuItemAt(View* source, int x, int y);
349 // Returns true if the coordinate is over the scroll buttons of the
350 // SubmenuView's MenuScrollViewContainer. If true is returned, part is set to
351 // indicate which scroll button the coordinate is.
352 bool IsScrollButtonAt(SubmenuView* source,
353 int x,
354 int y,
355 MenuPart::Type* part);
357 // Returns the target for the mouse event. The coordinates are in terms of
358 // source's scroll view container.
359 MenuPart GetMenuPart(SubmenuView* source, const gfx::Point& source_loc);
361 // Returns the target for mouse events. The search is done through |item| and
362 // all its parents.
363 MenuPart GetMenuPartByScreenCoordinateUsingMenu(MenuItemView* item,
364 const gfx::Point& screen_loc);
366 // Implementation of GetMenuPartByScreenCoordinate for a single menu. Returns
367 // true if the supplied SubmenuView contains the location in terms of the
368 // screen. If it does, part is set appropriately and true is returned.
369 bool GetMenuPartByScreenCoordinateImpl(SubmenuView* menu,
370 const gfx::Point& screen_loc,
371 MenuPart* part);
373 // Returns true if the SubmenuView contains the specified location. This does
374 // NOT included the scroll buttons, only the submenu view.
375 bool DoesSubmenuContainLocation(SubmenuView* submenu,
376 const gfx::Point& screen_loc);
378 // Opens/Closes the necessary menus such that state_ matches that of
379 // pending_state_. This is invoked if submenus are not opened immediately,
380 // but after a delay.
381 void CommitPendingSelection();
383 // If item has a submenu, it is closed. This does NOT update the selection
384 // in anyway.
385 void CloseMenu(MenuItemView* item);
387 // If item has a submenu, it is opened. This does NOT update the selection
388 // in anyway.
389 void OpenMenu(MenuItemView* item);
391 // Implementation of OpenMenu. If |show| is true, this invokes show on the
392 // menu, otherwise Reposition is invoked.
393 void OpenMenuImpl(MenuItemView* item, bool show);
395 // Invoked when the children of a menu change and the menu is showing.
396 // This closes any submenus and resizes the submenu.
397 void MenuChildrenChanged(MenuItemView* item);
399 // Builds the paths of the two menu items into the two paths, and
400 // sets first_diff_at to the location of the first difference between the
401 // two paths.
402 void BuildPathsAndCalculateDiff(MenuItemView* old_item,
403 MenuItemView* new_item,
404 std::vector<MenuItemView*>* old_path,
405 std::vector<MenuItemView*>* new_path,
406 size_t* first_diff_at);
408 // Builds the path for the specified item.
409 void BuildMenuItemPath(MenuItemView* item, std::vector<MenuItemView*>* path);
411 // Starts/stops the timer that commits the pending state to state
412 // (opens/closes submenus).
413 void StartShowTimer();
414 void StopShowTimer();
416 // Starts/stops the timer cancel the menu. This is used during drag and
417 // drop when the drop enters/exits the menu.
418 void StartCancelAllTimer();
419 void StopCancelAllTimer();
421 // Calculates the bounds of the menu to show. is_leading is set to match the
422 // direction the menu opened in.
423 gfx::Rect CalculateMenuBounds(MenuItemView* item,
424 bool prefer_leading,
425 bool* is_leading);
427 // Calculates the bubble bounds of the menu to show. is_leading is set to
428 // match the direction the menu opened in.
429 gfx::Rect CalculateBubbleMenuBounds(MenuItemView* item,
430 bool prefer_leading,
431 bool* is_leading);
433 // Returns the depth of the menu.
434 static int MenuDepth(MenuItemView* item);
436 // Selects the next or previous (depending on |direction|) menu item.
437 void IncrementSelection(SelectionIncrementDirectionType direction);
439 // Returns the first (|direction| == NAVIGATE_SELECTION_DOWN) or the last
440 // (|direction| == INCREMENT_SELECTION_UP) selectable child menu item of
441 // |parent|. If there are no selectable items returns NULL.
442 MenuItemView* FindInitialSelectableMenuItem(
443 MenuItemView* parent,
444 SelectionIncrementDirectionType direction);
446 // Returns the next or previous selectable child menu item of |parent|
447 // starting at |index| and incrementing or decrementing index by 1 depending
448 // on |direction|. If there are no more selectable items NULL is returned.
449 MenuItemView* FindNextSelectableMenuItem(
450 MenuItemView* parent,
451 int index,
452 SelectionIncrementDirectionType direction);
454 // If the selected item has a submenu and it isn't currently open, the
455 // the selection is changed such that the menu opens immediately.
456 void OpenSubmenuChangeSelectionIfCan();
458 // If possible, closes the submenu.
459 void CloseSubmenu();
461 // Returns details about which menu items match the mnemonic |key|.
462 // |match_function| is used to determine which menus match.
463 SelectByCharDetails FindChildForMnemonic(
464 MenuItemView* parent,
465 base::char16 key,
466 bool (*match_function)(MenuItemView* menu, base::char16 mnemonic));
468 // Selects or accepts the appropriate menu item based on |details|.
469 void AcceptOrSelect(MenuItemView* parent, const SelectByCharDetails& details);
471 // Selects by mnemonic, and if that doesn't work tries the first character of
472 // the title.
473 void SelectByChar(base::char16 key);
475 // For Windows and Aura we repost an event for some events that dismiss
476 // the context menu. The event is then reprocessed to cause its result
477 // if the context menu had not been present.
478 // On non-aura Windows, a new mouse event is generated and posted to
479 // the window (if there is one) at the location of the event. On
480 // aura, the event is reposted on the RootWindow.
481 void RepostEvent(SubmenuView* source, const ui::LocatedEvent& event);
483 // Sets the drop target to new_item.
484 void SetDropMenuItem(MenuItemView* new_item,
485 MenuDelegate::DropPosition position);
487 // Starts/stops scrolling as appropriate. part gives the part the mouse is
488 // over.
489 void UpdateScrolling(const MenuPart& part);
491 // Stops scrolling.
492 void StopScrolling();
494 // Updates active mouse view from the location of the event and sends it
495 // the appropriate events. This is used to send mouse events to child views so
496 // that they react to click-drag-release as if the user clicked on the view
497 // itself.
498 void UpdateActiveMouseView(SubmenuView* event_source,
499 const ui::MouseEvent& event,
500 View* target_menu);
502 // Sends a mouse release event to the current active mouse view and sets
503 // it to null.
504 void SendMouseReleaseToActiveView(SubmenuView* event_source,
505 const ui::MouseEvent& event);
507 // Sends a mouse capture lost event to the current active mouse view and sets
508 // it to null.
509 void SendMouseCaptureLostToActiveView();
511 // Sets/gets the active mouse view. See UpdateActiveMouseView() for details.
512 void SetActiveMouseView(View* view);
513 View* GetActiveMouseView();
515 // Sets exit type. Calling this can terminate the active nested message-loop.
516 void SetExitType(ExitType type);
518 // Terminates the current nested message-loop.
519 void TerminateNestedMessageLoop();
521 // Handles the mouse location event on the submenu |source|.
522 void HandleMouseLocation(SubmenuView* source,
523 const gfx::Point& mouse_location);
525 // Retrieve an appropriate Screen.
526 gfx::Screen* GetScreen();
528 // The active instance.
529 static MenuController* active_instance_;
531 // If true, Run blocks. If false, Run doesn't block and this is used for
532 // drag and drop. Note that the semantics for drag and drop are slightly
533 // different: cancel timer is kicked off any time the drag moves outside the
534 // menu, mouse events do nothing...
535 bool blocking_run_;
537 // If true, we're showing.
538 bool showing_;
540 // Indicates what to exit.
541 ExitType exit_type_;
543 // Whether we did a capture. We do a capture only if we're blocking and
544 // the mouse was down when Run.
545 bool did_capture_;
547 // As the user drags the mouse around pending_state_ changes immediately.
548 // When the user stops moving/dragging the mouse (or clicks the mouse)
549 // pending_state_ is committed to state_, potentially resulting in
550 // opening or closing submenus. This gives a slight delayed effect to
551 // submenus as the user moves the mouse around. This is done so that as the
552 // user moves the mouse all submenus don't immediately pop.
553 State pending_state_;
554 State state_;
556 // If the user accepted the selection, this is the result.
557 MenuItemView* result_;
559 // The event flags when the user selected the menu.
560 int accept_event_flags_;
562 // If not empty, it means we're nested. When Run is invoked from within
563 // Run, the current state (state_) is pushed onto menu_stack_. This allows
564 // MenuController to restore the state when the nested run returns.
565 typedef std::pair<State, linked_ptr<MenuButton::PressedLock> > NestedState;
566 std::list<NestedState> menu_stack_;
568 // As the mouse moves around submenus are not opened immediately. Instead
569 // they open after this timer fires.
570 base::OneShotTimer<MenuController> show_timer_;
572 // Used to invoke CancelAll(). This is used during drag and drop to hide the
573 // menu after the mouse moves out of the of the menu. This is necessitated by
574 // the lack of an ability to detect when the drag has completed from the drop
575 // side.
576 base::OneShotTimer<MenuController> cancel_all_timer_;
578 // Drop target.
579 MenuItemView* drop_target_;
580 MenuDelegate::DropPosition drop_position_;
582 // Owner of child windows.
583 // WARNING: this may be NULL.
584 Widget* owner_;
586 // Indicates a possible drag operation.
587 bool possible_drag_;
589 // True when drag operation is in progress.
590 bool drag_in_progress_;
592 // True when the drag operation in progress was initiated by the
593 // MenuController for a child MenuItemView (as opposed to initiated separately
594 // by a child View).
595 bool did_initiate_drag_;
597 // Location the mouse was pressed at. Used to detect d&d.
598 gfx::Point press_pt_;
600 // We get a slew of drag updated messages as the mouse is over us. To avoid
601 // continually processing whether we can drop, we cache the coordinates.
602 bool valid_drop_coordinates_;
603 gfx::Point drop_pt_;
604 int last_drop_operation_;
606 // If true, we're in the middle of invoking ShowAt on a submenu.
607 bool showing_submenu_;
609 // Task for scrolling the menu. If non-null indicates a scroll is currently
610 // underway.
611 scoped_ptr<MenuScrollTask> scroll_task_;
613 // The lock to keep the menu button pressed while a menu is visible.
614 scoped_ptr<MenuButton::PressedLock> pressed_lock_;
616 // ViewStorage id used to store the view mouse drag events are forwarded to.
617 // See UpdateActiveMouseView() for details.
618 const int active_mouse_view_id_;
620 internal::MenuControllerDelegate* delegate_;
622 // How deep we are in nested message loops. This should be at most 2 (when
623 // showing a context menu from a menu).
624 int message_loop_depth_;
626 views::MenuConfig menu_config_;
628 // The timestamp of the event which closed the menu - or 0 otherwise.
629 base::TimeDelta closing_event_time_;
631 // Time when the menu is first shown.
632 base::TimeTicks menu_start_time_;
634 // If a mouse press triggered this menu, this will have its location (in
635 // screen coordinates). Otherwise this will be (0, 0).
636 gfx::Point menu_start_mouse_press_loc_;
638 // Controls behavior differences between a combobox and other types of menu
639 // (like a context menu).
640 bool is_combobox_;
642 // Set to true if the menu item was selected by touch.
643 bool item_selected_by_touch_;
645 scoped_ptr<MenuMessageLoop> message_loop_;
647 DISALLOW_COPY_AND_ASSIGN(MenuController);
650 } // namespace views
652 #endif // UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_