chrome ui源码剖析-Accelerator(快捷键)

 

好久没有自己写东西了,chrome有着取之不尽的技术精华供学习,记录一下.

源码目录:

http://src.chromium.org/viewvc/chrome/trunk/src/ui/base/accelerators/

一.Accelerator 类

// This class describe a keyboard accelerator (or keyboard shortcut).
// Keyboard accelerators are registered with the FocusManager.
// It has a copy constructor and assignment operator so that it can be copied.
// It also defines the < operator so that it can be used as a key in a std::map.
//

#ifndef UI_BASE_ACCELERATORS_ACCELERATOR_H_
#define UI_BASE_ACCELERATORS_ACCELERATOR_H_

namespace ui {

class PlatformAccelerator;

// This is a cross-platform class for accelerator keys used in menus.
// |platform_accelerator| should be used to store platform specific data.
class UI_EXPORT Accelerator {
 public:
  Accelerator();
  Accelerator(ui::KeyboardCode keycode, int modifiers);
  Accelerator(const Accelerator& accelerator);
  ~Accelerator();

  ui::KeyboardCode key_code() const { return key_code_; }

  // Sets the event type if the accelerator should be processed on an event
  // other than ui::ET_KEY_PRESSED.
  void set_type(ui::EventType type) { type_ = type; }
  ui::EventType type() const { return type_; }

  int modifiers() const { return modifiers_; }

  bool IsShiftDown() const;
  bool IsCtrlDown() const;
  bool IsAltDown() const;
  bool IsCmdDown() const;

 
  protected:
  // The keycode (VK_...).
  KeyboardCode key_code_;

  // The event type (usually ui::ET_KEY_PRESSED).
  EventType type_;

  // The state of the Shift/Ctrl/Alt keys.
  int modifiers_;

  // Stores platform specific data. May be NULL.
  scoped_ptr<PlatformAccelerator> platform_accelerator_;
};

定义了快捷键属性

主要有三个属性

  1. key_code_:按下的键盘键
  2. type_:触发的事件,比如keydown,keyup
  3. modifiers_:特殊组合按钮

二.AcceleratorTarget

 

// An interface that classes that want to register for keyboard accelerators
// should implement.
class UI_EXPORT AcceleratorTarget {
 public:
  // Should return true if the accelerator was processed.
  virtual bool AcceleratorPressed(const Accelerator& accelerator) = 0;

  // Should return true if the target can handle the accelerator events. The
  // AcceleratorPressed method is invoked only for targets for which
  // CanHandleAccelerators returns true.
  virtual bool CanHandleAccelerators() const = 0;

 protected:
  virtual ~AcceleratorTarget() {}
};

注册快捷键的对象,

  1. AcceleratorPressed方法为处理快捷键事件,返回true则表明处理完毕
  2. CanHandleAccelerators方法是处理快捷键之前的状态检查

三.AcceleratorManager


#ifndef UI_BASE_ACCELERATORS_ACCELERATOR_MANAGER_H_
#define UI_BASE_ACCELERATORS_ACCELERATOR_MANAGER_H_

#include <list>
#include <map>
#include <utility>

namespace ui {

// The AcceleratorManger is used to handle keyboard accelerators.
class UI_EXPORT AcceleratorManager {
 public:
  enum HandlerPriority {
    kNormalPriority,
    kHighPriority,
  };

  AcceleratorManager();
  ~AcceleratorManager();

  // 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 Register(const Accelerator& accelerator,
                HandlerPriority priority,
                AcceleratorTarget* target);

  // Unregister the specified keyboard accelerator for the specified target.
  void Unregister(const Accelerator& accelerator, AcceleratorTarget* target);

