diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/any_map.cpp | 33 | ||||
-rw-r--r-- | src/any_map.h | 100 | ||||
-rw-r--r-- | src/application.hpp | 8 | ||||
-rw-r--r-- | src/ui/control.hpp | 33 | ||||
-rw-r--r-- | src/ui/theme.cpp | 6 | ||||
-rw-r--r-- | src/ui/theme.hpp | 8 |
6 files changed, 159 insertions, 29 deletions
diff --git a/src/any_map.cpp b/src/any_map.cpp new file mode 100644 index 00000000..6f9ada66 --- /dev/null +++ b/src/any_map.cpp @@ -0,0 +1,33 @@ +#include "any_map.h" + +namespace cru +{ + AnyMap::ListenerToken AnyMap::RegisterValueChangeListener(const String& key, const Listener& listener) + { + const auto token = current_listener_token_++; + map_[key].second.push_back(token); + listeners_.emplace(token, listener); + return token; + } + + void AnyMap::UnregisterValueChangeListener(const ListenerToken token) + { + const auto find_result = listeners_.find(token); + if (find_result != listeners_.cend()) + listeners_.erase(find_result); + } + + void AnyMap::InvokeListeners(std::list<ListenerToken>& listener_list, const std::any& value) + { + auto i = listener_list.cbegin(); + while (i != listener_list.cend()) + { + auto current_i = i++; + const auto find_result = listeners_.find(*current_i); + if (find_result != listeners_.cend()) + find_result->second(value); + else + listener_list.erase(current_i); // otherwise remove the invalid listener token. + } + } +} diff --git a/src/any_map.h b/src/any_map.h new file mode 100644 index 00000000..2dee75a6 --- /dev/null +++ b/src/any_map.h @@ -0,0 +1,100 @@ +#pragma once + +#include <any> +#include <unordered_map> +#include <functional> +#include <optional> +#include <typeinfo> + +#include "base.hpp" +#include "format.hpp" + + +namespace cru +{ + // A map with String as key and any type as value. + // It also has notification when value with specified key changed. + class AnyMap : public Object + { + public: + using ListenerToken = long; + using Listener = std::function<void(const std::any&)>; + + AnyMap() = default; + AnyMap(const AnyMap& other) = delete; + AnyMap(AnyMap&& other) = delete; + AnyMap& operator=(const AnyMap& other) = delete; + AnyMap& operator=(AnyMap&& other) = delete; + ~AnyMap() override = default; + + + // return the value if the value exists and the type of value is T. + // return a null optional if value doesn't exists. + // throw std::runtime_error if type is mismatch. + template <typename T> + std::optional<T> GetOptionalValue(const String& key) const + { + try + { + const auto find_result = map_.find(key); + if (find_result != map_.cend()) + { + const auto& value = find_result->second.first; + if (value.has_value()) + return std::any_cast<T>(value); + return std::nullopt; + } + return std::nullopt; + } + catch (const std::bad_any_cast&) + { + throw std::runtime_error(Format("Value of key \"{}\" in AnyMap is not of the type {}.", ToUtf8String(key), typeid(T).name())); + } + } + + // return the value if the value exists and the type of value is T. + // throw if value doesn't exists. (different from "GetOptionalValue"). + // throw std::runtime_error if type is mismatch. + template <typename T> + T GetValue(const String& key) const + { + const auto optional_value = GetOptionalValue<T>(key); + if (optional_value.has_value()) + return optional_value.value(); + else + throw std::runtime_error(Format("Key \"{}\" does not exists in AnyMap.", ToUtf8String(key))); + } + + // Set the value of key, and trigger all related listeners. + template <typename T> + void SetValue(const String& key, const T& value) + { + auto& p = map_[key]; + p.first = std::make_any<T>(value); + InvokeListeners(p.second, p.first); + } + + // Remove the value of the key. + void ClearValue(const String& key) + { + auto& p = map_[key]; + p.first = std::any{}; + InvokeListeners(p.second, std::any{}); + } + + // Add a listener which is called when value of key is changed. + // Return a token used to remove the listener. + ListenerToken RegisterValueChangeListener(const String& key, const Listener& listener); + + // Remove a listener by token. + void UnregisterValueChangeListener(ListenerToken token); + + private: + void InvokeListeners(std::list<ListenerToken>& listener_list, const std::any& value); + + private: + std::unordered_map<String, std::pair<std::any, std::list<ListenerToken>>> map_{}; + std::unordered_map<ListenerToken, Listener> listeners_{}; + ListenerToken current_listener_token_ = 0; + }; +} diff --git a/src/application.hpp b/src/application.hpp index fe38ba0f..48c87a7a 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -6,6 +6,7 @@ #include <functional> #include "base.hpp" +#include "any_map.h" namespace cru { @@ -121,6 +122,11 @@ namespace cru return caret_info_; } + AnyMap* GetThemeMap() + { + return &theme_map_; + } + #ifdef CRU_DEBUG_LAYOUT const DebugLayoutResource* GetDebugLayoutResource() const { @@ -143,6 +149,8 @@ namespace cru #endif CaretInfo caret_info_; + + AnyMap theme_map_{}; }; diff --git a/src/ui/control.hpp b/src/ui/control.hpp index 26be653a..0e078da2 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -3,16 +3,15 @@ #include "system_headers.hpp" #include <unordered_map> #include <any> -#include <typeinfo> #include <utility> #include "base.hpp" -#include "format.hpp" #include "ui_base.hpp" #include "layout_base.hpp" #include "events/ui_event.hpp" #include "border_property.hpp" #include "cursor.hpp" +#include "any_map.h" namespace cru::ui { @@ -183,33 +182,9 @@ namespace cru::ui //*************** region: additional properties *************** - template <typename T> - std::optional<T> GetAdditionalProperty(const String& key) + AnyMap* GetAdditionalPropertyMap() { - try - { - const auto find_result = additional_properties_.find(key); - if (find_result != additional_properties_.cend()) - return std::any_cast<T>(find_result->second); - else - return std::nullopt; - } - catch (const std::bad_any_cast&) - { - throw std::runtime_error(Format("Key \"{}\" is not of the type {}.", ToUtf8String(key), typeid(T).name())); - } - } - - template <typename T> - void SetAdditionalProperty(const String& key, const T& value) - { - additional_properties_[key] = std::make_any<T>(value); - } - - template <typename T> - void SetAdditionalProperty(const String& key, T&& value) - { - additional_properties_[key] = std::make_any<T>(std::move(value)); + return &additional_property_map_; } @@ -389,7 +364,7 @@ namespace cru::ui bool is_bordered_ = false; BorderProperty border_property_; - std::unordered_map<String, std::any> additional_properties_{}; + AnyMap additional_property_map_{}; bool is_focus_on_pressed_ = true; diff --git a/src/ui/theme.cpp b/src/ui/theme.cpp new file mode 100644 index 00000000..eb0f3df6 --- /dev/null +++ b/src/ui/theme.cpp @@ -0,0 +1,6 @@ +#include "theme.hpp" + +namespace cru::ui::theme +{ + +} diff --git a/src/ui/theme.hpp b/src/ui/theme.hpp new file mode 100644 index 00000000..571a05fe --- /dev/null +++ b/src/ui/theme.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "any_map.h" + +namespace cru::ui::theme +{ + +} |