diff options
-rw-r--r-- | include/cru/common/Base.hpp | 13 | ||||
-rw-r--r-- | include/cru/common/Event.hpp | 4 | ||||
-rw-r--r-- | include/cru/ui/ShortcutHub.hpp | 36 | ||||
-rw-r--r-- | src/ui/ShortcutHub.cpp | 53 |
4 files changed, 98 insertions, 8 deletions
diff --git a/include/cru/common/Base.hpp b/include/cru/common/Base.hpp index a5a9421d..560f83bb 100644 --- a/include/cru/common/Base.hpp +++ b/include/cru/common/Base.hpp @@ -1,8 +1,8 @@ #pragma once #include "PreConfig.hpp" +#include <exception> #include <gsl/gsl> - #include <stdexcept> #define CRU_UNUSED(entity) static_cast<void>(entity); @@ -42,12 +42,17 @@ struct Interface { virtual ~Interface() = default; }; -[[noreturn]] inline void UnreachableCode() { - throw std::runtime_error("Unreachable code."); -} +[[noreturn]] inline void UnreachableCode() { std::terminate(); } using Index = gsl::index; +// https://www.boost.org/doc/libs/1_54_0/doc/html/hash/reference.html#boost.hash_combine +template <class T> +inline void hash_combine(std::size_t& s, const T& v) { + std::hash<T> h; + s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2); +} + #define CRU_DEFINE_CLASS_LOG_TAG(tag) \ private: \ constexpr static std::u16string_view log_tag = tag; diff --git a/include/cru/common/Event.hpp b/include/cru/common/Event.hpp index 6417bc78..93ab9b7a 100644 --- a/include/cru/common/Event.hpp +++ b/include/cru/common/Event.hpp @@ -232,6 +232,10 @@ class EventRevokerListGuard { return *this; } + void Clear() { event_revoker_guard_list_.clear(); } + + bool IsEmpty() const { return event_revoker_guard_list_.empty(); } + private: std::vector<EventRevokerGuard> event_revoker_guard_list_; }; diff --git a/include/cru/ui/ShortcutHub.hpp b/include/cru/ui/ShortcutHub.hpp index 9995a4e1..7a75e4a1 100644 --- a/include/cru/ui/ShortcutHub.hpp +++ b/include/cru/ui/ShortcutHub.hpp @@ -1,12 +1,17 @@ #pragma once #include "Base.hpp" +#include "cru/common/Base.hpp" +#include "cru/common/Event.hpp" #include "cru/platform/native/Keyboard.hpp" +#include <cstddef> #include <functional> +#include <memory> #include <optional> #include <string> #include <string_view> +#include <unordered_map> #include <vector> namespace cru::ui { @@ -42,8 +47,23 @@ class ShortcutKeyBind { platform::native::KeyCode key_; platform::native::KeyModifier modifier_; }; +} // namespace cru::ui + +namespace std { +template <> +struct hash<cru::ui::ShortcutKeyBind> { + std::size_t operator()(const cru::ui::ShortcutKeyBind& value) const { + std::size_t result = 0; + cru::hash_combine(result, value.GetKey()); + cru::hash_combine(result, value.GetModifier()); + return result; + } +}; +} // namespace std +namespace cru::ui { struct ShortcutInfo { + int id; std::u16string name; ShortcutKeyBind key_bind; std::function<bool()> handler; @@ -51,15 +71,16 @@ struct ShortcutInfo { class ShortcutHub : public Object { public: - ShortcutHub(); + ShortcutHub() = default; CRU_DELETE_COPY(ShortcutHub) CRU_DELETE_MOVE(ShortcutHub) - ~ShortcutHub() override; + ~ShortcutHub() override = default; // Handler return true if it consumes the shortcut. Or return false if it does - // not handle the shortcut. Name is just for debug. + // not handle the shortcut. Name is just for debug. Return an id used for + // unregistering. int RegisterShortcut(std::u16string name, ShortcutKeyBind bind, std::function<bool()> handler); @@ -67,12 +88,19 @@ class ShortcutHub : public Object { std::vector<ShortcutInfo> GetAllShortcuts() const; std::optional<ShortcutInfo> GetShortcut(int id) const; - std::vector<ShortcutInfo> GetShortcutByKeyBind( + const std::vector<ShortcutInfo>& GetShortcutByKeyBind( const ShortcutKeyBind& key_bind) const; void Install(Control* control); void Uninstall(); private: + std::unordered_map<ShortcutKeyBind, std::vector<ShortcutInfo>> map_; + + const std::vector<ShortcutInfo> empty_list_; + + int current_id_ = 1; + + EventRevokerListGuard event_guard_; }; } // namespace cru::ui diff --git a/src/ui/ShortcutHub.cpp b/src/ui/ShortcutHub.cpp index 2246b6eb..e5847f8c 100644 --- a/src/ui/ShortcutHub.cpp +++ b/src/ui/ShortcutHub.cpp @@ -1,5 +1,58 @@ #include "cru/ui/ShortcutHub.hpp" +#include <algorithm> +#include <iterator> +#include <optional> namespace cru::ui { +int ShortcutHub::RegisterShortcut(std::u16string name, ShortcutKeyBind bind, + std::function<bool()> handler) { + const int id = current_id_++; + ShortcutInfo info{id, std::move(name), bind, std::move(handler)}; + map_[bind].push_back(std::move(info)); + return id; +} + +void ShortcutHub::UnregisterShortcut(int id) { + if (id <= 0) return; + for (auto& pair : map_) { + auto& list = pair.second; + auto result = + std::find_if(list.cbegin(), list.cend(), + [id](const ShortcutInfo& info) { return info.id == id; }); + if (result != list.cend()) { + list.erase(result); + } + } +} + +std::vector<ShortcutInfo> ShortcutHub::GetAllShortcuts() const { + std::vector<ShortcutInfo> result; + + for (const auto& pair : map_) { + std::copy(pair.second.cbegin(), pair.second.cend(), + std::back_inserter(result)); + } + + return result; +} + +std::optional<ShortcutInfo> ShortcutHub::GetShortcut(int id) const { + for (auto& pair : map_) { + auto& list = pair.second; + auto result = + std::find_if(list.cbegin(), list.cend(), + [id](const ShortcutInfo& info) { return info.id == id; }); + if (result != list.cend()) { + return *result; + } + } + return std::nullopt; +} +const std::vector<ShortcutInfo>& ShortcutHub::GetShortcutByKeyBind( + const ShortcutKeyBind& key_bind) const { + auto result = map_.find(key_bind); + if (result != map_.cend()) return result->second; + return empty_list_; } +} // namespace cru::ui |