diff options
-rw-r--r-- | include/cru/ui/ThemeManager.h | 71 | ||||
-rw-r--r-- | include/cru/ui/ThemeResourceDictionary.h | 84 | ||||
-rw-r--r-- | src/ui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/ui/ThemeManager.cpp | 62 | ||||
-rw-r--r-- | src/ui/ThemeResourceDictionary.cpp | 57 |
5 files changed, 181 insertions, 94 deletions
diff --git a/include/cru/ui/ThemeManager.h b/include/cru/ui/ThemeManager.h index 9176d85e..9917f219 100644 --- a/include/cru/ui/ThemeManager.h +++ b/include/cru/ui/ThemeManager.h @@ -1,28 +1,11 @@ #pragma once #include "Base.h" -#include "cru/common/Base.h" #include "cru/common/Event.h" -#include "cru/common/Exception.h" -#include "cru/platform/graphics/Brush.h" -#include "cru/ui/mapper/MapperRegistry.h" -#include "cru/ui/style/StyleRuleSet.h" -#include "cru/xml/XmlNode.h" +#include "cru/ui/ThemeResourceDictionary.h" -#include <any> -#include <typeindex> -#include <typeinfo> -#include <unordered_map> +#include <vector> namespace cru::ui { -class CRU_UI_API ThemeResourceKeyNotExistException : public Exception { - public: - using Exception::Exception; -}; - -class CRU_UI_API BadThemeResourceException : public Exception { - public: - using Exception::Exception; -}; class CRU_UI_API ThemeManager : public Object { public: @@ -37,33 +20,21 @@ class CRU_UI_API ThemeManager : public Object { ~ThemeManager() override; - IEvent<std::nullptr_t>* ThemeResourceChangeEvent() { - return &theme_resource_change_event_; - } - - void ReadResourcesFile(const String& file_path); + std::vector<ThemeResourceDictionary*> GetThemeResourceDictionaryList() const; - void SetThemeXml(xml::XmlElementNode* root); + void PrependThemeResourceDictionary( + std::unique_ptr<ThemeResourceDictionary> theme_resource_dictionary); template <typename T> T GetResource(const String& key) { - auto find_result = theme_resource_map_.find(key); - if (find_result == theme_resource_map_.cend()) { - throw ThemeResourceKeyNotExistException( - Format(u"Theme resource key \"%s\" not exist.", key)); + for (const auto& resource_dictionary : theme_resource_dictionary_list_) { + try { + return resource_dictionary->GetResource<T>(key); + } catch (ThemeResourceKeyNotExistException&) { + } } - - auto& cache = find_result->second.cache; - auto cache_find_result = cache.find(typeid(T)); - if (cache_find_result != cache.cend()) { - return std::any_cast<T>(cache_find_result->second); - } - - auto mapper_registry = mapper::MapperRegistry::GetInstance(); - auto mapper = mapper_registry->GetMapper<T>(); - auto resource = mapper->MapFromXml(find_result->second.xml_node); - cache[typeid(T)] = resource; - return resource; + throw ThemeResourceKeyNotExistException( + Format(u"Theme resource key {} not exist.", key)); } std::shared_ptr<platform::graphics::IBrush> GetResourceBrush( @@ -74,19 +45,13 @@ class CRU_UI_API ThemeManager : public Object { std::shared_ptr<style::StyleRuleSet> GetResourceStyleRuleSet( const String& key); - private: - struct ResourceEntry { - CRU_DEFAULT_CONSTRUCTOR_DESTRUCTOR(ResourceEntry) - CRU_DEFAULT_COPY(ResourceEntry) - CRU_DEFAULT_MOVE(ResourceEntry) - - String name; - xml::XmlElementNode* xml_node; - std::unordered_map<std::type_index, std::any> cache; - }; + IEvent<std::nullptr_t>* ThemeResourceChangeEvent() { + return &theme_resource_change_event_; + } + private: Event<std::nullptr_t> theme_resource_change_event_; - std::unique_ptr<xml::XmlElementNode> theme_resource_xml_root_; - std::unordered_map<String, ResourceEntry> theme_resource_map_; + std::vector<std::unique_ptr<ThemeResourceDictionary>> + theme_resource_dictionary_list_; }; } // namespace cru::ui diff --git a/include/cru/ui/ThemeResourceDictionary.h b/include/cru/ui/ThemeResourceDictionary.h new file mode 100644 index 00000000..26f7ec8d --- /dev/null +++ b/include/cru/ui/ThemeResourceDictionary.h @@ -0,0 +1,84 @@ +#pragma once +#include "Base.h" + +#include "cru/common/Base.h" +#include "cru/xml/XmlNode.h" +#include "mapper/MapperRegistry.h" +#include "style/StyleRuleSet.h" + +#include <any> +#include <typeindex> +#include <typeinfo> + +namespace cru::ui { +class CRU_UI_API ThemeResourceKeyNotExistException : public Exception { + public: + using Exception::Exception; +}; + +class CRU_UI_API BadThemeResourceException : public Exception { + public: + using Exception::Exception; +}; + +class CRU_UI_API ThemeResourceDictionary : public Object { + CRU_DEFINE_CLASS_LOG_TAG(u"ThemeResources"); + + public: + static std::unique_ptr<ThemeResourceDictionary> FromFile(const String& file_path); + + explicit ThemeResourceDictionary(xml::XmlElementNode* xml_root, bool clone = true); + + CRU_DELETE_COPY(ThemeResourceDictionary) + CRU_DELETE_MOVE(ThemeResourceDictionary) + + ~ThemeResourceDictionary() override; + + public: + template <typename T> + T GetResource(const String& key) { + auto find_result = resource_map_.find(key); + if (find_result == resource_map_.cend()) { + throw ThemeResourceKeyNotExistException( + Format(u"Theme resource key {} not exist.", key)); + } + + auto& cache = find_result->second.cache; + auto cache_find_result = cache.find(typeid(T)); + if (cache_find_result != cache.cend()) { + return std::any_cast<T>(cache_find_result->second); + } + + auto mapper_registry = mapper::MapperRegistry::GetInstance(); + auto mapper = mapper_registry->GetMapper<T>(); + auto resource = mapper->MapFromXml(find_result->second.xml_node); + cache[typeid(T)] = resource; + return resource; + } + + std::shared_ptr<platform::graphics::IBrush> GetResourceBrush( + const String& key); + + std::shared_ptr<platform::graphics::IFont> GetResourceFont(const String& key); + + std::shared_ptr<style::StyleRuleSet> GetResourceStyleRuleSet( + const String& key); + + private: + void UpdateResourceMap(xml::XmlElementNode* root_xml); + + private: + struct ResourceEntry { + CRU_DEFAULT_CONSTRUCTOR_DESTRUCTOR(ResourceEntry) + CRU_DEFAULT_COPY(ResourceEntry) + CRU_DEFAULT_MOVE(ResourceEntry) + + String name; + xml::XmlElementNode* xml_node; + std::unordered_map<std::type_index, std::any> cache; + }; + + std::unique_ptr<xml::XmlElementNode> xml_root_; + std::unordered_map<String, ResourceEntry> resource_map_; +}; +} // namespace cru::ui diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 56205db0..91128862 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(cru_ui SHARED Helper.cpp ThemeManager.cpp + ThemeResourceDictionary.cpp components/Component.cpp components/Menu.cpp components/PopupButton.cpp diff --git a/src/ui/ThemeManager.cpp b/src/ui/ThemeManager.cpp index 4649f70f..1adbddaf 100644 --- a/src/ui/ThemeManager.cpp +++ b/src/ui/ThemeManager.cpp @@ -7,6 +7,7 @@ #include "cru/platform/graphics/Brush.h" #include "cru/platform/graphics/Factory.h" #include "cru/platform/gui/UiApplication.h" +#include "cru/ui/ThemeResourceDictionary.h" #include "cru/ui/style/StyleRuleSet.h" #include "cru/xml/XmlParser.h" @@ -24,11 +25,30 @@ ThemeManager::ThemeManager() { throw Exception(u"Default resources file not found."); } - ReadResourcesFile(String::FromStdPath(resourses_file)); + PrependThemeResourceDictionary( + ThemeResourceDictionary::FromFile(String::FromStdPath(resourses_file))); } ThemeManager::~ThemeManager() {} +std::vector<ThemeResourceDictionary*> +ThemeManager::GetThemeResourceDictionaryList() const { + std::vector<ThemeResourceDictionary*> result; + for (const auto& theme_resource_dictionary : + theme_resource_dictionary_list_) { + result.push_back(theme_resource_dictionary.get()); + } + return result; +} + +void ThemeManager::PrependThemeResourceDictionary( + std::unique_ptr<ThemeResourceDictionary> theme_resource_dictionary) { + theme_resource_dictionary_list_.insert( + theme_resource_dictionary_list_.begin(), + std::move(theme_resource_dictionary)); + theme_resource_change_event_.Raise(nullptr); +} + std::shared_ptr<platform::graphics::IBrush> ThemeManager::GetResourceBrush( const String& key) { return GetResource<std::shared_ptr<platform::graphics::IBrush>>(key); @@ -43,44 +63,4 @@ std::shared_ptr<style::StyleRuleSet> ThemeManager::GetResourceStyleRuleSet( const String& key) { return GetResource<std::shared_ptr<style::StyleRuleSet>>(key); } - -void ThemeManager::ReadResourcesFile(const String& file_path) { - io::FileStream stream(file_path, io::OpenFileFlags::Read); - auto xml_string = stream.ReadAllAsString(); - auto parser = xml::XmlParser(xml_string); - SetThemeXml(parser.Parse()); -} - -void ThemeManager::SetThemeXml(xml::XmlElementNode* root) { - theme_resource_xml_root_.reset(root); - theme_resource_map_.clear(); - - if (!theme_resource_xml_root_->GetTag().CaseInsensitiveEqual(u"Theme")) { - throw Exception(u"Root tag of theme must be \"Theme\"."); - } - - for (auto child : theme_resource_xml_root_->GetChildren()) { - if (child->IsElementNode()) { - auto c = child->AsElement(); - if (c->GetTag().CaseInsensitiveEqual(u"Resource")) { - auto key_attr = c->GetOptionalAttributeCaseInsensitive(u"key"); - if (!key_attr) { - throw Exception(u"\"key\" attribute is required for resource."); - } - if (!c->GetChildElementCount()) { - throw Exception(u"Resource must have only one child element."); - } - - ResourceEntry entry; - - entry.name = *key_attr; - entry.xml_node = c->GetFirstChildElement(); - - theme_resource_map_[entry.name] = std::move(entry); - } - } - } - - theme_resource_change_event_.Raise(nullptr); -} } // namespace cru::ui diff --git a/src/ui/ThemeResourceDictionary.cpp b/src/ui/ThemeResourceDictionary.cpp new file mode 100644 index 00000000..d9483f6c --- /dev/null +++ b/src/ui/ThemeResourceDictionary.cpp @@ -0,0 +1,57 @@ +#include "cru/ui/ThemeResourceDictionary.h" +#include "cru/common/io/FileStream.h" +#include "cru/common/log/Logger.h" +#include "cru/xml/XmlNode.h" +#include "cru/xml/XmlParser.h" + +namespace cru::ui { + +std::unique_ptr<ThemeResourceDictionary> ThemeResourceDictionary::FromFile( + const String& file_path) { + io::FileStream stream(file_path, io::OpenFileFlags::Read); + auto xml_string = stream.ReadAllAsString(); + auto parser = xml::XmlParser(xml_string); + return std::make_unique<ThemeResourceDictionary>(parser.Parse(), false); +} + +ThemeResourceDictionary::ThemeResourceDictionary(xml::XmlElementNode* xml_root, + bool clone) { + Expects(xml_root); + xml_root_.reset(clone ? xml_root->Clone()->AsElement() : xml_root); + UpdateResourceMap(xml_root_.get()); +} + +ThemeResourceDictionary::~ThemeResourceDictionary() = default; + +void ThemeResourceDictionary::UpdateResourceMap(xml::XmlElementNode* xml_root) { + if (!xml_root->GetTag().CaseInsensitiveEqual(u"Theme")) { + throw Exception(u"Root tag of theme must be 'Theme'."); + } + + for (auto child : xml_root->GetChildren()) { + if (child->IsElementNode()) { + auto c = child->AsElement(); + if (c->GetTag().CaseInsensitiveEqual(u"Resource")) { + auto key_attr = c->GetOptionalAttributeCaseInsensitive(u"key"); + if (!key_attr) { + throw Exception(u"'key' attribute is required for resource."); + } + if (c->GetChildElementCount() != 1) { + throw Exception(u"Resource must have only one child element."); + } + + ResourceEntry entry; + + entry.name = *key_attr; + entry.xml_node = c->GetFirstChildElement(); + + resource_map_[entry.name] = std::move(entry); + } else { + CRU_LOG_DEBUG(u"Ignore unknown element {} of theme.", c->GetTag()); + } + } else { + CRU_LOG_WARN(u"Ignore text node of theme."); + } + } +} +} // namespace cru::ui |