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"
14 #include "base/compiler_specific.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/timer/timer.h"
17 #include "ui/events/event.h"
18 #include "ui/events/event_constants.h"
19 #include "ui/events/platform/platform_event_dispatcher.h"
20 #include "ui/views/controls/menu/menu_config.h"
21 #include "ui/views/controls/menu/menu_delegate.h"
22 #include "ui/views/widget/widget_observer.h"
25 class MessagePumpDispatcher
;
33 class ScopedEventDispatcher
;
38 class MenuHostRootView
;
40 class MenuMessageLoop
;
46 class MenuControllerDelegate
;
47 class MenuEventDispatcher
;
48 class MenuMessagePumpDispatcher
;
52 // MenuController -------------------------------------------------------------
54 // MenuController is used internally by the various menu classes to manage
55 // showing, selecting and drag/drop for menus. All relevant events are
56 // forwarded to the MenuController from SubmenuView and MenuHost.
57 class VIEWS_EXPORT MenuController
: public WidgetObserver
{
59 // Enumeration of how the menu should exit.
64 // All menus, including nested, should be exited.
67 // Only the outermost menu should be exited.
70 // This is set if the menu is being closed as the result of one of the menus
75 // If a menu is currently active, this returns the controller for it.
76 static MenuController
* GetActiveInstance();
78 // Runs the menu at the specified location. If the menu was configured to
79 // block, the selected item is returned. If the menu does not block this
80 // returns NULL immediately.
81 MenuItemView
* Run(Widget
* parent
,
84 const gfx::Rect
& bounds
,
85 MenuAnchorPosition position
,
90 // Whether or not Run blocks.
91 bool IsBlockingRun() const { return blocking_run_
; }
93 bool in_nested_run() const { return !menu_stack_
.empty(); }
95 // Whether or not drag operation is in progress.
96 bool drag_in_progress() const { return drag_in_progress_
; }
98 // Whether the MenuController initiated the drag in progress. False if there
99 // is no drag in progress.
100 bool did_initiate_drag() const { return did_initiate_drag_
; }
102 // Returns the owner of child windows.
103 // WARNING: this may be NULL.
104 Widget
* owner() { return owner_
; }
106 // Get the anchor position wich is used to show this menu.
107 MenuAnchorPosition
GetAnchorPosition() { return state_
.anchor
; }
109 // Cancels the current Run. See ExitType for a description of what happens
110 // with the various parameters.
111 void Cancel(ExitType type
);
113 // An alternative to Cancel(EXIT_ALL) that can be used with a OneShotTimer.
114 void CancelAll() { Cancel(EXIT_ALL
); }
116 // Returns the current exit type. This returns a value other than EXIT_NONE if
117 // the menu is being canceled.
118 ExitType
exit_type() const { return exit_type_
; }
120 // Returns the time from the event which closed the menu - or 0.
121 base::TimeDelta
closing_event_time() const { return closing_event_time_
; }
123 void set_is_combobox(bool is_combobox
) { is_combobox_
= is_combobox
; }
125 // Various events, forwarded from the submenu.
127 // NOTE: the coordinates of the events are in that of the
128 // MenuScrollViewContainer.
129 void OnMousePressed(SubmenuView
* source
, const ui::MouseEvent
& event
);
130 void OnMouseDragged(SubmenuView
* source
, const ui::MouseEvent
& event
);
131 void OnMouseReleased(SubmenuView
* source
, const ui::MouseEvent
& event
);
132 void OnMouseMoved(SubmenuView
* source
, const ui::MouseEvent
& event
);
133 void OnMouseEntered(SubmenuView
* source
, const ui::MouseEvent
& event
);
134 bool OnMouseWheel(SubmenuView
* source
, const ui::MouseWheelEvent
& event
);
135 void OnGestureEvent(SubmenuView
* source
, ui::GestureEvent
* event
);
140 std::set
<ui::OSExchangeData::CustomFormat
>* custom_formats
);
141 bool AreDropTypesRequired(SubmenuView
* source
);
142 bool CanDrop(SubmenuView
* source
, const ui::OSExchangeData
& data
);
143 void OnDragEntered(SubmenuView
* source
, const ui::DropTargetEvent
& event
);
144 int OnDragUpdated(SubmenuView
* source
, const ui::DropTargetEvent
& event
);
145 void OnDragExited(SubmenuView
* source
);
146 int OnPerformDrop(SubmenuView
* source
, const ui::DropTargetEvent
& event
);
148 // Invoked from the scroll buttons of the MenuScrollViewContainer.
149 void OnDragEnteredScrollButton(SubmenuView
* source
, bool is_up
);
150 void OnDragExitedScrollButton(SubmenuView
* source
);
152 // Called by the Widget when a drag is about to start on a child view. This
153 // could be initiated by one of our MenuItemViews, or could be through another
155 void OnDragWillStart();
157 // Called by the Widget when the drag has completed. |should_close|
158 // corresponds to whether or not the menu should close.
159 void OnDragComplete(bool should_close
);
161 // Update the submenu's selection based on the current mouse location
162 void UpdateSubmenuSelection(SubmenuView
* source
);
164 // WidgetObserver overrides:
165 virtual void OnWidgetDestroying(Widget
* widget
) OVERRIDE
;
167 // Only used for testing.
168 bool IsCancelAllTimerRunningForTest();
170 // Only used for testing.
171 static void TurnOffMenuSelectionHoldForTest();
174 friend class internal::MenuEventDispatcher
;
175 friend class internal::MenuMessagePumpDispatcher
;
176 friend class internal::MenuRunnerImpl
;
177 friend class MenuControllerTest
;
178 friend class MenuHostRootView
;
179 friend class MenuItemView
;
180 friend class SubmenuView
;
182 class MenuScrollTask
;
184 struct SelectByCharDetails
;
186 // Values supplied to SetSelection.
187 enum SetSelectionTypes
{
188 SELECTION_DEFAULT
= 0,
190 // If set submenus are opened immediately, otherwise submenus are only
191 // openned after a timer fires.
192 SELECTION_UPDATE_IMMEDIATELY
= 1 << 0,
194 // If set and the menu_item has a submenu, the submenu is shown.
195 SELECTION_OPEN_SUBMENU
= 1 << 1,
197 // SetSelection is being invoked as the result exiting or cancelling the
198 // menu. This is used for debugging.
199 SELECTION_EXIT
= 1 << 2,
202 // Result type for SendAcceleratorToHotTrackedView
203 enum SendAcceleratorResultType
{
204 // Accelerator is not sent because of no hot tracked views.
205 ACCELERATOR_NOT_PROCESSED
,
207 // Accelerator is sent to the hot tracked views.
208 ACCELERATOR_PROCESSED
,
210 // Same as above and the accelerator causes the exit of the menu.
211 ACCELERATOR_PROCESSED_EXIT
214 // Tracks selection information.
219 // The selected menu item.
222 // If item has a submenu this indicates if the submenu is showing.
225 // Bounds passed to the run menu. Used for positioning the first menu.
226 gfx::Rect initial_bounds
;
228 // Position of the initial menu.
229 MenuAnchorPosition anchor
;
231 // The direction child menus have opened in.
232 std::list
<bool> open_leading
;
234 // Bounds for the monitor we're showing on.
235 gfx::Rect monitor_bounds
;
237 // Is the current menu a context menu.
241 // Used by GetMenuPart to indicate the menu part at a particular location.
251 MenuPart() : type(NONE
), menu(NULL
), parent(NULL
), submenu(NULL
) {}
253 // Convenience for testing type == SCROLL_DOWN or type == SCROLL_UP.
254 bool is_scroll() const { return type
== SCROLL_DOWN
|| type
== SCROLL_UP
; }
259 // If type is MENU_ITEM, this is the menu item the mouse is over, otherwise
261 // NOTE: if type is MENU_ITEM and the mouse is not over a valid menu item
262 // but is over a menu (for example, the mouse is over a separator or
263 // empty menu), this is NULL and parent is the menu the mouse was
267 // If type is MENU_ITEM but the mouse is not over a menu item this is the
268 // parent of the menu item the user clicked on. Otherwise this is NULL.
269 MenuItemView
* parent
;
271 // This is the submenu the mouse is over.
272 SubmenuView
* submenu
;
275 // Sets the selection to |menu_item|. A value of NULL unselects
276 // everything. |types| is a bitmask of |SetSelectionTypes|.
278 // Internally this updates pending_state_ immediatley. state_ is only updated
279 // immediately if SELECTION_UPDATE_IMMEDIATELY is set. If
280 // SELECTION_UPDATE_IMMEDIATELY is not set CommitPendingSelection is invoked
281 // to show/hide submenus and update state_.
282 void SetSelection(MenuItemView
* menu_item
, int types
);
284 void SetSelectionOnPointerDown(SubmenuView
* source
,
285 const ui::LocatedEvent
& event
);
286 void StartDrag(SubmenuView
* source
, const gfx::Point
& location
);
288 // Key processing. The return value of this is returned from Dispatch.
289 // In other words, if this returns false (which happens if escape was
290 // pressed, or a matching mnemonic was found) the message loop returns.
291 bool OnKeyDown(ui::KeyboardCode key_code
);
293 // Creates a MenuController. If |blocking| is true a nested message loop is
295 MenuController(ui::NativeTheme
* theme
,
297 internal::MenuControllerDelegate
* delegate
);
299 virtual ~MenuController();
301 // Runs the platform specific bits of the message loop. If |nested_menu| is
302 // true we're being asked to run a menu from within a menu (eg a context
304 void RunMessageLoop(bool nested_menu
);
306 // AcceleratorPressed is invoked on the hot tracked view if it exists.
307 SendAcceleratorResultType
SendAcceleratorToHotTrackedView();
309 void UpdateInitialLocation(const gfx::Rect
& bounds
,
310 MenuAnchorPosition position
,
313 // Invoked when the user accepts the selected item. This is only used
314 // when blocking. This schedules the loop to quit.
315 void Accept(MenuItemView
* item
, int event_flags
);
317 bool ShowSiblingMenu(SubmenuView
* source
, const gfx::Point
& mouse_location
);
319 // Shows a context menu for |menu_item| as a result of a located event if
320 // appropriate. This is invoked on long press and releasing the right mouse
321 // button. Returns whether a context menu was shown.
322 bool ShowContextMenu(MenuItemView
* menu_item
,
324 const ui::LocatedEvent
& event
,
325 ui::MenuSourceType source_type
);
327 // Closes all menus, including any menus of nested invocations of Run.
328 void CloseAllNestedMenus();
330 // Gets the enabled menu item at the specified location.
331 // If over_any_menu is non-null it is set to indicate whether the location
332 // is over any menu. It is possible for this to return NULL, but
333 // over_any_menu to be true. For example, the user clicked on a separator.
334 MenuItemView
* GetMenuItemAt(View
* menu
, int x
, int y
);
336 // If there is an empty menu item at the specified location, it is returned.
337 MenuItemView
* GetEmptyMenuItemAt(View
* source
, int x
, int y
);
339 // Returns true if the coordinate is over the scroll buttons of the
340 // SubmenuView's MenuScrollViewContainer. If true is returned, part is set to
341 // indicate which scroll button the coordinate is.
342 bool IsScrollButtonAt(SubmenuView
* source
,
345 MenuPart::Type
* part
);
347 // Returns the target for the mouse event. The coordinates are in terms of
348 // source's scroll view container.
349 MenuPart
GetMenuPart(SubmenuView
* source
, const gfx::Point
& source_loc
);
351 // Returns the target for mouse events. The search is done through |item| and
353 MenuPart
GetMenuPartByScreenCoordinateUsingMenu(MenuItemView
* item
,
354 const gfx::Point
& screen_loc
);
356 // Implementation of GetMenuPartByScreenCoordinate for a single menu. Returns
357 // true if the supplied SubmenuView contains the location in terms of the
358 // screen. If it does, part is set appropriately and true is returned.
359 bool GetMenuPartByScreenCoordinateImpl(SubmenuView
* menu
,
360 const gfx::Point
& screen_loc
,
363 // Returns true if the SubmenuView contains the specified location. This does
364 // NOT included the scroll buttons, only the submenu view.
365 bool DoesSubmenuContainLocation(SubmenuView
* submenu
,
366 const gfx::Point
& screen_loc
);
368 // Opens/Closes the necessary menus such that state_ matches that of
369 // pending_state_. This is invoked if submenus are not opened immediately,
370 // but after a delay.
371 void CommitPendingSelection();
373 // If item has a submenu, it is closed. This does NOT update the selection
375 void CloseMenu(MenuItemView
* item
);
377 // If item has a submenu, it is opened. This does NOT update the selection
379 void OpenMenu(MenuItemView
* item
);
381 // Implementation of OpenMenu. If |show| is true, this invokes show on the
382 // menu, otherwise Reposition is invoked.
383 void OpenMenuImpl(MenuItemView
* item
, bool show
);
385 // Invoked when the children of a menu change and the menu is showing.
386 // This closes any submenus and resizes the submenu.
387 void MenuChildrenChanged(MenuItemView
* item
);
389 // Builds the paths of the two menu items into the two paths, and
390 // sets first_diff_at to the location of the first difference between the
392 void BuildPathsAndCalculateDiff(MenuItemView
* old_item
,
393 MenuItemView
* new_item
,
394 std::vector
<MenuItemView
*>* old_path
,
395 std::vector
<MenuItemView
*>* new_path
,
396 size_t* first_diff_at
);
398 // Builds the path for the specified item.
399 void BuildMenuItemPath(MenuItemView
* item
, std::vector
<MenuItemView
*>* path
);
401 // Starts/stops the timer that commits the pending state to state
402 // (opens/closes submenus).
403 void StartShowTimer();
404 void StopShowTimer();
406 // Starts/stops the timer cancel the menu. This is used during drag and
407 // drop when the drop enters/exits the menu.
408 void StartCancelAllTimer();
409 void StopCancelAllTimer();
411 // Calculates the bounds of the menu to show. is_leading is set to match the
412 // direction the menu opened in.
413 gfx::Rect
CalculateMenuBounds(MenuItemView
* item
,
417 // Calculates the bubble bounds of the menu to show. is_leading is set to
418 // match the direction the menu opened in.
419 gfx::Rect
CalculateBubbleMenuBounds(MenuItemView
* item
,
423 // Returns the depth of the menu.
424 static int MenuDepth(MenuItemView
* item
);
426 // Selects the next/previous menu item.
427 void IncrementSelection(int delta
);
429 // Returns the next selectable child menu item of |parent| starting at |index|
430 // and incrementing index by |delta|. If there are no more selected menu items
432 MenuItemView
* FindNextSelectableMenuItem(MenuItemView
* parent
,
436 // If the selected item has a submenu and it isn't currently open, the
437 // the selection is changed such that the menu opens immediately.
438 void OpenSubmenuChangeSelectionIfCan();
440 // If possible, closes the submenu.
443 // Returns details about which menu items match the mnemonic |key|.
444 // |match_function| is used to determine which menus match.
445 SelectByCharDetails
FindChildForMnemonic(
446 MenuItemView
* parent
,
448 bool (*match_function
)(MenuItemView
* menu
, base::char16 mnemonic
));
450 // Selects or accepts the appropriate menu item based on |details|. Returns
451 // true if |Accept| was invoked (which happens if there aren't multiple item
452 // with the same mnemonic and the item to select does not have a submenu).
453 bool AcceptOrSelect(MenuItemView
* parent
, const SelectByCharDetails
& details
);
455 // Selects by mnemonic, and if that doesn't work tries the first character of
456 // the title. Returns true if a match was selected and the menu should exit.
457 bool SelectByChar(base::char16 key
);
459 // For Windows and Aura we repost an event for some events that dismiss
460 // the context menu. The event is then reprocessed to cause its result
461 // if the context menu had not been present.
462 // On non-aura Windows, a new mouse event is generated and posted to
463 // the window (if there is one) at the location of the event. On
464 // aura, the event is reposted on the RootWindow.
465 void RepostEvent(SubmenuView
* source
, const ui::LocatedEvent
& event
);
467 // Sets the drop target to new_item.
468 void SetDropMenuItem(MenuItemView
* new_item
,
469 MenuDelegate::DropPosition position
);
471 // Starts/stops scrolling as appropriate. part gives the part the mouse is
473 void UpdateScrolling(const MenuPart
& part
);
476 void StopScrolling();
478 // Updates active mouse view from the location of the event and sends it
479 // the appropriate events. This is used to send mouse events to child views so
480 // that they react to click-drag-release as if the user clicked on the view
482 void UpdateActiveMouseView(SubmenuView
* event_source
,
483 const ui::MouseEvent
& event
,
486 // Sends a mouse release event to the current active mouse view and sets
488 void SendMouseReleaseToActiveView(SubmenuView
* event_source
,
489 const ui::MouseEvent
& event
);
491 // Sends a mouse capture lost event to the current active mouse view and sets
493 void SendMouseCaptureLostToActiveView();
495 // Sets/gets the active mouse view. See UpdateActiveMouseView() for details.
496 void SetActiveMouseView(View
* view
);
497 View
* GetActiveMouseView();
499 // Sets exit type. Calling this can terminate the active nested message-loop.
500 void SetExitType(ExitType type
);
502 // Terminates the current nested message-loop.
503 void TerminateNestedMessageLoop();
505 // Returns true if SetExitType() should quit the message loop.
506 bool ShouldQuitNow() const;
508 // Handles the mouse location event on the submenu |source|.
509 void HandleMouseLocation(SubmenuView
* source
,
510 const gfx::Point
& mouse_location
);
512 // Retrieve an appropriate Screen.
513 gfx::Screen
* GetScreen();
515 // The active instance.
516 static MenuController
* active_instance_
;
518 // If true, Run blocks. If false, Run doesn't block and this is used for
519 // drag and drop. Note that the semantics for drag and drop are slightly
520 // different: cancel timer is kicked off any time the drag moves outside the
521 // menu, mouse events do nothing...
524 // If true, we're showing.
527 // Indicates what to exit.
530 // Whether we did a capture. We do a capture only if we're blocking and
531 // the mouse was down when Run.
534 // As the user drags the mouse around pending_state_ changes immediately.
535 // When the user stops moving/dragging the mouse (or clicks the mouse)
536 // pending_state_ is committed to state_, potentially resulting in
537 // opening or closing submenus. This gives a slight delayed effect to
538 // submenus as the user moves the mouse around. This is done so that as the
539 // user moves the mouse all submenus don't immediately pop.
540 State pending_state_
;
543 // If the user accepted the selection, this is the result.
544 MenuItemView
* result_
;
546 // The event flags when the user selected the menu.
547 int accept_event_flags_
;
549 // If not empty, it means we're nested. When Run is invoked from within
550 // Run, the current state (state_) is pushed onto menu_stack_. This allows
551 // MenuController to restore the state when the nested run returns.
552 std::list
<State
> menu_stack_
;
554 // As the mouse moves around submenus are not opened immediately. Instead
555 // they open after this timer fires.
556 base::OneShotTimer
<MenuController
> show_timer_
;
558 // Used to invoke CancelAll(). This is used during drag and drop to hide the
559 // menu after the mouse moves out of the of the menu. This is necessitated by
560 // the lack of an ability to detect when the drag has completed from the drop
562 base::OneShotTimer
<MenuController
> cancel_all_timer_
;
565 MenuItemView
* drop_target_
;
566 MenuDelegate::DropPosition drop_position_
;
568 // Owner of child windows.
569 // WARNING: this may be NULL.
572 // Indicates a possible drag operation.
575 // True when drag operation is in progress.
576 bool drag_in_progress_
;
578 // True when the drag operation in progress was initiated by the
579 // MenuController for a child MenuItemView (as opposed to initiated separately
581 bool did_initiate_drag_
;
583 // Location the mouse was pressed at. Used to detect d&d.
584 gfx::Point press_pt_
;
586 // We get a slew of drag updated messages as the mouse is over us. To avoid
587 // continually processing whether we can drop, we cache the coordinates.
588 bool valid_drop_coordinates_
;
590 int last_drop_operation_
;
592 // If true, we're in the middle of invoking ShowAt on a submenu.
593 bool showing_submenu_
;
595 // Task for scrolling the menu. If non-null indicates a scroll is currently
597 scoped_ptr
<MenuScrollTask
> scroll_task_
;
599 MenuButton
* menu_button_
;
601 // ViewStorage id used to store the view mouse drag events are forwarded to.
602 // See UpdateActiveMouseView() for details.
603 const int active_mouse_view_id_
;
605 internal::MenuControllerDelegate
* delegate_
;
607 // How deep we are in nested message loops. This should be at most 2 (when
608 // showing a context menu from a menu).
609 int message_loop_depth_
;
611 views::MenuConfig menu_config_
;
613 // The timestamp of the event which closed the menu - or 0 otherwise.
614 base::TimeDelta closing_event_time_
;
616 // Time when the menu is first shown.
617 base::TimeTicks menu_start_time_
;
619 // If a mouse press triggered this menu, this will have its location (in
620 // screen coordinates). Otherwise this will be (0, 0).
621 gfx::Point menu_start_mouse_press_loc_
;
623 // Controls behavior differences between a combobox and other types of menu
624 // (like a context menu).
627 // Set to true if the menu item was selected by touch.
628 bool item_selected_by_touch_
;
630 scoped_ptr
<MenuMessageLoop
> message_loop_
;
632 DISALLOW_COPY_AND_ASSIGN(MenuController
);
637 #endif // UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_