// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef UI_VIEWS_FOCUS_FOCUS_MANAGER_H_ #define UI_VIEWS_FOCUS_FOCUS_MANAGER_H_ #include <list> #include <map> #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/accelerators/accelerator_manager.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" // The FocusManager class is used to handle focus traversal, store/restore // focused views and handle keyboard accelerators. // // There are 2 types of focus: // - the native focus, which is the focus that an gfx::NativeView has. // - the view focus, which is the focus that a views::View has. // // Each native view must register with their Focus Manager so the focus manager // gets notified when they are focused (and keeps track of the native focus) and // as well so that the tab key events can be intercepted. // They can provide when they register a View that is kept in synch in term of // focus. This is used in NativeControl for example, where a View wraps an // actual native window. // This is already done for you if you subclass the NativeControl class or if // you use the NativeViewHost class. // // When creating a top window (derived from views::Widget) that is not a child // window, it creates and owns a FocusManager to manage the focus for itself and // all its child windows. // // The FocusTraversable interface exposes the methods a class should implement // in order to be able to be focus traversed when tab key is pressed. // RootViews implement FocusTraversable. // The FocusManager contains a top FocusTraversable instance, which is the top // RootView. // // If you just use views, then the focus traversal is handled for you by the // RootView. The default traversal order is the order in which the views have // been added to their container. You can modify this order by using the View // method SetNextFocusableView(). // // If you are embedding a native view containing a nested RootView (for example // by adding a NativeControl that contains a NativeWidgetWin as its native // component), then you need to: // - override the View::GetFocusTraversable() method in your outer component. // It should return the RootView of the inner component. This is used when // the focus traversal traverse down the focus hierarchy to enter the nested // RootView. In the example mentioned above, the NativeControl overrides // GetFocusTraversable() and returns hwnd_view_container_->GetRootView(). // - call Widget::SetFocusTraversableParent() on the nested RootView and point // it to the outer RootView. This is used when the focus goes out of the // nested RootView. In the example: // hwnd_view_container_->GetWidget()->SetFocusTraversableParent( // native_control->GetRootView()); // - call RootView::SetFocusTraversableParentView() on the nested RootView with // the parent view that directly contains the native window. This is needed // when traversing up from the nested RootView to know which view to start // with when going to the next/previous view. // In our example: // hwnd_view_container_->GetWidget()->SetFocusTraversableParent( // native_control); // // Note that FocusTraversable do not have to be RootViews: AccessibleToolbarView // is FocusTraversable. namespace ui { class AcceleratorTarget; class AcceleratorManager; class EventHandler; class KeyEvent; } namespace views { class FocusManagerDelegate; class FocusSearch; class RootView; class View; class Widget; // The FocusTraversable interface is used by components that want to process // focus traversal events (due to Tab/Shift-Tab key events). class VIEWS_EXPORT FocusTraversable { public: // Return a FocusSearch object that implements the algorithm to find // the next or previous focusable view. virtual FocusSearch* GetFocusSearch() = 0; // Should return the parent FocusTraversable. // The top RootView which is the top FocusTraversable returns NULL. virtual FocusTraversable* GetFocusTraversableParent() = 0; // This should return the View this FocusTraversable belongs to. // It is used when walking up the view hierarchy tree to find which view // should be used as the starting view for finding the next/previous view. virtual View* GetFocusTraversableParentView() = 0; protected: virtual ~FocusTraversable() {} }; // This interface should be implemented by classes that want to be notified when // the focus is about to change. See the Add/RemoveFocusChangeListener methods. class VIEWS_EXPORT FocusChangeListener { public: // No change to focus state has occurred yet when this function is called. virtual void OnWillChangeFocus(View* focused_before, View* focused_now) = 0; // Called after focus state has changed. virtual void OnDidChangeFocus(View* focused_before, View* focused_now) = 0; protected: virtual ~FocusChangeListener() {} }; class VIEWS_EXPORT FocusManager { public: // The reason why the focus changed. enum FocusChangeReason { // The focus changed because the user traversed focusable views using // keys like Tab or Shift+Tab. kReasonFocusTraversal, // The focus changed due to restoring the focus. kReasonFocusRestore, // The focus changed due to a click or a shortcut to jump directly to // a particular view. kReasonDirectFocusChange }; // TODO: use Direction in place of bool reverse throughout. enum Direction { kForward, kBackward }; enum FocusCycleWrappingBehavior { kWrap, kNoWrap }; FocusManager(Widget* widget, FocusManagerDelegate* delegate); virtual ~FocusManager(); // Processes the passed key event for accelerators and keyboard traversal. // Returns false if the event has been consumed and should not be processed // further. bool OnKeyEvent(const ui::KeyEvent& event); // Returns true is the specified is part of the hierarchy of the window // associated with this FocusManager. bool ContainsView(View* view); // Advances the focus (backward if reverse is true). void AdvanceFocus(bool reverse); // The FocusManager keeps track of the focused view within a RootView. View* GetFocusedView() { return focused_view_; } const View* GetFocusedView() const { return focused_view_; } // Low-level methods to force the focus to change (and optionally provide // a reason). If the focus change should only happen if the view is // currenty focusable, enabled, and visible, call view->RequestFocus(). void SetFocusedViewWithReason(View* view, FocusChangeReason reason); void SetFocusedView(View* view) { SetFocusedViewWithReason(view, kReasonDirectFocusChange); } // Get the reason why the focus most recently changed. FocusChangeReason focus_change_reason() const { return focus_change_reason_; } // Clears the focused view. The window associated with the top root view gets // the native focus (so we still get keyboard events). void ClearFocus(); // Validates the focused view, clearing it if the window it belongs too is not // attached to the window hierarchy anymore. void ValidateFocusedView(); // Stores the focused view. Used when the widget loses activation. // |clear_native_focus| indicates whether this should invoke ClearFocus(). // Typically |true| should be passed in. void StoreFocusedView(bool clear_native_focus); // Restore the view saved with a previous call to StoreFocusedView(). Used // when the widget becomes active. Returns true when the previous view was // successfully refocused - otherwise false. bool RestoreFocusedView(); // Sets the |view| to be restored when calling RestoreFocusView. This is used // to set where the focus should go on restoring a Window created without // focus being set. void SetStoredFocusView(View* view); // Returns the View that either currently has focus, or if no view has focus // the view that last had focus. View* GetStoredFocusView(); // Clears the stored focused view. void ClearStoredFocusedView(); // Returns true if in the process of changing the focused view. bool is_changing_focus() const { return is_changing_focus_; } // Disable shortcut handling. static void set_shortcut_handling_suspended(bool suspended) { shortcut_handling_suspended_ = suspended; } // Returns whether shortcut handling is currently suspended. bool shortcut_handling_suspended() { return shortcut_handling_suspended_; } // Register a keyboard accelerator for the specified target. If multiple // targets are registered for an accelerator, a target registered later has // higher priority. // |accelerator| is the accelerator to register. // |priority| denotes the priority of the handler. // NOTE: In almost all cases, you should specify kNormalPriority for this // parameter. Setting it to kHighPriority prevents Chrome from sending the // shortcut to the webpage if the renderer has focus, which is not desirable // except for very isolated cases. // |target| is the AcceleratorTarget that handles the event once the // accelerator is pressed. // Note that we are currently limited to accelerators that are either: // - a key combination including Ctrl or Alt // - the escape key // - the enter key // - any F key (F1, F2, F3 ...) // - any browser specific keys (as available on special keyboards) void RegisterAccelerator(const ui::Accelerator& accelerator, ui::AcceleratorManager::HandlerPriority priority, ui::AcceleratorTarget* target); // Unregister the specified keyboard accelerator for the specified target. void UnregisterAccelerator(const ui::Accelerator& accelerator, ui::AcceleratorTarget* target); // Unregister all keyboard accelerator for the specified target. void UnregisterAccelerators(ui::AcceleratorTarget* target); // Activate the target associated with the specified accelerator. // First, AcceleratorPressed handler of the most recently registered target // is called, and if that handler processes the event (i.e. returns true), // this method immediately returns. If not, we do the same thing on the next // target, and so on. // Returns true if an accelerator was activated. bool ProcessAccelerator(const ui::Accelerator& accelerator); // Resets menu key state if |event| is not menu key release. // This is effective only on x11. void MaybeResetMenuKeyState(const ui::KeyEvent& key); // Called by a RootView when a view within its hierarchy is removed // from its parent. This will only be called by a RootView in a // hierarchy of Widgets that this FocusManager is attached to the // parent Widget of. void ViewRemoved(View* removed); // Adds/removes a listener. The FocusChangeListener is notified every time // the focused view is about to change. void AddFocusChangeListener(FocusChangeListener* listener); void RemoveFocusChangeListener(FocusChangeListener* listener); // Returns the AcceleratorTarget that should be activated for the specified // keyboard accelerator, or NULL if no view is registered for that keyboard // accelerator. ui::AcceleratorTarget* GetCurrentTargetForAccelerator( const ui::Accelerator& accelertor) const; // Whether the given |accelerator| has a priority handler associated with it. bool HasPriorityHandler(const ui::Accelerator& accelerator) const; // Clears the native view having the focus. virtual void ClearNativeFocus(); // Focuses the next keyboard-accessible pane, taken from the list of // views returned by WidgetDelegate::GetAccessiblePanes(). If there are // no panes, the widget's root view is treated as a single pane. // A keyboard-accessible pane should subclass from AccessiblePaneView in // order to trap keyboard focus within that pane. If |wrap| is kWrap, // it keeps cycling within this widget, otherwise it returns false after // reaching the last pane so that focus can cycle to another widget. bool RotatePaneFocus(Direction direction, FocusCycleWrappingBehavior wrap); // Convenience method that returns true if the passed |key_event| should // trigger tab traversal (if it is a TAB key press with or without SHIFT // pressed). static bool IsTabTraversalKeyEvent(const ui::KeyEvent& key_event); // Sets whether arrow key traversal is enabled. When enabled, right/down key // behaves like tab and left/up key behaves like shift-tab. Note when this // is enabled, the arrow key movement within grouped views are disabled. static void set_arrow_key_traversal_enabled(bool enabled) { arrow_key_traversal_enabled_ = enabled; } // Returns whether arrow key traversal is enabled. static bool arrow_key_traversal_enabled() { return arrow_key_traversal_enabled_; } // Returns the next focusable view. Traversal starts at |starting_view|. If // |starting_view| is NULL |starting_widget| is consuled to determine which // Widget to start from. See // WidgetDelegate::ShouldAdvanceFocusToTopLevelWidget() for details. If both // |starting_view| and |starting_widget| are NULL, traversal starts at // |widget_|. View* GetNextFocusableView(View* starting_view, Widget* starting_widget, bool reverse, bool dont_loop); private: // Returns the focusable view found in the FocusTraversable specified starting // at the specified view. This traverses down along the FocusTraversable // hierarchy. // Returns NULL if no focusable view were found. View* FindFocusableView(FocusTraversable* focus_traversable, View* starting_view, bool reverse); // Process arrow key traversal. Returns true if the event has been consumed // and should not be processed further. bool ProcessArrowKeyTraversal(const ui::KeyEvent& event); // Keeps track of whether shortcut handling is currently suspended. static bool shortcut_handling_suspended_; // Whether arrow key traversal is enabled. static bool arrow_key_traversal_enabled_; // The top-level Widget this FocusManager is associated with. Widget* widget_; // The object which handles an accelerator when |accelerator_manager_| doesn't // handle it. scoped_ptr<FocusManagerDelegate> delegate_; // The view that currently is focused. View* focused_view_; // The AcceleratorManager this FocusManager is associated with. scoped_ptr<ui::AcceleratorManager> accelerator_manager_; // The storage id used in the ViewStorage to store/restore the view that last // had focus. int stored_focused_view_storage_id_; // The reason why the focus most recently changed. FocusChangeReason focus_change_reason_; // The list of registered FocusChange listeners. ObserverList<FocusChangeListener, true> focus_change_listeners_; // See description above getter. bool is_changing_focus_; DISALLOW_COPY_AND_ASSIGN(FocusManager); }; } // namespace views #endif // UI_VIEWS_FOCUS_FOCUS_MANAGER_H_