aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CruUI.vcxproj4
-rw-r--r--CruUI.vcxproj.filters12
-rw-r--r--src/any_map.cpp33
-rw-r--r--src/any_map.h100
-rw-r--r--src/application.hpp8
-rw-r--r--src/ui/control.hpp33
-rw-r--r--src/ui/theme.cpp6
-rw-r--r--src/ui/theme.hpp8
8 files changed, 175 insertions, 29 deletions
diff --git a/CruUI.vcxproj b/CruUI.vcxproj
index 048c06cc..97fb323d 100644
--- a/CruUI.vcxproj
+++ b/CruUI.vcxproj
@@ -117,12 +117,14 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClCompile Include="src\any_map.cpp" />
<ClCompile Include="src\cru_debug.cpp" />
<ClCompile Include="src\application.cpp" />
<ClCompile Include="src\base.cpp" />
<ClCompile Include="src\exception.cpp" />
<ClCompile Include="src\graph\graph.cpp" />
<ClCompile Include="src\main.cpp" />
+ <ClCompile Include="src\ui\theme.cpp" />
<ClCompile Include="src\timer.cpp" />
<ClCompile Include="src\ui\animations\animation.cpp" />
<ClCompile Include="src\ui\control.cpp" />
@@ -131,7 +133,9 @@
<ClCompile Include="src\ui\controls\linear_layout.cpp" />
<ClCompile Include="src\ui\controls\text_block.cpp" />
<ClCompile Include="src\ui\controls\text_box.cpp" />
+ <ClInclude Include="src\any_map.h" />
<ClInclude Include="src\format.hpp" />
+ <ClInclude Include="src\ui\theme.hpp" />
<ClInclude Include="src\ui\border_property.hpp" />
<ClInclude Include="src\ui\controls\text_control.hpp" />
<ClCompile Include="src\ui\controls\toggle_button.cpp" />
diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters
index 08201810..323b559e 100644
--- a/CruUI.vcxproj.filters
+++ b/CruUI.vcxproj.filters
@@ -75,6 +75,12 @@
<ClCompile Include="src\cru_debug.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\any_map.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\ui\theme.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\graph\graph.hpp">
@@ -149,6 +155,12 @@
<ClInclude Include="src\timer.hpp">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\any_map.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\ui\theme.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\application.cpp">
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 7357bbaf..c286cc08 100644
--- a/src/application.hpp
+++ b/src/application.hpp
@@ -8,6 +8,7 @@
#include <type_traits>
#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
{
@@ -141,6 +147,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
+{
+
+}