1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_ACCELERATORS_ACCELERATOR_MAP_H_
#define UI_BASE_ACCELERATORS_ACCELERATOR_MAP_H_
#include <map>
#include <utility>
#include <vector>
#include "base/component_export.h"
#include "build/build_config.h"
#include "ui/base/accelerators/accelerator.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#endif
namespace ui {
// This is a wrapper around an internal std::map of type
// |std::map<Accelerator, V>| where |V| is the mapped value.
//
// Accelerators in Chrome on all platforms are specified with the |key_code|,
// aka VKEY, however certain keys (eg. brackets, comma, period, plus, minus),
// are in different places based on the keyboard layout. In some cases the
// VKEYs don't exist, in some cases they now conflict with other shortcuts.
//
// Chrome OS uses a positional mapping for this subset of keys. Shortcuts
// based on these keys are determined by the position of the key on a US
// keyboard. This was already the case for all non-latin alphabet keyboards
// (Chinese, Japanese, Arabic, Russian, etc.).
//
// To achieve this on Chrome OS for the remaining layouts, an additional
// remapping may happen to the accelerator used for lookup based on the state
// of |use_positional_lookup_|. When false no remapping occurs. When true,
// the |code| aka DomCode (which is by definition fixed position), is used to
// find the US layout VKEY, and that VKEY is used to lookup in the map.
//
// Other non-positional keys, eg. alphanumeric, F-keys, and special keys are
// all not remapped. Alphanumeric keys always continue to follow the
// |code|/VKEY defined by the current layout as is existing behavior.
template <typename V>
class COMPONENT_EXPORT(UI_BASE) AcceleratorMap {
public:
AcceleratorMap() = default;
~AcceleratorMap() = default;
using iterator = typename std::map<Accelerator, V>::iterator;
using const_iterator = typename std::map<Accelerator, V>::const_iterator;
// Lookup an accelerator and return a pointer to the value. If the
// accelerator is not in the map, this returns nullptr.
const V* Find(const Accelerator& accelerator) const {
auto iter = FindImpl(accelerator);
return iter == map_.end() ? nullptr : &iter->second;
}
V* Find(const Accelerator& accelerator) {
// Call the const implementation to avoid duplicating code.
return const_cast<V*>(
const_cast<const AcceleratorMap*>(this)->Find(accelerator));
}
// Lookup an accelerator and return a reference to the value. If the
// accelerator is not present this function will DCHECK.
const V& Get(const Accelerator& accelerator) const {
auto iter = FindImpl(accelerator);
DCHECK(iter != map_.end());
return iter->second;
}
V& Get(const Accelerator& accelerator) {
// Call the const implementation to avoid duplicating code.
return const_cast<V&>(
const_cast<const AcceleratorMap*>(this)->Get(accelerator));
}
V& GetOrInsertDefault(const Accelerator& accelerator) {
#if BUILDFLAG(IS_CHROMEOS)
// Ensure the DomCode is NONE before registering. The DomCode is only
// used during lookup to select the correct VKEY.
if (accelerator.code() != DomCode::NONE) {
Accelerator accelerator_copy = accelerator;
accelerator_copy.reset_code();
return map_[accelerator_copy];
}
#endif
return map_[accelerator];
}
// Erase an accelerator from the map.
bool Erase(const Accelerator& accelerator) {
return map_.erase(accelerator) > 0;
}
void Clear() { map_.clear(); }
// Inserts a new accelerator and value into the map. DCHECKs if the
// accelerator was already in the map.
void InsertNew(const std::pair<const Accelerator, V>& value) {
#if BUILDFLAG(IS_CHROMEOS)
// Ensure the DomCode is NONE before registering. The DomCode is only
// used during lookup to select the correct VKEY.
if (value.first.code() != DomCode::NONE) {
Accelerator accelerator_copy = value.first;
accelerator_copy.reset_code();
auto value_copy = std::make_pair(accelerator_copy, value.second);
auto result = map_.insert(value_copy);
DCHECK(result.second);
return;
}
#endif
auto result = map_.insert(value);
DCHECK(result.second);
}
// Iterators for the internal map.
iterator begin() { return map_.begin(); }
iterator end() { return map_.end(); }
// Returns the number of items in the map.
size_t size() const { return map_.size(); }
// Returns true if the map is empty.
bool empty() const { return map_.empty(); }
#if BUILDFLAG(IS_CHROMEOS)
// When true, lookup operators on the map will remap the |key_code| of
// position-based keys based on the |code|.
void set_use_positional_lookup(bool use_positional_lookup) {
use_positional_lookup_ = use_positional_lookup;
}
#endif
private:
std::map<Accelerator, V> map_;
#if BUILDFLAG(IS_CHROMEOS)
bool use_positional_lookup_ = false;
// For the shortcuts that use positional mapping, the lookup is done based
// on the |key_code| in the US layout. The supplied |code| (DomCode) is used
// to remap the layout specific |key_code| with the US layout equivalent,
// which effectively pins the location of these keys in place regardless of
// the actual key mapping. Note that this only happens for a subset of keys
// and has no overlap with alphanumeric characters or the language equivalent
// keys that map to the alphanumeric set of VKEYS.
//
// One such example is Alt ']'. In the US layout ']' is VKEY_OEM_6, in the
// DE layout it is VKEY_OEM_PLUS. However the key in that position is always
// DomCode::BRACKET_RIGHT regardless of what the key generates when pressed.
// This function uses the DomCode to remap the VKEY back to it's US layout
// equivalent.
//
// See the design doc in crbug.com/1174326 for more information.
Accelerator RemapAcceleratorForLookup(const Accelerator& accelerator) const {
KeyboardCode lookup_key_code = accelerator.key_code();
if (use_positional_lookup_) {
// If there's a valid remapping, use that |KeyboardCode| instead.
KeyboardCode remapped_key_code =
KeycodeConverter::MapPositionalDomCodeToUSShortcutKey(
accelerator.code(), accelerator.key_code());
lookup_key_code = remapped_key_code != VKEY_UNKNOWN ? remapped_key_code
: lookup_key_code;
}
return Accelerator(lookup_key_code, DomCode::NONE, accelerator.modifiers(),
accelerator.key_state());
}
#endif // BUILDFLAG(IS_CHROMEOS)
const_iterator FindImpl(const Accelerator& accelerator) const {
#if BUILDFLAG(IS_CHROMEOS)
auto iter = map_.find(RemapAcceleratorForLookup(accelerator));
// Sanity check that a DomCode was never inserted into the map.
DCHECK(iter == map_.end() || iter->first.code() == DomCode::NONE);
return iter;
#endif // BUILDFLAG(IS_CHROMEOS)
return map_.find(accelerator);
}
iterator FindImpl(const Accelerator& accelerator) {
return const_cast<V*>(
const_cast<const AcceleratorMap*>(this)->FindImpl(accelerator));
}
};
} // namespace ui
#endif // UI_BASE_ACCELERATORS_ACCELERATOR_MAP_H_
|