C++程序  |  382行  |  15.29 KB

// 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 AcceleratorManager;
class AcceleratorTarget;
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_; }

  // Changes the text input focus to |view->GetTextInputClient()| iff |view|
  // is focused.  Views must call this method when their internal
  // TextInputClient instance changes.
  void OnTextInputClientChanged(View* view);

  // Moves the text input focus into/out from |view|.
  void FocusTextInputClient(View* view);
  void BlurTextInputClient(View* view);

  // 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_