  // Unregister all keyboard accelerator for the specified target.
  void UnregisterAll(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 Process(const Accelerator& accelerator);

  // Returns the AcceleratorTarget that should be activated for the specified
  // keyboard accelerator, or NULL if no view is registered for that keyboard
  // accelerator.
  AcceleratorTarget* GetCurrentTarget(const Accelerator& accelertor) const;

  // Whether the given |accelerator| has a priority handler associated with it.
  bool HasPriorityHandler(const Accelerator& accelerator) const;

 private:
  // The accelerators and associated targets.
  typedef std::list<AcceleratorTarget*> AcceleratorTargetList;
  // This construct pairs together a |bool| (denoting whether the list contains
  // a priority_handler at the front) with the list of AcceleratorTargets.
  typedef std::pair<bool, AcceleratorTargetList> AcceleratorTargets;
  typedef std::map<Accelerator, AcceleratorTargets> AcceleratorMap;
  AcceleratorMap accelerators_;

  DISALLOW_COPY_AND_ASSIGN(AcceleratorManager);
};

}  // namespace ui

#endif  // UI_BASE_ACCELERATORS_ACCELERATOR_MANAGER_H_

数据结构为一个Accelerator对应一个AcceleratorTargets列表,bool用来表示优先级

Process方法用来处理快捷键流程

bool AcceleratorManager::Process(const Accelerator& accelerator) {
  bool result = false;
  AcceleratorMap::iterator map_iter = accelerators_.find(accelerator);
  if (map_iter != accelerators_.end()) {
    // We have to copy the target list here, because an AcceleratorPressed
    // event handler may modify the list.
    AcceleratorTargetList targets(map_iter->second.second);
    for (AcceleratorTargetList::iterator iter = targets.begin();
         iter != targets.end(); ++iter) {
      if ((*iter)->CanHandleAccelerators() &&
          (*iter)->AcceleratorPressed(accelerator)) {
        result = true;
        break;
      }
    }
  }
  return result;
}

再来看下Register方法,新注册的快捷键都排到前面来了,这个是比较关键的

void AcceleratorManager::Register(const Accelerator& accelerator,
                                  HandlerPriority priority,
                                  AcceleratorTarget* target) {
  AcceleratorTargetList& targets = accelerators_[accelerator].second;
  DCHECK(std::find(targets.begin(), targets.end(), target) == targets.end())
      << "Registering the same target multiple times";

  // All priority accelerators go to the front of the line.
  if (priority) {
    DCHECK(!accelerators_[accelerator].first)
        << "Only one _priority_ handler can be registered";
    targets.push_front(target);
    // Mark that we have a priority accelerator at the front.
    accelerators_[accelerator].first = true;
    return;
  }

  // We are registering a normal priority handler. If no priority accelerator
  // handler has been registered before us, just add the new handler to the
  // front. Otherwise, register it after the first (only) priority handler.
  if (!accelerators_[accelerator].first)
    targets.push_front(target);
  else
    targets.insert(++targets.begin(), target);
}

测试代码:

1.注册一个快捷键

TEST_F(AcceleratorManagerTest, Register) {
  const Accelerator accelerator_a(VKEY_A, EF_NONE);
  TestTarget target;
  manager_.Register(accelerator_a, AcceleratorManager::kNormalPriority,
                    &target);

  // The registered accelerator is processed.
  EXPECT_TRUE(manager_.Process(accelerator_a));
  EXPECT_EQ(1, target.accelerator_pressed_count());
}

2.注册多个快捷键

TEST_F(AcceleratorManagerTest, RegisterMultipleTarget) {
  const Accelerator accelerator_a(VKEY_A, EF_NONE);
  TestTarget target1;
  manager_.Register(accelerator_a, AcceleratorManager::kNormalPriority,
                    &target1);
  TestTarget target2;
  manager_.Register(accelerator_a, AcceleratorManager::kNormalPriority,
                    &target2);

  // If multiple targets are registered with the same accelerator, the target
  // registered later processes the accelerator.
  EXPECT_TRUE(manager_.Process(accelerator_a));
  EXPECT_EQ(0, target1.accelerator_pressed_count());
  EXPECT_EQ(1, target2.accelerator_pressed_count());
}

image

 

简单的用以上关系来表达三者之间的关系,其实逻辑还是比较清晰的,使用map查找起来速度也比较快,不会存在什么性能上面的问题,Register方法都是push_front也保证了时效性,所以其还是满足了快捷键使用的需求,非常轻巧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值