• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #ifndef UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_
6 #define UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_
7 
8 #include "build/build_config.h"
9 
10 #include <list>
11 #include <set>
12 #include <vector>
13 
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"
23 
24 namespace base {
25 class MessagePumpDispatcher;
26 }
27 namespace gfx {
28 class Screen;
29 }
30 namespace ui {
31 class NativeTheme;
32 class OSExchangeData;
33 class ScopedEventDispatcher;
34 }
35 namespace views {
36 
37 class MenuButton;
38 class MenuHostRootView;
39 class MenuItemView;
40 class MenuMessageLoop;
41 class MouseEvent;
42 class SubmenuView;
43 class View;
44 
45 namespace internal {
46 class MenuControllerDelegate;
47 class MenuEventDispatcher;
48 class MenuMessagePumpDispatcher;
49 class MenuRunnerImpl;
50 }
51 
52 // MenuController -------------------------------------------------------------
53 
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 {
58  public:
59   // Enumeration of how the menu should exit.
60   enum ExitType {
61     // Don't exit.
62     EXIT_NONE,
63 
64     // All menus, including nested, should be exited.
65     EXIT_ALL,
66 
67     // Only the outermost menu should be exited.
68     EXIT_OUTERMOST,
69 
70     // This is set if the menu is being closed as the result of one of the menus
71     // being destroyed.
72     EXIT_DESTROYED
73   };
74 
75   // If a menu is currently active, this returns the controller for it.
76   static MenuController* GetActiveInstance();
77 
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,
82                     MenuButton* button,
83                     MenuItemView* root,
84                     const gfx::Rect& bounds,
85                     MenuAnchorPosition position,
86                     bool context_menu,
87                     int* event_flags);
88 
89   // Whether or not Run blocks.
IsBlockingRun()90   bool IsBlockingRun() const { return blocking_run_; }
91 
92   // Whether or not drag operation is in progress.
drag_in_progress()93   bool drag_in_progress() const { return drag_in_progress_; }
94 
95   // Returns the owner of child windows.
96   // WARNING: this may be NULL.
owner()97   Widget* owner() { return owner_; }
98 
99   // Get the anchor position wich is used to show this menu.
GetAnchorPosition()100   MenuAnchorPosition GetAnchorPosition() { return state_.anchor; }
101 
102   // Cancels the current Run. See ExitType for a description of what happens
103   // with the various parameters.
104   void Cancel(ExitType type);
105 
106   // An alternative to Cancel(EXIT_ALL) that can be used with a OneShotTimer.
CancelAll()107   void CancelAll() { Cancel(EXIT_ALL); }
108 
109   // Returns the current exit type. This returns a value other than EXIT_NONE if
110   // the menu is being canceled.
exit_type()111   ExitType exit_type() const { return exit_type_; }
112 
113   // Returns the time from the event which closed the menu - or 0.
closing_event_time()114   base::TimeDelta closing_event_time() const { return closing_event_time_; }
115 
set_is_combobox(bool is_combobox)116   void set_is_combobox(bool is_combobox) { is_combobox_ = is_combobox; }
117 
118   // Various events, forwarded from the submenu.
119   //
120   // NOTE: the coordinates of the events are in that of the
121   // MenuScrollViewContainer.
122   void OnMousePressed(SubmenuView* source, const ui::MouseEvent& event);
123   void OnMouseDragged(SubmenuView* source, const ui::MouseEvent& event);
124   void OnMouseReleased(SubmenuView* source, const ui::MouseEvent& event);
125   void OnMouseMoved(SubmenuView* source, const ui::MouseEvent& event);
126   void OnMouseEntered(SubmenuView* source, const ui::MouseEvent& event);
127   bool OnMouseWheel(SubmenuView* source, const ui::MouseWheelEvent& event);
128   void OnGestureEvent(SubmenuView* source, ui::GestureEvent* event);
129 
130   bool GetDropFormats(
131       SubmenuView* source,
132       int* formats,
133       std::set<ui::OSExchangeData::CustomFormat>* custom_formats);
134   bool AreDropTypesRequired(SubmenuView* source);
135   bool CanDrop(SubmenuView* source, const ui::OSExchangeData& data);
136   void OnDragEntered(SubmenuView* source, const ui::DropTargetEvent& event);
137   int OnDragUpdated(SubmenuView* source, const ui::DropTargetEvent& event);
138   void OnDragExited(SubmenuView* source);
139   int OnPerformDrop(SubmenuView* source, const ui::DropTargetEvent& event);
140 
141   // Invoked from the scroll buttons of the MenuScrollViewContainer.
142   void OnDragEnteredScrollButton(SubmenuView* source, bool is_up);
143   void OnDragExitedScrollButton(SubmenuView* source);
144 
145   // Update the submenu's selection based on the current mouse location
146   void UpdateSubmenuSelection(SubmenuView* source);
147 
148   // WidgetObserver overrides:
149   virtual void OnWidgetDestroying(Widget* widget) OVERRIDE;
150 
151   // Only used for testing.
152   static void TurnOffMenuSelectionHoldForTest();
153 
154  private:
155   friend class internal::MenuEventDispatcher;
156   friend class internal::MenuMessagePumpDispatcher;
157   friend class internal::MenuRunnerImpl;
158   friend class MenuControllerTest;
159   friend class MenuHostRootView;
160   friend class MenuItemView;
161   friend class SubmenuView;
162 
163   class MenuScrollTask;
164 
165   struct SelectByCharDetails;
166 
167   // Values supplied to SetSelection.
168   enum SetSelectionTypes {
169     SELECTION_DEFAULT               = 0,
170 
171     // If set submenus are opened immediately, otherwise submenus are only
172     // openned after a timer fires.
173     SELECTION_UPDATE_IMMEDIATELY    = 1 << 0,
174 
175     // If set and the menu_item has a submenu, the submenu is shown.
176     SELECTION_OPEN_SUBMENU          = 1 << 1,
177 
178     // SetSelection is being invoked as the result exiting or cancelling the
179     // menu. This is used for debugging.
180     SELECTION_EXIT                  = 1 << 2,
181   };
182 
183   // Result type for SendAcceleratorToHotTrackedView
184   enum SendAcceleratorResultType {
185     // Accelerator is not sent because of no hot tracked views.
186     ACCELERATOR_NOT_PROCESSED,
187 
188     // Accelerator is sent to the hot tracked views.
189     ACCELERATOR_PROCESSED,
190 
191     // Same as above and the accelerator causes the exit of the menu.
192     ACCELERATOR_PROCESSED_EXIT
193   };
194 
195   // Tracks selection information.
196   struct State {
197     State();
198     ~State();
199 
200     // The selected menu item.
201     MenuItemView* item;
202 
203     // If item has a submenu this indicates if the submenu is showing.
204     bool submenu_open;
205 
206     // Bounds passed to the run menu. Used for positioning the first menu.
207     gfx::Rect initial_bounds;
208 
209     // Position of the initial menu.
210     MenuAnchorPosition anchor;
211 
212     // The direction child menus have opened in.
213     std::list<bool> open_leading;
214 
215     // Bounds for the monitor we're showing on.
216     gfx::Rect monitor_bounds;
217 
218     // Is the current menu a context menu.
219     bool context_menu;
220   };
221 
222   // Used by GetMenuPart to indicate the menu part at a particular location.
223   struct MenuPart {
224     // Type of part.
225     enum Type {
226       NONE,
227       MENU_ITEM,
228       SCROLL_UP,
229       SCROLL_DOWN
230     };
231 
MenuPartMenuPart232     MenuPart() : type(NONE), menu(NULL), parent(NULL), submenu(NULL) {}
233 
234     // Convenience for testing type == SCROLL_DOWN or type == SCROLL_UP.
is_scrollMenuPart235     bool is_scroll() const { return type == SCROLL_DOWN || type == SCROLL_UP; }
236 
237     // Type of part.
238     Type type;
239 
240     // If type is MENU_ITEM, this is the menu item the mouse is over, otherwise
241     // this is NULL.
242     // NOTE: if type is MENU_ITEM and the mouse is not over a valid menu item
243     //       but is over a menu (for example, the mouse is over a separator or
244     //       empty menu), this is NULL and parent is the menu the mouse was
245     //       clicked on.
246     MenuItemView* menu;
247 
248     // If type is MENU_ITEM but the mouse is not over a menu item this is the
249     // parent of the menu item the user clicked on. Otherwise this is NULL.
250     MenuItemView* parent;
251 
252     // This is the submenu the mouse is over.
253     SubmenuView* submenu;
254   };
255 
256   // Sets the selection to |menu_item|. A value of NULL unselects
257   // everything. |types| is a bitmask of |SetSelectionTypes|.
258   //
259   // Internally this updates pending_state_ immediatley. state_ is only updated
260   // immediately if SELECTION_UPDATE_IMMEDIATELY is set. If
261   // SELECTION_UPDATE_IMMEDIATELY is not set CommitPendingSelection is invoked
262   // to show/hide submenus and update state_.
263   void SetSelection(MenuItemView* menu_item, int types);
264 
265   void SetSelectionOnPointerDown(SubmenuView* source,
266                                  const ui::LocatedEvent& event);
267   void StartDrag(SubmenuView* source, const gfx::Point& location);
268 
269   // Key processing. The return value of this is returned from Dispatch.
270   // In other words, if this returns false (which happens if escape was
271   // pressed, or a matching mnemonic was found) the message loop returns.
272   bool OnKeyDown(ui::KeyboardCode key_code);
273 
274   // Creates a MenuController. If |blocking| is true a nested message loop is
275   // started in |Run|.
276   MenuController(ui::NativeTheme* theme,
277                  bool blocking,
278                  internal::MenuControllerDelegate* delegate);
279 
280   virtual ~MenuController();
281 
282   // Runs the platform specific bits of the message loop. If |nested_menu| is
283   // true we're being asked to run a menu from within a menu (eg a context
284   // menu).
285   void RunMessageLoop(bool nested_menu);
286 
287   // AcceleratorPressed is invoked on the hot tracked view if it exists.
288   SendAcceleratorResultType SendAcceleratorToHotTrackedView();
289 
290   void UpdateInitialLocation(const gfx::Rect& bounds,
291                              MenuAnchorPosition position,
292                              bool context_menu);
293 
294   // Invoked when the user accepts the selected item. This is only used
295   // when blocking. This schedules the loop to quit.
296   void Accept(MenuItemView* item, int event_flags);
297 
298   bool ShowSiblingMenu(SubmenuView* source, const gfx::Point& mouse_location);
299 
300   // Shows a context menu for |menu_item| as a result of a located event if
301   // appropriate. This is invoked on long press and releasing the right mouse
302   // button. Returns whether a context menu was shown.
303   bool ShowContextMenu(MenuItemView* menu_item,
304                        SubmenuView* source,
305                        const ui::LocatedEvent& event,
306                        ui::MenuSourceType source_type);
307 
308   // Closes all menus, including any menus of nested invocations of Run.
309   void CloseAllNestedMenus();
310 
311   // Gets the enabled menu item at the specified location.
312   // If over_any_menu is non-null it is set to indicate whether the location
313   // is over any menu. It is possible for this to return NULL, but
314   // over_any_menu to be true. For example, the user clicked on a separator.
315   MenuItemView* GetMenuItemAt(View* menu, int x, int y);
316 
317   // If there is an empty menu item at the specified location, it is returned.
318   MenuItemView* GetEmptyMenuItemAt(View* source, int x, int y);
319 
320   // Returns true if the coordinate is over the scroll buttons of the
321   // SubmenuView's MenuScrollViewContainer. If true is returned, part is set to
322   // indicate which scroll button the coordinate is.
323   bool IsScrollButtonAt(SubmenuView* source,
324                         int x,
325                         int y,
326                         MenuPart::Type* part);
327 
328   // Returns the target for the mouse event. The coordinates are in terms of
329   // source's scroll view container.
330   MenuPart GetMenuPart(SubmenuView* source, const gfx::Point& source_loc);
331 
332   // Returns the target for mouse events. The search is done through |item| and
333   // all its parents.
334   MenuPart GetMenuPartByScreenCoordinateUsingMenu(MenuItemView* item,
335                                                   const gfx::Point& screen_loc);
336 
337   // Implementation of GetMenuPartByScreenCoordinate for a single menu. Returns
338   // true if the supplied SubmenuView contains the location in terms of the
339   // screen. If it does, part is set appropriately and true is returned.
340   bool GetMenuPartByScreenCoordinateImpl(SubmenuView* menu,
341                                          const gfx::Point& screen_loc,
342                                          MenuPart* part);
343 
344   // Returns true if the SubmenuView contains the specified location. This does
345   // NOT included the scroll buttons, only the submenu view.
346   bool DoesSubmenuContainLocation(SubmenuView* submenu,
347                                   const gfx::Point& screen_loc);
348 
349   // Opens/Closes the necessary menus such that state_ matches that of
350   // pending_state_. This is invoked if submenus are not opened immediately,
351   // but after a delay.
352   void CommitPendingSelection();
353 
354   // If item has a submenu, it is closed. This does NOT update the selection
355   // in anyway.
356   void CloseMenu(MenuItemView* item);
357 
358   // If item has a submenu, it is opened. This does NOT update the selection
359   // in anyway.
360   void OpenMenu(MenuItemView* item);
361 
362   // Implementation of OpenMenu. If |show| is true, this invokes show on the
363   // menu, otherwise Reposition is invoked.
364   void OpenMenuImpl(MenuItemView* item, bool show);
365 
366   // Invoked when the children of a menu change and the menu is showing.
367   // This closes any submenus and resizes the submenu.
368   void MenuChildrenChanged(MenuItemView* item);
369 
370   // Builds the paths of the two menu items into the two paths, and
371   // sets first_diff_at to the location of the first difference between the
372   // two paths.
373   void BuildPathsAndCalculateDiff(MenuItemView* old_item,
374                                   MenuItemView* new_item,
375                                   std::vector<MenuItemView*>* old_path,
376                                   std::vector<MenuItemView*>* new_path,
377                                   size_t* first_diff_at);
378 
379   // Builds the path for the specified item.
380   void BuildMenuItemPath(MenuItemView* item, std::vector<MenuItemView*>* path);
381 
382   // Starts/stops the timer that commits the pending state to state
383   // (opens/closes submenus).
384   void StartShowTimer();
385   void StopShowTimer();
386 
387   // Starts/stops the timer cancel the menu. This is used during drag and
388   // drop when the drop enters/exits the menu.
389   void StartCancelAllTimer();
390   void StopCancelAllTimer();
391 
392   // Calculates the bounds of the menu to show. is_leading is set to match the
393   // direction the menu opened in.
394   gfx::Rect CalculateMenuBounds(MenuItemView* item,
395                                 bool prefer_leading,
396                                 bool* is_leading);
397 
398   // Calculates the bubble bounds of the menu to show. is_leading is set to
399   // match the direction the menu opened in.
400   gfx::Rect CalculateBubbleMenuBounds(MenuItemView* item,
401                                       bool prefer_leading,
402                                       bool* is_leading);
403 
404   // Returns the depth of the menu.
405   static int MenuDepth(MenuItemView* item);
406 
407   // Selects the next/previous menu item.
408   void IncrementSelection(int delta);
409 
410   // Returns the next selectable child menu item of |parent| starting at |index|
411   // and incrementing index by |delta|. If there are no more selected menu items
412   // NULL is returned.
413   MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent,
414                                            int index,
415                                            int delta);
416 
417   // If the selected item has a submenu and it isn't currently open, the
418   // the selection is changed such that the menu opens immediately.
419   void OpenSubmenuChangeSelectionIfCan();
420 
421   // If possible, closes the submenu.
422   void CloseSubmenu();
423 
424   // Returns details about which menu items match the mnemonic |key|.
425   // |match_function| is used to determine which menus match.
426   SelectByCharDetails FindChildForMnemonic(
427       MenuItemView* parent,
428       base::char16 key,
429       bool (*match_function)(MenuItemView* menu, base::char16 mnemonic));
430 
431   // Selects or accepts the appropriate menu item based on |details|. Returns
432   // true if |Accept| was invoked (which happens if there aren't multiple item
433   // with the same mnemonic and the item to select does not have a submenu).
434   bool AcceptOrSelect(MenuItemView* parent, const SelectByCharDetails& details);
435 
436   // Selects by mnemonic, and if that doesn't work tries the first character of
437   // the title. Returns true if a match was selected and the menu should exit.
438   bool SelectByChar(base::char16 key);
439 
440   // For Windows and Aura we repost an event for some events that dismiss
441   // the context menu. The event is then reprocessed to cause its result
442   // if the context menu had not been present.
443   // On non-aura Windows, a new mouse event is generated and posted to
444   // the window (if there is one) at the location of the event. On
445   // aura, the event is reposted on the RootWindow.
446   void RepostEvent(SubmenuView* source, const ui::LocatedEvent& event);
447 
448   // Sets the drop target to new_item.
449   void SetDropMenuItem(MenuItemView* new_item,
450                        MenuDelegate::DropPosition position);
451 
452   // Starts/stops scrolling as appropriate. part gives the part the mouse is
453   // over.
454   void UpdateScrolling(const MenuPart& part);
455 
456   // Stops scrolling.
457   void StopScrolling();
458 
459   // Updates active mouse view from the location of the event and sends it
460   // the appropriate events. This is used to send mouse events to child views so
461   // that they react to click-drag-release as if the user clicked on the view
462   // itself.
463   void UpdateActiveMouseView(SubmenuView* event_source,
464                              const ui::MouseEvent& event,
465                              View* target_menu);
466 
467   // Sends a mouse release event to the current active mouse view and sets
468   // it to null.
469   void SendMouseReleaseToActiveView(SubmenuView* event_source,
470                                     const ui::MouseEvent& event);
471 
472   // Sends a mouse capture lost event to the current active mouse view and sets
473   // it to null.
474   void SendMouseCaptureLostToActiveView();
475 
476   // Sets/gets the active mouse view. See UpdateActiveMouseView() for details.
477   void SetActiveMouseView(View* view);
478   View* GetActiveMouseView();
479 
480   // Sets exit type. Calling this can terminate the active nested message-loop.
481   void SetExitType(ExitType type);
482 
483   // Terminates the current nested message-loop.
484   void TerminateNestedMessageLoop();
485 
486   // Returns true if SetExitType() should quit the message loop.
487   bool ShouldQuitNow() const;
488 
489   // Handles the mouse location event on the submenu |source|.
490   void HandleMouseLocation(SubmenuView* source,
491                            const gfx::Point& mouse_location);
492 
493   // Retrieve an appropriate Screen.
494   gfx::Screen* GetScreen();
495 
496   // The active instance.
497   static MenuController* active_instance_;
498 
499   // If true, Run blocks. If false, Run doesn't block and this is used for
500   // drag and drop. Note that the semantics for drag and drop are slightly
501   // different: cancel timer is kicked off any time the drag moves outside the
502   // menu, mouse events do nothing...
503   bool blocking_run_;
504 
505   // If true, we're showing.
506   bool showing_;
507 
508   // Indicates what to exit.
509   ExitType exit_type_;
510 
511   // Whether we did a capture. We do a capture only if we're blocking and
512   // the mouse was down when Run.
513   bool did_capture_;
514 
515   // As the user drags the mouse around pending_state_ changes immediately.
516   // When the user stops moving/dragging the mouse (or clicks the mouse)
517   // pending_state_ is committed to state_, potentially resulting in
518   // opening or closing submenus. This gives a slight delayed effect to
519   // submenus as the user moves the mouse around. This is done so that as the
520   // user moves the mouse all submenus don't immediately pop.
521   State pending_state_;
522   State state_;
523 
524   // If the user accepted the selection, this is the result.
525   MenuItemView* result_;
526 
527   // The event flags when the user selected the menu.
528   int accept_event_flags_;
529 
530   // If not empty, it means we're nested. When Run is invoked from within
531   // Run, the current state (state_) is pushed onto menu_stack_. This allows
532   // MenuController to restore the state when the nested run returns.
533   std::list<State> menu_stack_;
534 
535   // As the mouse moves around submenus are not opened immediately. Instead
536   // they open after this timer fires.
537   base::OneShotTimer<MenuController> show_timer_;
538 
539   // Used to invoke CancelAll(). This is used during drag and drop to hide the
540   // menu after the mouse moves out of the of the menu. This is necessitated by
541   // the lack of an ability to detect when the drag has completed from the drop
542   // side.
543   base::OneShotTimer<MenuController> cancel_all_timer_;
544 
545   // Drop target.
546   MenuItemView* drop_target_;
547   MenuDelegate::DropPosition drop_position_;
548 
549   // Owner of child windows.
550   // WARNING: this may be NULL.
551   Widget* owner_;
552 
553   // Indicates a possible drag operation.
554   bool possible_drag_;
555 
556   // True when drag operation is in progress.
557   bool drag_in_progress_;
558 
559   // Location the mouse was pressed at. Used to detect d&d.
560   gfx::Point press_pt_;
561 
562   // We get a slew of drag updated messages as the mouse is over us. To avoid
563   // continually processing whether we can drop, we cache the coordinates.
564   bool valid_drop_coordinates_;
565   gfx::Point drop_pt_;
566   int last_drop_operation_;
567 
568   // If true, we're in the middle of invoking ShowAt on a submenu.
569   bool showing_submenu_;
570 
571   // Task for scrolling the menu. If non-null indicates a scroll is currently
572   // underway.
573   scoped_ptr<MenuScrollTask> scroll_task_;
574 
575   MenuButton* menu_button_;
576 
577   // ViewStorage id used to store the view mouse drag events are forwarded to.
578   // See UpdateActiveMouseView() for details.
579   const int active_mouse_view_id_;
580 
581   internal::MenuControllerDelegate* delegate_;
582 
583   // How deep we are in nested message loops. This should be at most 2 (when
584   // showing a context menu from a menu).
585   int message_loop_depth_;
586 
587   views::MenuConfig menu_config_;
588 
589   // The timestamp of the event which closed the menu - or 0 otherwise.
590   base::TimeDelta closing_event_time_;
591 
592   // Time when the menu is first shown.
593   base::TimeTicks menu_start_time_;
594 
595   // If a mouse press triggered this menu, this will have its location (in
596   // screen coordinates). Otherwise this will be (0, 0).
597   gfx::Point menu_start_mouse_press_loc_;
598 
599   // Controls behavior differences between a combobox and other types of menu
600   // (like a context menu).
601   bool is_combobox_;
602 
603   // Set to true if the menu item was selected by touch.
604   bool item_selected_by_touch_;
605 
606   scoped_ptr<MenuMessageLoop> message_loop_;
607 
608   DISALLOW_COPY_AND_ASSIGN(MenuController);
609 };
610 
611 }  // namespace views
612 
613 #endif  // UI_VIEWS_CONTROLS_MENU_MENU_CONTROLLER_H_
614