aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2019-03-19 16:21:54 +0800
committercrupest <crupest@outlook.com>2019-03-19 16:21:54 +0800
commit5dc738a57930271194bd86673eb86f149096a7b2 (patch)
tree71174aba0d1c0918cc7d7a1be0b86ec0d5c20401
parent06edefebe8dfb138404397fb2c46732da6cd733a (diff)
downloadcru-5dc738a57930271194bd86673eb86f149096a7b2.tar.gz
cru-5dc738a57930271194bd86673eb86f149096a7b2.tar.bz2
cru-5dc738a57930271194bd86673eb86f149096a7b2.zip
...
-rw-r--r--.clang-format1
-rw-r--r--CruUI.vcxproj18
-rw-r--r--CruUI.vcxproj.filters54
-rw-r--r--snippets/vc++snippets.snippet124
-rw-r--r--src/any_map.cpp33
-rw-r--r--src/any_map.hpp103
-rw-r--r--src/application.cpp192
-rw-r--r--src/application.hpp177
-rw-r--r--src/base.cpp20
-rw-r--r--src/base.hpp91
-rw-r--r--src/cru_debug.cpp10
-rw-r--r--src/cru_debug.hpp67
-rw-r--r--src/cru_event.hpp134
-rw-r--r--src/exception.cpp76
-rw-r--r--src/exception.hpp94
-rw-r--r--src/format.hpp110
-rw-r--r--src/math_util.hpp69
-rw-r--r--src/pre.hpp5
-rw-r--r--src/system_headers.hpp2
-rw-r--r--src/timer.cpp105
-rw-r--r--src/timer.hpp90
-rw-r--r--src/ui/control.cpp681
-rw-r--r--src/ui/control.hpp301
-rw-r--r--src/ui/controls/button.hpp2
-rw-r--r--src/ui/controls/list_item.hpp2
-rw-r--r--src/ui/controls/scroll_control.hpp2
-rw-r--r--src/ui/render/linear_layout_render_object.cpp0
-rw-r--r--src/ui/render/linear_layout_render_object.hpp1
-rw-r--r--src/ui/render/render_object.cpp176
-rw-r--r--src/ui/render/render_object.hpp302
-rw-r--r--src/ui/ui_base.hpp39
-rw-r--r--src/ui/window.cpp23
-rw-r--r--src/ui/window.hpp28
-rw-r--r--src/ui/window_class.cpp25
-rw-r--r--src/ui/window_class.hpp26
-rw-r--r--src/util/any_map.cpp30
-rw-r--r--src/util/any_map.hpp94
-rw-r--r--src/util/format.hpp94
-rw-r--r--src/util/math_util.hpp51
-rw-r--r--src/util/string_util.cpp21
-rw-r--r--src/util/string_util.hpp8
41 files changed, 1260 insertions, 2221 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..f6cb8ad9
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1 @@
+BasedOnStyle: Google
diff --git a/CruUI.vcxproj b/CruUI.vcxproj
index ee2c7eb1..9bedd3f8 100644
--- a/CruUI.vcxproj
+++ b/CruUI.vcxproj
@@ -117,10 +117,10 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
- <ClCompile Include="src\any_map.cpp" />
+ <ClCompile Include="src\ui\window_class.cpp" />
+ <ClCompile Include="src\util\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" />
@@ -136,9 +136,11 @@
<ClCompile Include="src\ui\controls\scroll_control.cpp" />
<ClCompile Include="src\ui\controls\text_block.cpp" />
<ClCompile Include="src\ui\controls\text_box.cpp" />
- <ClInclude Include="src\any_map.hpp" />
- <ClInclude Include="src\format.hpp" />
- <ClInclude Include="src\math_util.hpp" />
+ <ClCompile Include="src\util\string_util.cpp" />
+ <ClInclude Include="src\ui\window_class.hpp" />
+ <ClInclude Include="src\util\any_map.hpp" />
+ <ClInclude Include="src\util\format.hpp" />
+ <ClInclude Include="src\util\math_util.hpp" />
<ClInclude Include="src\ui\border_property.hpp" />
<ClInclude Include="src\ui\controls\frame_layout.hpp" />
<ClInclude Include="src\ui\controls\list_item.hpp" />
@@ -150,10 +152,12 @@
<ClCompile Include="src\ui\events\ui_event.cpp" />
<ClCompile Include="src\ui\input_util.cpp" />
<ClCompile Include="src\ui\layout_base.cpp" />
+ <ClCompile Include="src\ui\render\linear_layout_render_object.cpp" />
<ClCompile Include="src\ui\render\render_object.cpp" />
<ClCompile Include="src\ui\ui_manager.cpp" />
<ClCompile Include="src\ui\ui_base.cpp" />
<ClCompile Include="src\ui\window.cpp" />
+ <ClInclude Include="src\util\string_util.hpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\application.hpp" />
@@ -178,6 +182,7 @@
<ClInclude Include="src\ui\events\ui_event.hpp" />
<ClInclude Include="src\ui\input_util.hpp" />
<ClInclude Include="src\ui\layout_base.hpp" />
+ <ClInclude Include="src\ui\render\linear_layout_render_object.hpp" />
<ClInclude Include="src\ui\render\render_object.hpp" />
<ClInclude Include="src\ui\ui_manager.hpp" />
<ClInclude Include="src\ui\ui_base.hpp" />
@@ -186,6 +191,9 @@
<ItemGroup>
<Xml Include="snippets\vc++snippets.snippet" />
</ItemGroup>
+ <ItemGroup>
+ <None Include=".clang-format" />
+ </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters
index 13976a7d..9a85da3f 100644
--- a/CruUI.vcxproj.filters
+++ b/CruUI.vcxproj.filters
@@ -18,9 +18,6 @@
<ClCompile Include="src\application.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\base.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\exception.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -75,9 +72,6 @@
<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\ui_manager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -96,6 +90,21 @@
<ClCompile Include="src\ui\render\render_object.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\ui\input_util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\ui\render\linear_layout_render_object.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\util\string_util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\util\any_map.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\ui\window_class.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\graph\graph.hpp">
@@ -158,9 +167,6 @@
<ClInclude Include="src\exception.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\format.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\system_headers.hpp">
<Filter>Header Files</Filter>
</ClInclude>
@@ -170,9 +176,6 @@
<ClInclude Include="src\ui\ui_manager.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\any_map.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\ui\controls\list_item.hpp">
<Filter>Header Files</Filter>
</ClInclude>
@@ -185,9 +188,6 @@
<ClInclude Include="src\ui\controls\scroll_control.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\math_util.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\pre.hpp">
<Filter>Header Files</Filter>
</ClInclude>
@@ -197,6 +197,27 @@
<ClInclude Include="src\ui\d2d_util.hpp">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\ui\input_util.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\ui\render\linear_layout_render_object.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\util\format.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\util\math_util.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\util\string_util.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\util\any_map.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\ui\window_class.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\application.cpp">
@@ -257,4 +278,7 @@
<ItemGroup>
<Xml Include="snippets\vc++snippets.snippet" />
</ItemGroup>
+ <ItemGroup>
+ <None Include=".clang-format" />
+ </ItemGroup>
</Project> \ No newline at end of file
diff --git a/snippets/vc++snippets.snippet b/snippets/vc++snippets.snippet
index 1a5f7666..ebd72463 100644
--- a/snippets/vc++snippets.snippet
+++ b/snippets/vc++snippets.snippet
@@ -5,7 +5,7 @@
<Title>Deleted Copy Constructor/Assignment</Title>
<Author>crupest</Author>
<Description>Declare a deleted copy constructor and a deleted copy assignment operator for a class.</Description>
- <Shortcut>dcopy</Shortcut>
+ <Shortcut>deletecopy</Shortcut>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
@@ -28,10 +28,36 @@
</CodeSnippet>
<CodeSnippet Format="1.0.0">
<Header>
+ <Title>Default Copy Constructor/Assignment</Title>
+ <Author>crupest</Author>
+ <Description>Declare a default copy constructor and a default copy assignment operator for a class.</Description>
+ <Shortcut>defaultcopy</Shortcut>
+ <SnippetTypes>
+ <SnippetType>Expansion</SnippetType>
+ </SnippetTypes>
+ </Header>
+ <Snippet>
+ <Declarations>
+ <Literal>
+ <ID>classname</ID>
+ <Default>class_name</Default>
+ <ToolTip>The name of the class.</ToolTip>
+ </Literal>
+ </Declarations>
+ <Code Language="CPP" Kind="method decl">
+ <![CDATA[
+ $classname$(const $classname$& other) = default;
+ $classname$& operator=(const $classname$& other) = default;
+ ]]>
+ </Code>
+ </Snippet>
+ </CodeSnippet>
+ <CodeSnippet Format="1.0.0">
+ <Header>
<Title>Deleted Move Constructor/Assignment</Title>
<Author>crupest</Author>
<Description>Declare a deleted move constructor and a deleted move assignment operator for a class.</Description>
- <Shortcut>dmove</Shortcut>
+ <Shortcut>deletemove</Shortcut>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
@@ -52,4 +78,98 @@
</Code>
</Snippet>
</CodeSnippet>
+ <CodeSnippet Format="1.0.0">
+ <Header>
+ <Title>Default Move Constructor/Assignment</Title>
+ <Author>crupest</Author>
+ <Description>Declare a default move constructor and a default move assignment operator for a class.</Description>
+ <Shortcut>defaultmove</Shortcut>
+ <SnippetTypes>
+ <SnippetType>Expansion</SnippetType>
+ </SnippetTypes>
+ </Header>
+ <Snippet>
+ <Declarations>
+ <Literal>
+ <ID>classname</ID>
+ <Default>class_name</Default>
+ <ToolTip>The name of the class.</ToolTip>
+ </Literal>
+ </Declarations>
+ <Code Language="CPP" Kind="method decl">
+ <![CDATA[
+ $classname$($classname$&& other) = default;
+ $classname$& operator=($classname$&& other) = default;
+ ]]>
+ </Code>
+ </Snippet>
+ </CodeSnippet>
+ <CodeSnippet Format="1.0.0">
+ <Header>
+ <Title>Default Copyable/Movable Class</Title>
+ <Author>crupest</Author>
+ <Description>Define a class with default constructor, destructor, move/copy constructor/assignment-operator.</Description>
+ <Shortcut>defaultclass</Shortcut>
+ <SnippetTypes>
+ <SnippetType>Expansion</SnippetType>
+ </SnippetTypes>
+ </Header>
+ <Snippet>
+ <Declarations>
+ <Literal>
+ <ID>classname</ID>
+ <Default>class_name</Default>
+ <ToolTip>The name of the class.</ToolTip>
+ </Literal>
+ </Declarations>
+ <Code Language="CPP" Kind="type decl">
+ <![CDATA[
+ class $classname$
+ {
+ public:
+ $classname$() = default;
+ $classname$(const $classname$& other) = default;
+ $classname$& operator=(const $classname$& other) = default;
+ $classname$($classname$&& other) = default;
+ $classname$& operator=($classname$&& other) = default;
+ ~$classname$() = default;
+ };
+ ]]>
+ </Code>
+ </Snippet>
+ </CodeSnippet>
+ <CodeSnippet Format="1.0.0">
+ <Header>
+ <Title>Default Noncopyable/Nonmovable Class</Title>
+ <Author>crupest</Author>
+ <Description>Define a class with default constructor, destructor and deleted move/copy constructor/assignment-operator.</Description>
+ <Shortcut>objectclass</Shortcut>
+ <SnippetTypes>
+ <SnippetType>Expansion</SnippetType>
+ </SnippetTypes>
+ </Header>
+ <Snippet>
+ <Declarations>
+ <Literal>
+ <ID>classname</ID>
+ <Default>class_name</Default>
+ <ToolTip>The name of the class.</ToolTip>
+ </Literal>
+ </Declarations>
+ <Code Language="CPP" Kind="type decl">
+ <![CDATA[
+ class $classname$
+ {
+ public:
+ $classname$() = default;
+ $classname$(const $classname$& other) = delete;
+ $classname$& operator=(const $classname$& other) = delete;
+ $classname$($classname$&& other) = delete;
+ $classname$& operator=($classname$&& other) = delete;
+ ~$classname$() = default;
+ };
+ ]]>
+ </Code>
+ </Snippet>
+ </CodeSnippet>
</CodeSnippets> \ No newline at end of file
diff --git a/src/any_map.cpp b/src/any_map.cpp
deleted file mode 100644
index de13f85e..00000000
--- a/src/any_map.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "any_map.hpp"
-
-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.hpp b/src/any_map.hpp
deleted file mode 100644
index dfc54f3f..00000000
--- a/src/any_map.hpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#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, T&& value)
- {
- auto& p = map_[key];
- p.first = std::make_any<T>(std::forward<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.cpp b/src/application.cpp
index c3669f72..e580b56b 100644
--- a/src/application.cpp
+++ b/src/application.cpp
@@ -2,135 +2,113 @@
#include "exception.hpp"
#include "timer.hpp"
-#include "ui/window.hpp"
#include "ui/cursor.hpp"
+#include "ui/window_class.hpp"
namespace cru {
- constexpr auto god_window_class_name = L"GodWindowClass";
- constexpr int invoke_later_message_id = WM_USER + 2000;
-
-
- LRESULT CALLBACK GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- const auto app = Application::GetInstance();
-
- if (app)
- {
- const auto result = app->GetGodWindow()->HandleGodWindowMessage(hWnd, uMsg, wParam, lParam);
- if (result.has_value())
- return result.value();
- else
- return DefWindowProc(hWnd, uMsg, wParam, lParam);
- }
- else
- return DefWindowProc(hWnd, uMsg, wParam, lParam);
- }
+constexpr auto god_window_class_name = L"GodWindowClass";
+constexpr int invoke_later_message_id = WM_USER + 2000;
+
+LRESULT CALLBACK GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam) {
+ const auto app = Application::GetInstance();
+
+ if (app) {
+ const auto result =
+ app->GetGodWindow()->HandleGodWindowMessage(hWnd, uMsg, wParam, lParam);
+ if (result.has_value())
+ return result.value();
+ else
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ } else
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
- GodWindow::GodWindow(Application* application)
- {
- const auto h_instance = application->GetInstanceHandle();
+GodWindow::GodWindow(Application* application) {
+ const auto h_instance = application->GetInstanceHandle();
- god_window_class_ = std::make_unique<ui::WindowClass>(god_window_class_name, GodWndProc, h_instance);
+ god_window_class_ = std::make_unique<ui::WindowClass>(god_window_class_name,
+ GodWndProc, h_instance);
- hwnd_ = CreateWindowEx(0,
- god_window_class_name,
- L"", 0,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- HWND_MESSAGE, nullptr, h_instance, nullptr
- );
+ hwnd_ = CreateWindowEx(0, god_window_class_name, L"", 0, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ HWND_MESSAGE, nullptr, h_instance, nullptr);
- if (hwnd_ == nullptr)
- throw std::runtime_error("Failed to create window.");
- }
+ if (hwnd_ == nullptr) throw std::runtime_error("Failed to create window.");
+}
- GodWindow::~GodWindow()
- {
- ::DestroyWindow(hwnd_);
+GodWindow::~GodWindow() { ::DestroyWindow(hwnd_); }
+
+std::optional<LRESULT> GodWindow::HandleGodWindowMessage(HWND hwnd, int msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ switch (msg) {
+ case invoke_later_message_id: {
+ const auto p_action = reinterpret_cast<std::function<void()>*>(w_param);
+ (*p_action)();
+ delete p_action;
+ return 0;
}
-
- std::optional<LRESULT> GodWindow::HandleGodWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param)
- {
- switch (msg)
- {
- case invoke_later_message_id:
- {
- const auto p_action = reinterpret_cast<std::function<void()>*>(w_param);
- (*p_action)();
- delete p_action;
- return 0;
- }
- case WM_TIMER:
- {
- const auto id = static_cast<UINT_PTR>(w_param);
- const auto action = TimerManager::GetInstance()->GetAction(id);
- if (action.has_value())
- {
- (action.value().second)();
- if (!action.value().first)
- TimerManager::GetInstance()->KillTimer(id);
- return 0;
- }
- break;
- }
- default:
- return std::nullopt;
- }
- return std::nullopt;
+ case WM_TIMER: {
+ const auto id = static_cast<UINT_PTR>(w_param);
+ const auto action = TimerManager::GetInstance()->GetAction(id);
+ if (action.has_value()) {
+ (action.value().second)();
+ if (!action.value().first) TimerManager::GetInstance()->KillTimer(id);
+ return 0;
+ }
+ break;
}
+ default:
+ return std::nullopt;
+ }
+ return std::nullopt;
+}
+Application* Application::instance_ = nullptr;
+Application* Application::GetInstance() { return instance_; }
- Application* Application::instance_ = nullptr;
-
- Application * Application::GetInstance() {
- return instance_;
- }
-
- Application::Application(HINSTANCE h_instance)
- : h_instance_(h_instance) {
-
- if (instance_)
- throw std::runtime_error("A application instance already exists.");
+Application::Application(HINSTANCE h_instance) : h_instance_(h_instance) {
+ if (instance_)
+ throw std::runtime_error("A application instance already exists.");
- instance_ = this;
+ instance_ = this;
- if (!::IsWindows8OrGreater())
- throw std::runtime_error("Must run on Windows 8 or later.");
+ if (!::IsWindows8OrGreater())
+ throw std::runtime_error("Must run on Windows 8 or later.");
- god_window_ = std::make_unique<GodWindow>(this);
+ god_window_ = std::make_unique<GodWindow>(this);
- ui::cursors::LoadSystemCursors();
- }
+ ui::cursors::LoadSystemCursors();
+}
- Application::~Application()
- {
- for (auto i = singleton_list_.crbegin(); i != singleton_list_.crend(); ++i)
- delete *i;
- instance_ = nullptr;
- }
+Application::~Application() {
+ for (auto i = singleton_list_.crbegin(); i != singleton_list_.crend(); ++i)
+ delete *i;
+ instance_ = nullptr;
+}
- int Application::Run()
- {
- MSG msg;
+int Application::Run() {
+ MSG msg;
- while (GetMessage(&msg, nullptr, 0, 0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
+ while (GetMessage(&msg, nullptr, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
- return static_cast<int>(msg.wParam);
- }
+ return static_cast<int>(msg.wParam);
+}
- void Application::Quit(const int quit_code) {
- ::PostQuitMessage(quit_code);
- }
+void Application::Quit(const int quit_code) { ::PostQuitMessage(quit_code); }
- void InvokeLater(const std::function<void()>& action) {
- //copy the action to a safe place
- auto p_action_copy = new std::function<void()>(action);
+void InvokeLater(const std::function<void()>& action) {
+ // copy the action to a safe place
+ auto p_action_copy = new std::function<void()>(action);
- if (PostMessageW(Application::GetInstance()->GetGodWindow()->GetHandle(), invoke_later_message_id, reinterpret_cast<WPARAM>(p_action_copy), 0) == 0)
- throw Win32Error(::GetLastError(), "InvokeLater failed to post message.");
- }
+ if (PostMessageW(Application::GetInstance()->GetGodWindow()->GetHandle(),
+ invoke_later_message_id,
+ reinterpret_cast<WPARAM>(p_action_copy), 0) == 0)
+ throw Win32Error(::GetLastError(), "InvokeLater failed to post message.");
}
+} // namespace cru
diff --git a/src/application.hpp b/src/application.hpp
index 8d739938..acf264c3 100644
--- a/src/application.hpp
+++ b/src/application.hpp
@@ -1,14 +1,12 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
-#include "system_headers.hpp"
+#include <functional>
#include <memory>
#include <optional>
-#include <functional>
-#include <typeindex>
#include <type_traits>
+#include <typeindex>
+#include "system_headers.hpp"
#include "base.hpp"
@@ -16,103 +14,92 @@
#include <unordered_set>
#endif
-namespace cru
-{
- class Application;
-
- namespace ui
- {
- class WindowClass;
- }
-
-
- class GodWindow : public Object
- {
- public:
- explicit GodWindow(Application* application);
- GodWindow(const GodWindow& other) = delete;
- GodWindow(GodWindow&& other) = delete;
- GodWindow& operator=(const GodWindow& other) = delete;
- GodWindow& operator=(GodWindow&& other) = delete;
- ~GodWindow() override;
-
- HWND GetHandle() const
- {
- return hwnd_;
- }
-
- std::optional<LRESULT> HandleGodWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param);
-
- private:
- std::unique_ptr<ui::WindowClass> god_window_class_;
- HWND hwnd_;
- };
-
- class Application : public Object
- {
- public:
- static Application* GetInstance();
- private:
- static Application* instance_;
-
- public:
- explicit Application(HINSTANCE h_instance);
- Application(const Application&) = delete;
- Application(Application&&) = delete;
- Application& operator = (const Application&) = delete;
- Application& operator = (Application&&) = delete;
- ~Application() override;
-
- public:
- int Run();
- void Quit(int quit_code);
-
-
- HINSTANCE GetInstanceHandle() const
- {
- return h_instance_;
- }
-
- GodWindow* GetGodWindow() const
- {
- return god_window_.get();
- }
-
- // Resolve a singleton.
- // All singletons will be delete in reverse order of resolve.
- template<typename T, typename = std::enable_if_t<std::is_base_of_v<Object, T>>>
- T* ResolveSingleton(const std::function<T*(Application*)>& creator)
- {
- const auto& index = std::type_index{typeid(T)};
- const auto find_result = singleton_map_.find(index);
- if (find_result != singleton_map_.cend())
- return static_cast<T*>(find_result->second);
+namespace cru {
+class Application;
+
+namespace ui {
+class WindowClass;
+}
+
+class GodWindow : public Object {
+ public:
+ explicit GodWindow(Application* application);
+ GodWindow(const GodWindow& other) = delete;
+ GodWindow(GodWindow&& other) = delete;
+ GodWindow& operator=(const GodWindow& other) = delete;
+ GodWindow& operator=(GodWindow&& other) = delete;
+ ~GodWindow() override;
+
+ HWND GetHandle() const { return hwnd_; }
+
+ std::optional<LRESULT> HandleGodWindowMessage(HWND hwnd, int msg,
+ WPARAM w_param, LPARAM l_param);
+
+ private:
+ std::unique_ptr<ui::WindowClass> god_window_class_;
+ HWND hwnd_;
+};
+
+class Application : public Object {
+ public:
+ static Application* GetInstance();
+
+ private:
+ static Application* instance_;
+
+ public:
+ explicit Application(HINSTANCE h_instance);
+ Application(const Application&) = delete;
+ Application(Application&&) = delete;
+ Application& operator=(const Application&) = delete;
+ Application& operator=(Application&&) = delete;
+ ~Application() override;
+
+ public:
+ int Run();
+ void Quit(int quit_code);
+
+ HINSTANCE GetInstanceHandle() const { return h_instance_; }
+
+ GodWindow* GetGodWindow() const { return god_window_.get(); }
+
+ // Resolve a singleton.
+ // All singletons will be delete in reverse order of resolve.
+ template <typename T,
+ typename = std::enable_if_t<std::is_base_of_v<Object, T>>>
+ T* ResolveSingleton(const std::function<T*(Application*)>& creator) {
+ const auto& index = std::type_index{typeid(T)};
+ const auto find_result = singleton_map_.find(index);
+ if (find_result != singleton_map_.cend())
+ return static_cast<T*>(find_result->second);
#ifdef CRU_DEBUG
- const auto type_find_result = singleton_type_set_.find(index);
- if (type_find_result != singleton_type_set_.cend())
- throw std::logic_error("The singleton of that type is being constructed. This may cause a dead recursion.");
- singleton_type_set_.insert(index);
+ const auto type_find_result = singleton_type_set_.find(index);
+ if (type_find_result != singleton_type_set_.cend())
+ throw std::logic_error(
+ "The singleton of that type is being constructed. This may cause a "
+ "dead recursion.");
+ singleton_type_set_.insert(index);
#endif
- auto singleton = creator(this);
- singleton_map_.emplace(index, static_cast<Object*>(singleton));
- singleton_list_.push_back(singleton);
- return singleton;
- }
+ auto singleton = creator(this);
+ singleton_map_.emplace(index, static_cast<Object*>(singleton));
+ singleton_list_.push_back(singleton);
+ return singleton;
+ }
- private:
- HINSTANCE h_instance_;
+ private:
+ HINSTANCE h_instance_;
- std::unique_ptr<GodWindow> god_window_;
+ std::unique_ptr<GodWindow> god_window_;
- std::unordered_map<std::type_index, Object*> singleton_map_;
- std::list<Object*> singleton_list_; // used for reverse destroy.
+ std::unordered_map<std::type_index, Object*> singleton_map_;
+ std::list<Object*> singleton_list_; // used for reverse destroy.
#ifdef CRU_DEBUG
- std::unordered_set<std::type_index> singleton_type_set_; // used for detecting dead recursion.
+ std::unordered_set<std::type_index>
+ singleton_type_set_; // used for detecting dead recursion.
#endif
- };
-
+};
- void InvokeLater(const std::function<void()>& action);
-}
+void InvokeLater(const std::function<void()>& action);
+} // namespace cru
diff --git a/src/base.cpp b/src/base.cpp
deleted file mode 100644
index a2d20fc4..00000000
--- a/src/base.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "base.hpp"
-
-#include "system_headers.hpp"
-#include "exception.hpp"
-
-namespace cru
-{
- MultiByteString ToUtf8String(const StringView& string)
- {
- if (string.empty())
- return MultiByteString();
-
- const auto length = ::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, nullptr, 0, nullptr, nullptr);
- MultiByteString result;
- result.reserve(length);
- if (::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, result.data(), static_cast<int>(result.capacity()), nullptr, nullptr) == 0)
- throw Win32Error(::GetLastError(), "Failed to convert wide string to UTF-8.");
- return result;
- }
-}
diff --git a/src/base.hpp b/src/base.hpp
index 64ce7f6e..a62a745b 100644
--- a/src/base.hpp
+++ b/src/base.hpp
@@ -1,59 +1,46 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
-#include <string>
+#include <chrono>
#include <stdexcept>
+#include <string>
#include <string_view>
-#include <chrono>
-namespace cru
-{
- template<typename T> struct type_tag {};
-
- //typedefs
- using String = std::wstring;
- using MultiByteString = std::string;
-
- using StringView = std::wstring_view;
- using MultiByteStringView = std::string_view;
-
- using FloatSecond = std::chrono::duration<double, std::chrono::seconds::period>;
-
- enum class FlowControl
- {
- Continue,
- Break
- };
-
-
- class Object
- {
- public:
- Object() = default;
- Object(const Object&) = default;
- Object& operator = (const Object&) = default;
- Object(Object&&) = default;
- Object& operator = (Object&&) = default;
- virtual ~Object() = default;
- };
-
- struct Interface
- {
- virtual ~Interface() = default;
- };
-
- [[noreturn]] inline void UnreachableCode()
- {
- throw std::logic_error("Unreachable code.");
- }
-
- MultiByteString ToUtf8String(const StringView& string);
-
- inline void Require(const bool condition, const MultiByteStringView& error_message)
- {
- if (!condition)
- throw std::invalid_argument(error_message.data());
- }
+namespace cru {
+template <typename T>
+struct type_tag {};
+
+// typedefs
+using String = std::wstring;
+using MultiByteString = std::string;
+
+using StringView = std::wstring_view;
+using MultiByteStringView = std::string_view;
+
+using FloatSecond = std::chrono::duration<double, std::chrono::seconds::period>;
+
+enum class FlowControl { Continue, Break };
+
+class Object {
+ public:
+ Object() = default;
+ Object(const Object&) = default;
+ Object& operator=(const Object&) = default;
+ Object(Object&&) = default;
+ Object& operator=(Object&&) = default;
+ virtual ~Object() = default;
+};
+
+struct Interface {
+ virtual ~Interface() = default;
+};
+
+[[noreturn]] inline void UnreachableCode() {
+ throw std::logic_error("Unreachable code.");
+}
+
+inline void Require(const bool condition,
+ const MultiByteStringView& error_message) {
+ if (!condition) throw std::invalid_argument(error_message.data());
}
+} // namespace cru
diff --git a/src/cru_debug.cpp b/src/cru_debug.cpp
index 9c61d052..b9226132 100644
--- a/src/cru_debug.cpp
+++ b/src/cru_debug.cpp
@@ -2,10 +2,8 @@
#include "system_headers.hpp"
-namespace cru::debug
-{
- void DebugMessage(const StringView& message)
- {
- ::OutputDebugStringW(message.data());
- }
+namespace cru::debug {
+void DebugMessage(const StringView& message) {
+ ::OutputDebugStringW(message.data());
}
+} // namespace cru::debug
diff --git a/src/cru_debug.hpp b/src/cru_debug.hpp
index 17cc7b53..9c22a24f 100644
--- a/src/cru_debug.hpp
+++ b/src/cru_debug.hpp
@@ -1,47 +1,46 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
#include <functional>
#include "base.hpp"
-#include "format.hpp"
+#include "util/format.hpp"
-namespace cru::debug
-{
- void DebugMessage(const StringView& message);
+namespace cru::debug {
+void DebugMessage(const StringView& message);
#ifdef CRU_DEBUG
- inline void DebugTime(const std::function<void()>& action, const StringView& hint_message)
- {
- const auto before = std::chrono::steady_clock::now();
- action();
- const auto after = std::chrono::steady_clock::now();
- const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
- DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count()));
- }
+inline void DebugTime(const std::function<void()>& action,
+ const StringView& hint_message) {
+ const auto before = std::chrono::steady_clock::now();
+ action();
+ const auto after = std::chrono::steady_clock::now();
+ const auto duration =
+ std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
+ DebugMessage(util::Format(L"{}: {}ms.\n", hint_message, duration.count()));
+}
- template<typename TReturn>
- TReturn DebugTime(const std::function<TReturn()>& action, const StringView& hint_message)
- {
- const auto before = std::chrono::steady_clock::now();
- auto&& result = action();
- const auto after = std::chrono::steady_clock::now();
- const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
- DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count()));
- return std::move(result);
- }
+template <typename TReturn>
+TReturn DebugTime(const std::function<TReturn()>& action,
+ const StringView& hint_message) {
+ const auto before = std::chrono::steady_clock::now();
+ auto&& result = action();
+ const auto after = std::chrono::steady_clock::now();
+ const auto duration =
+ std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
+ DebugMessage(util::Format(L"{}: {}ms.\n", hint_message, duration.count()));
+ return std::move(result);
+}
#else
- inline void DebugTime(const std::function<void()>& action, const StringView& hint_message)
- {
- action();
- }
+inline void DebugTime(const std::function<void()>& action,
+ const StringView& hint_message) {
+ action();
+}
- template<typename TReturn>
- TReturn DebugTime(const std::function<TReturn()>& action, const StringView& hint_message)
- {
- return action();
- }
-#endif
+template <typename TReturn>
+TReturn DebugTime(const std::function<TReturn()>& action,
+ const StringView& hint_message) {
+ return action();
}
+#endif
+} // namespace cru::debug
diff --git a/src/cru_event.hpp b/src/cru_event.hpp
index fa41d646..a669695f 100644
--- a/src/cru_event.hpp
+++ b/src/cru_event.hpp
@@ -1,84 +1,68 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
-#include <type_traits>
#include <functional>
#include <map>
+#include <type_traits>
#include "base.hpp"
namespace cru {
- //Base class of all event args.
- class BasicEventArgs : public Object
- {
- public:
- explicit BasicEventArgs(Object* sender)
- : sender_(sender)
- {
-
- }
- BasicEventArgs(const BasicEventArgs& other) = default;
- BasicEventArgs(BasicEventArgs&& other) = default;
- BasicEventArgs& operator=(const BasicEventArgs& other) = default;
- BasicEventArgs& operator=(BasicEventArgs&& other) = default;
- ~BasicEventArgs() override = default;
-
- //Get the sender of the event.
- Object* GetSender() const
- {
- return sender_;
- }
-
- private:
- Object* sender_;
- };
-
-
- //A non-copyable non-movable Event class.
- //It stores a list of event handlers.
- //TArgsType must be subclass of BasicEventArgs.
- template<typename TArgsType>
- class Event
- {
- public:
- static_assert(std::is_base_of_v<BasicEventArgs, TArgsType>,
- "TArgsType must be subclass of BasicEventArgs.");
-
-
- using ArgsType = TArgsType;
- using EventHandler = std::function<void(ArgsType&)>;
- using EventHandlerToken = long;
-
- Event() = default;
- Event(const Event&) = delete;
- Event& operator = (const Event&) = delete;
- Event(Event&&) = delete;
- Event& operator = (Event&&) = delete;
- ~Event() = default;
-
- EventHandlerToken AddHandler(const EventHandler& handler)
- {
- const auto token = current_token_++;
- handlers_.emplace(token, handler);
- return token;
- }
-
- void RemoveHandler(const EventHandlerToken token) {
- auto find_result = handlers_.find(token);
- if (find_result != handlers_.cend())
- handlers_.erase(find_result);
- }
-
- void Raise(ArgsType& args) {
- for (const auto& handler : handlers_)
- (handler.second)(args);
- }
-
- private:
- std::map<EventHandlerToken, EventHandler> handlers_;
-
- EventHandlerToken current_token_ = 0;
- };
-}
+// Base class of all event args.
+class BasicEventArgs : public Object {
+ public:
+ explicit BasicEventArgs(Object* sender) : sender_(sender) {}
+ BasicEventArgs(const BasicEventArgs& other) = default;
+ BasicEventArgs(BasicEventArgs&& other) = default;
+ BasicEventArgs& operator=(const BasicEventArgs& other) = default;
+ BasicEventArgs& operator=(BasicEventArgs&& other) = default;
+ ~BasicEventArgs() override = default;
+
+ // Get the sender of the event.
+ Object* GetSender() const { return sender_; }
+
+ private:
+ Object* sender_;
+};
+
+// A non-copyable non-movable Event class.
+// It stores a list of event handlers.
+// TArgsType must be subclass of BasicEventArgs.
+template <typename TArgsType>
+class Event {
+ public:
+ static_assert(std::is_base_of_v<BasicEventArgs, TArgsType>,
+ "TArgsType must be subclass of BasicEventArgs.");
+
+ using ArgsType = TArgsType;
+ using EventHandler = std::function<void(ArgsType&)>;
+ using EventHandlerToken = long;
+
+ Event() = default;
+ Event(const Event&) = delete;
+ Event& operator=(const Event&) = delete;
+ Event(Event&&) = delete;
+ Event& operator=(Event&&) = delete;
+ ~Event() = default;
+
+ EventHandlerToken AddHandler(const EventHandler& handler) {
+ const auto token = current_token_++;
+ handlers_.emplace(token, handler);
+ return token;
+ }
+
+ void RemoveHandler(const EventHandlerToken token) {
+ auto find_result = handlers_.find(token);
+ if (find_result != handlers_.cend()) handlers_.erase(find_result);
+ }
+
+ void Raise(ArgsType& args) {
+ for (const auto& handler : handlers_) (handler.second)(args);
+ }
+
+ private:
+ std::map<EventHandlerToken, EventHandler> handlers_;
+
+ EventHandlerToken current_token_ = 0;
+};
+} // namespace cru
diff --git a/src/exception.cpp b/src/exception.cpp
index cb1ca2c7..dbc98453 100644
--- a/src/exception.cpp
+++ b/src/exception.cpp
@@ -1,40 +1,42 @@
#include "exception.hpp"
-#include "format.hpp"
-
-namespace cru
-{
- inline std::string HResultMakeMessage(HRESULT h_result, std::optional<MultiByteStringView> message)
- {
- char buffer[10];
- sprintf_s(buffer, "%#08x", h_result);
-
- if (message.has_value())
- return Format("An HResultError is thrown. HRESULT: {}.\nAdditional message: {}\n", buffer, message.value());
- else
- return Format("An HResultError is thrown. HRESULT: {}.\n", buffer);
- }
-
- HResultError::HResultError(HRESULT h_result, std::optional<MultiByteStringView> additional_message)
- : runtime_error(HResultMakeMessage(h_result, std::nullopt)), h_result_(h_result)
- {
-
- }
-
- inline std::string Win32MakeMessage(DWORD error_code, std::optional<MultiByteStringView> message)
- {
- char buffer[10];
- sprintf_s(buffer, "%#04x", error_code);
-
- if (message.has_value())
- return Format("Last error code: {}.\nAdditional message: {}\n", buffer, message.value());
- else
- return Format("Last error code: {}.\n", buffer);
- }
-
- Win32Error::Win32Error(DWORD error_code, std::optional<MultiByteStringView> additional_message)
- : runtime_error(Win32MakeMessage(error_code, std::nullopt)), error_code_(error_code)
- {
-
- }
+#include "util/format.hpp"
+
+namespace cru {
+using util::Format;
+
+inline std::string HResultMakeMessage(
+ HRESULT h_result, std::optional<MultiByteStringView> message) {
+ char buffer[10];
+ sprintf_s(buffer, "%#08x", h_result);
+
+ if (message.has_value())
+ return Format(
+ "An HResultError is thrown. HRESULT: {}.\nAdditional message: {}\n",
+ buffer, message.value());
+ else
+ return Format("An HResultError is thrown. HRESULT: {}.\n", buffer);
}
+
+HResultError::HResultError(
+ HRESULT h_result, std::optional<MultiByteStringView> additional_message)
+ : runtime_error(HResultMakeMessage(h_result, std::nullopt)),
+ h_result_(h_result) {}
+
+inline std::string Win32MakeMessage(
+ DWORD error_code, std::optional<MultiByteStringView> message) {
+ char buffer[10];
+ sprintf_s(buffer, "%#04x", error_code);
+
+ if (message.has_value())
+ return Format("Last error code: {}.\nAdditional message: {}\n", buffer,
+ message.value());
+ else
+ return Format("Last error code: {}.\n", buffer);
+}
+
+Win32Error::Win32Error(DWORD error_code,
+ std::optional<MultiByteStringView> additional_message)
+ : runtime_error(Win32MakeMessage(error_code, std::nullopt)),
+ error_code_(error_code) {}
+} // namespace cru
diff --git a/src/exception.hpp b/src/exception.hpp
index b8cef604..db2572f1 100644
--- a/src/exception.hpp
+++ b/src/exception.hpp
@@ -1,60 +1,52 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
-#include "system_headers.hpp"
#include <optional>
+#include "system_headers.hpp"
#include "base.hpp"
-
namespace cru {
- class HResultError : public std::runtime_error
- {
- public:
- explicit HResultError(HRESULT h_result, std::optional<MultiByteStringView> additional_message = std::nullopt);
- HResultError(const HResultError& other) = default;
- HResultError(HResultError&& other) = default;
- HResultError& operator=(const HResultError& other) = default;
- HResultError& operator=(HResultError&& other) = default;
- ~HResultError() override = default;
-
- HRESULT GetHResult() const
- {
- return h_result_;
- }
-
- private:
- HRESULT h_result_;
- };
-
- inline void ThrowIfFailed(const HRESULT h_result) {
- if (FAILED(h_result))
- throw HResultError(h_result);
- }
-
- inline void ThrowIfFailed(const HRESULT h_result, const MultiByteStringView& message) {
- if (FAILED(h_result))
- throw HResultError(h_result, message);
- }
-
- class Win32Error : public std::runtime_error
- {
- public:
- explicit Win32Error(DWORD error_code, std::optional<MultiByteStringView> additional_message = std::nullopt);
- Win32Error(const Win32Error& other) = default;
- Win32Error(Win32Error&& other) = default;
- Win32Error& operator=(const Win32Error& other) = default;
- Win32Error& operator=(Win32Error&& other) = default;
- ~Win32Error() override = default;
-
- HRESULT GetErrorCode() const
- {
- return error_code_;
- }
-
- private:
- DWORD error_code_;
- };
+class HResultError : public std::runtime_error {
+ public:
+ explicit HResultError(
+ HRESULT h_result,
+ std::optional<MultiByteStringView> additional_message = std::nullopt);
+ HResultError(const HResultError& other) = default;
+ HResultError(HResultError&& other) = default;
+ HResultError& operator=(const HResultError& other) = default;
+ HResultError& operator=(HResultError&& other) = default;
+ ~HResultError() override = default;
+
+ HRESULT GetHResult() const { return h_result_; }
+
+ private:
+ HRESULT h_result_;
+};
+
+inline void ThrowIfFailed(const HRESULT h_result) {
+ if (FAILED(h_result)) throw HResultError(h_result);
}
+
+inline void ThrowIfFailed(const HRESULT h_result,
+ const MultiByteStringView& message) {
+ if (FAILED(h_result)) throw HResultError(h_result, message);
+}
+
+class Win32Error : public std::runtime_error {
+ public:
+ explicit Win32Error(
+ DWORD error_code,
+ std::optional<MultiByteStringView> additional_message = std::nullopt);
+ Win32Error(const Win32Error& other) = default;
+ Win32Error(Win32Error&& other) = default;
+ Win32Error& operator=(const Win32Error& other) = default;
+ Win32Error& operator=(Win32Error&& other) = default;
+ ~Win32Error() override = default;
+
+ HRESULT GetErrorCode() const { return error_code_; }
+
+ private:
+ DWORD error_code_;
+};
+} // namespace cru
diff --git a/src/format.hpp b/src/format.hpp
deleted file mode 100644
index efd25f89..00000000
--- a/src/format.hpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-#include "base.hpp"
-
-namespace cru
-{
- namespace details
- {
- constexpr StringView PlaceHolder(type_tag<String>)
- {
- return StringView(L"{}");
- }
-
- constexpr MultiByteStringView PlaceHolder(type_tag<MultiByteString>)
- {
- return MultiByteStringView("{}");
- }
-
- template<typename TString>
- void FormatInternal(TString& string)
- {
- const auto find_result = string.find(PlaceHolder(type_tag<TString>{}));
- if (find_result != TString::npos)
- throw std::invalid_argument("There is more placeholders than args.");
- }
-
- template<typename TString, typename T, typename... TRest>
- void FormatInternal(TString& string, const T& arg, const TRest&... args)
- {
- const auto find_result = string.find(PlaceHolder(type_tag<TString>{}));
- if (find_result == TString::npos)
- throw std::invalid_argument("There is less placeholders than args.");
-
- string.replace(find_result, 2, FormatToString(arg, type_tag<TString>{}));
- FormatInternal<TString>(string, args...);
- }
- }
-
- template<typename... T>
- String Format(const StringView& format, const T&... args)
- {
- String result(format);
- details::FormatInternal<String>(result, args...);
- return result;
- }
-
- template<typename... T>
- MultiByteString Format(const MultiByteStringView& format, const T&... args)
- {
- MultiByteString result(format);
- details::FormatInternal<MultiByteString>(result, args...);
- return result;
- }
-
-#define CRU_FORMAT_NUMBER(type) \
- inline String FormatToString(const type number, type_tag<String>) \
- { \
- return std::to_wstring(number); \
- } \
- inline MultiByteString FormatToString(const type number, type_tag<MultiByteString>) \
- { \
- return std::to_string(number); \
- }
-
- CRU_FORMAT_NUMBER(int)
- CRU_FORMAT_NUMBER(short)
- CRU_FORMAT_NUMBER(long)
- CRU_FORMAT_NUMBER(long long)
- CRU_FORMAT_NUMBER(unsigned int)
- CRU_FORMAT_NUMBER(unsigned short)
- CRU_FORMAT_NUMBER(unsigned long)
- CRU_FORMAT_NUMBER(unsigned long long)
- CRU_FORMAT_NUMBER(float)
- CRU_FORMAT_NUMBER(double)
-
-#undef CRU_FORMAT_NUMBER
-
- inline StringView FormatToString(const String& string, type_tag<String>)
- {
- return string;
- }
-
- inline MultiByteString FormatToString(const MultiByteString& string, type_tag<MultiByteString>)
- {
- return string;
- }
-
- inline StringView FormatToString(const StringView& string, type_tag<String>)
- {
- return string;
- }
-
- inline MultiByteStringView FormatToString(const MultiByteStringView& string, type_tag<MultiByteString>)
- {
- return string;
- }
-
- inline StringView FormatToString(const wchar_t* string, type_tag<String>)
- {
- return StringView(string);
- }
-
- inline MultiByteStringView FormatToString(const char* string, type_tag<MultiByteString>)
- {
- return MultiByteString(string);
- }
-}
diff --git a/src/math_util.hpp b/src/math_util.hpp
deleted file mode 100644
index 8f0741b8..00000000
--- a/src/math_util.hpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include "pre.hpp"
-
-// ReSharper disable once CppUnusedIncludeDirective
-#include <type_traits>
-#include <optional>
-
-namespace cru
-{
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const std::optional<T> min, const std::optional<T> max)
- {
- if (min.has_value() && n < min.value())
- return min.value();
- if (max.has_value() && n > max.value())
- return max.value();
- return n;
- }
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const T min, const T max)
- {
- if (n < min)
- return min;
- if (n > max)
- return max;
- return n;
- }
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const std::nullopt_t, const std::optional<T> max)
- {
- if (max.has_value() && n > max.value())
- return max.value();
- return n;
- }
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const std::optional<T> min, const std::nullopt_t)
- {
- if (min.has_value() && n < min.value())
- return min.value();
- return n;
- }
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const std::nullopt_t, const T max)
- {
- if (n > max)
- return max;
- return n;
- }
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- float Coerce(const T n, const T min, const std::nullopt_t)
- {
- if (n < min)
- return min;
- return n;
- }
-
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
- T AtLeast0(const T value)
- {
- return value < static_cast<T>(0) ? static_cast<T>(0) : value;
- }
-}
diff --git a/src/pre.hpp b/src/pre.hpp
index 03c51a94..ba47dc6c 100644
--- a/src/pre.hpp
+++ b/src/pre.hpp
@@ -8,11 +8,8 @@
#define CRU_DEBUG_LAYOUT
#endif
-
#ifdef CRU_DEBUG
-// ReSharper disable once IdentifierTypo
-// ReSharper disable once CppInconsistentNaming
#define _CRTDBG_MAP_ALLOC
-#include <cstdlib>
#include <crtdbg.h>
+#include <cstdlib>
#endif
diff --git a/src/system_headers.hpp b/src/system_headers.hpp
index eabc7c25..6cbad697 100644
--- a/src/system_headers.hpp
+++ b/src/system_headers.hpp
@@ -1,6 +1,4 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
//include system headers
diff --git a/src/timer.cpp b/src/timer.cpp
index c839a48d..40e32640 100644
--- a/src/timer.cpp
+++ b/src/timer.cpp
@@ -2,62 +2,51 @@
#include "application.hpp"
-namespace cru
-{
- TimerManager* TimerManager::GetInstance()
- {
- return Application::GetInstance()->ResolveSingleton<TimerManager>([](auto)
- {
- return new TimerManager{};
- });
- }
-
- UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop, const TimerAction& action)
- {
- const auto id = current_count_++;
- ::SetTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id, milliseconds, nullptr);
- map_.emplace(id, std::make_pair(loop, action));
- return id;
- }
-
- void TimerManager::KillTimer(const UINT_PTR id)
- {
- const auto find_result = map_.find(id);
- if (find_result != map_.cend())
- {
- ::KillTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id);
- map_.erase(find_result);
- }
- }
-
- std::optional<std::pair<bool, TimerAction>> TimerManager::GetAction(const UINT_PTR id)
- {
- const auto find_result = map_.find(id);
- if (find_result == map_.cend())
- return std::nullopt;
- return find_result->second;
- }
-
- TimerTask::TimerTask(const UINT_PTR id)
- : id_(id)
- {
-
- }
-
- void TimerTask::Cancel() const
- {
- TimerManager::GetInstance()->KillTimer(id_);
- }
-
- TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action)
- {
- const auto id = TimerManager::GetInstance()->CreateTimer(static_cast<UINT>(milliseconds.count()), false, action);
- return TimerTask(id);
- }
-
- TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action)
- {
- const auto id = TimerManager::GetInstance()->CreateTimer(static_cast<UINT>(milliseconds.count()), true, action);
- return TimerTask(id);
- }
+namespace cru {
+TimerManager* TimerManager::GetInstance() {
+ return Application::GetInstance()->ResolveSingleton<TimerManager>(
+ [](auto) { return new TimerManager{}; });
}
+
+UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop,
+ const TimerAction& action) {
+ const auto id = current_count_++;
+ ::SetTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id,
+ milliseconds, nullptr);
+ map_.emplace(id, std::make_pair(loop, action));
+ return id;
+}
+
+void TimerManager::KillTimer(const UINT_PTR id) {
+ const auto find_result = map_.find(id);
+ if (find_result != map_.cend()) {
+ ::KillTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id);
+ map_.erase(find_result);
+ }
+}
+
+std::optional<std::pair<bool, TimerAction>> TimerManager::GetAction(
+ const UINT_PTR id) {
+ const auto find_result = map_.find(id);
+ if (find_result == map_.cend()) return std::nullopt;
+ return find_result->second;
+}
+
+TimerTask::TimerTask(const UINT_PTR id) : id_(id) {}
+
+void TimerTask::Cancel() const { TimerManager::GetInstance()->KillTimer(id_); }
+
+TimerTask SetTimeout(std::chrono::milliseconds milliseconds,
+ const TimerAction& action) {
+ const auto id = TimerManager::GetInstance()->CreateTimer(
+ static_cast<UINT>(milliseconds.count()), false, action);
+ return TimerTask(id);
+}
+
+TimerTask SetInterval(std::chrono::milliseconds milliseconds,
+ const TimerAction& action) {
+ const auto id = TimerManager::GetInstance()->CreateTimer(
+ static_cast<UINT>(milliseconds.count()), true, action);
+ return TimerTask(id);
+}
+} // namespace cru
diff --git a/src/timer.hpp b/src/timer.hpp
index 5055a3d8..685e83b9 100644
--- a/src/timer.hpp
+++ b/src/timer.hpp
@@ -1,64 +1,64 @@
#pragma once
-
-// ReSharper disable once CppUnusedIncludeDirective
#include "pre.hpp"
-#include "system_headers.hpp"
-#include <map>
#include <chrono>
#include <functional>
+#include <map>
#include <optional>
+#include "system_headers.hpp"
#include "base.hpp"
-namespace cru
-{
- using TimerAction = std::function<void()>;
+namespace cru {
+using TimerAction = std::function<void()>;
+
+class TimerManager : public Object {
+ public:
+ static TimerManager* GetInstance();
- class TimerManager : public Object
- {
- public:
- static TimerManager* GetInstance();
+ private:
+ TimerManager() = default;
- private:
- TimerManager() = default;
- public:
- TimerManager(const TimerManager& other) = delete;
- TimerManager(TimerManager&& other) = delete;
- TimerManager& operator=(const TimerManager& other) = delete;
- TimerManager& operator=(TimerManager&& other) = delete;
- ~TimerManager() override = default;
+ public:
+ TimerManager(const TimerManager& other) = delete;
+ TimerManager(TimerManager&& other) = delete;
+ TimerManager& operator=(const TimerManager& other) = delete;
+ TimerManager& operator=(TimerManager&& other) = delete;
+ ~TimerManager() override = default;
- UINT_PTR CreateTimer(UINT milliseconds, bool loop, const TimerAction& action);
- void KillTimer(UINT_PTR id);
- std::optional<std::pair<bool, TimerAction>> GetAction(UINT_PTR id);
+ UINT_PTR CreateTimer(UINT milliseconds, bool loop, const TimerAction& action);
+ void KillTimer(UINT_PTR id);
+ std::optional<std::pair<bool, TimerAction>> GetAction(UINT_PTR id);
- private:
- std::map<UINT_PTR, std::pair<bool, TimerAction>> map_{};
- UINT_PTR current_count_ = 0;
- };
+ private:
+ std::map<UINT_PTR, std::pair<bool, TimerAction>> map_{};
+ UINT_PTR current_count_ = 0;
+};
- class TimerTask
- {
- friend TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action);
- friend TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action);
+class TimerTask {
+ friend TimerTask SetTimeout(std::chrono::milliseconds milliseconds,
+ const TimerAction& action);
+ friend TimerTask SetInterval(std::chrono::milliseconds milliseconds,
+ const TimerAction& action);
- private:
- explicit TimerTask(UINT_PTR id);
+ private:
+ explicit TimerTask(UINT_PTR id);
- public:
- TimerTask(const TimerTask& other) = default;
- TimerTask(TimerTask&& other) = default;
- TimerTask& operator=(const TimerTask& other) = default;
- TimerTask& operator=(TimerTask&& other) = default;
- ~TimerTask() = default;
+ public:
+ TimerTask(const TimerTask& other) = default;
+ TimerTask(TimerTask&& other) = default;
+ TimerTask& operator=(const TimerTask& other) = default;
+ TimerTask& operator=(TimerTask&& other) = default;
+ ~TimerTask() = default;
- void Cancel() const;
+ void Cancel() const;
- private:
- UINT_PTR id_;
- };
+ private:
+ UINT_PTR id_;
+};
- TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action);
- TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action);
-}
+TimerTask SetTimeout(std::chrono::milliseconds milliseconds,
+ const TimerAction& action);
+TimerTask SetInterval(std::chrono::milliseconds milliseconds,
+ const TimerAction& action);
+} // namespace cru
diff --git a/src/ui/control.cpp b/src/ui/control.cpp
index 9388c719..617c50c7 100644
--- a/src/ui/control.cpp
+++ b/src/ui/control.cpp
@@ -17,53 +17,6 @@
namespace cru::ui
{
- Control::Control()
- {
- mouse_leave_event.bubble.AddHandler([this](events::MouseEventArgs& args)
- {
- if (args.GetOriginalSender() != this)
- return;
- for (auto& is_mouse_click_valid : is_mouse_click_valid_map_)
- {
- if (is_mouse_click_valid.second)
- {
- is_mouse_click_valid.second = false;
- OnMouseClickEnd(is_mouse_click_valid.first);
- }
- }
- });
-
- mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
- {
- if (args.GetOriginalSender() != this)
- return;
-
- if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender())
- RequestFocus();
- const auto button = args.GetMouseButton();
- is_mouse_click_valid_map_[button] = true;
- OnMouseClickBegin(button);
- });
-
- mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
- {
- if (args.GetOriginalSender() != this)
- return;
-
- const auto button = args.GetMouseButton();
- if (is_mouse_click_valid_map_[button])
- {
- is_mouse_click_valid_map_[button] = false;
- OnMouseClickEnd(button);
- const auto point = args.GetPoint(GetWindow());
- InvokeLater([this, button, point]
- {
- DispatchEvent(this, &Control::mouse_click_event, nullptr, point, button);
- });
- }
- });
- }
-
void Control::SetParent(Control* parent)
{
@@ -74,19 +27,6 @@ namespace cru::ui
OnParentChanged(old_parent, new_parent);
}
- void Control::SetInternalParent(Control* internal_parent)
- {
- const auto old_internal_parent = GetInternalParent();
- const auto old_parent = GetParent();
- internal_parent_ = internal_parent;
- const auto new_internal_parent = GetInternalParent();
- const auto new_parent = GetParent();
- if (old_parent != new_parent)
- OnParentChanged(old_parent, new_parent);
- if (old_internal_parent != new_internal_parent)
- OnInternalParentChanged(old_internal_parent, new_internal_parent);
- }
-
void Control::SetDescendantWindow(Window* window)
{
if (window == nullptr && window_ == nullptr)
@@ -112,199 +52,29 @@ namespace cru::ui
});
}
-
- void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate)
- {
- predicate(control);
- for (auto c: control->GetInternalChildren())
- TraverseDescendantsInternal(c, predicate);
- }
-
void Control::TraverseDescendants(const std::function<void(Control*)>& predicate)
{
TraverseDescendantsInternal(this, predicate);
}
- Point Control::GetOffset()
- {
- return rect_.GetLeftTop();
- }
-
- Size Control::GetSize()
- {
- return rect_.GetSize();
- }
-
- void Control::SetRect(const Rect& rect)
- {
- const auto old_rect = rect_;
- rect_ = rect;
-
- RegenerateGeometryInfo();
-
- OnRectChange(old_rect, rect);
-
- if (auto window = GetWindow())
- window->InvalidateDraw();
- }
-
- namespace
+ void Control::TraverseDescendantsInternal(Control * control, const std::function<void(Control*)>& predicate)
{
-#ifdef CRU_DEBUG_LAYOUT
- Microsoft::WRL::ComPtr<ID2D1Geometry> CalculateSquareRingGeometry(const Rect& out, const Rect& in)
- {
- const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory();
- Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> out_geometry;
- ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry));
- Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> in_geometry;
- ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry));
- Microsoft::WRL::ComPtr<ID2D1PathGeometry> result_geometry;
- ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry));
- Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink;
- ThrowIfFailed(result_geometry->Open(&sink));
- ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get()));
- ThrowIfFailed(sink->Close());
- return result_geometry;
- }
-#endif
+ predicate(control);
+ for (auto c: control->GetInternalChildren())
+ TraverseDescendantsInternal(c, predicate);
}
- Point Control::GetPositionAbsolute() const
- {
- return position_cache_.lefttop_position_absolute;
- }
Point Control::ControlToWindow(const Point& point) const
{
- return Point(point.x + position_cache_.lefttop_position_absolute.x,
- point.y + position_cache_.lefttop_position_absolute.y);
+ const auto position = GetPositionInWindow();
+ return Point(point.x + position.x, point.y + position.y);
}
Point Control::WindowToControl(const Point & point) const
{
- return Point(point.x - position_cache_.lefttop_position_absolute.x,
- point.y - position_cache_.lefttop_position_absolute.y);
- }
-
- void Control::RefreshDescendantPositionCache()
- {
- auto point = Point::Zero();
- auto parent = this;
- while ((parent = parent->GetParent()))
- {
- const auto p = parent->GetOffset();
- point.x += p.x;
- point.y += p.y;
- }
- RefreshControlPositionCacheInternal(this, point);
- }
-
- void Control::RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute)
- {
- const auto position = control->GetOffset();
- const Point lefttop(
- parent_lefttop_absolute.x + position.x,
- parent_lefttop_absolute.y + position.y
- );
- control->position_cache_.lefttop_position_absolute = lefttop;
- for(auto c : control->GetInternalChildren())
- {
- RefreshControlPositionCacheInternal(c, lefttop);
- }
- }
-
- bool Control::IsPointInside(const Point & point)
- {
- const auto border_geometry = geometry_info_.border_geometry;
- if (border_geometry != nullptr)
- {
- if (IsBordered())
- {
- BOOL contains;
- border_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains);
- if (!contains)
- border_geometry->StrokeContainsPoint(Convert(point), GetBorderProperty().GetStrokeWidth(), nullptr, D2D1::Matrix3x2F::Identity(), &contains);
- return contains != 0;
- }
- else
- {
- BOOL contains;
- border_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains);
- return contains != 0;
- }
- }
- return false;
- }
-
- Control* Control::HitTest(const Point& point)
- {
- const auto point_inside = IsPointInside(point);
-
- if (IsClipContent())
- {
- if (!point_inside)
- return nullptr;
- if (geometry_info_.content_geometry != nullptr)
- {
- BOOL contains;
- ThrowIfFailed(geometry_info_.content_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains));
- if (contains == 0)
- return this;
- }
- }
-
- const auto& children = GetInternalChildren();
-
- for (auto i = children.crbegin(); i != children.crend(); ++i)
- {
- const auto&& lefttop = (*i)->GetOffset();
- const auto&& coerced_point = Point(point.x - lefttop.x, point.y - lefttop.y);
- const auto child_hit_test_result = (*i)->HitTest(coerced_point);
- if (child_hit_test_result != nullptr)
- return child_hit_test_result;
- }
-
- return point_inside ? this : nullptr;
- }
-
- void Control::SetClipContent(const bool clip)
- {
- if (clip_content_ == clip)
- return;
-
- clip_content_ = clip;
- InvalidateDraw();
- }
-
- void Control::Draw(ID2D1DeviceContext* device_context)
- {
- D2D1::Matrix3x2F old_transform;
- device_context->GetTransform(&old_transform);
-
- const auto position = GetOffset();
- device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y));
-
- OnDrawDecoration(device_context);
-
- const auto set_layer = geometry_info_.content_geometry != nullptr && IsClipContent();
- if (set_layer)
- device_context->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), geometry_info_.content_geometry.Get()), nullptr);
-
- OnDrawCore(device_context);
-
- for (auto child : GetInternalChildren())
- child->Draw(device_context);
-
- if (set_layer)
- device_context->PopLayer();
-
- device_context->SetTransform(old_transform);
- }
-
- void Control::InvalidateDraw()
- {
- if (window_ != nullptr)
- window_->InvalidateDraw();
+ const auto position = GetPositionInWindow();
+ return Point(point.x - position.x, point.y - position.y);
}
bool Control::RequestFocus()
@@ -325,109 +95,6 @@ namespace cru::ui
return window->GetFocusControl() == this;
}
- void Control::InvalidateLayout()
- {
- if (const auto window = GetWindow())
- window->WindowInvalidateLayout();
- }
-
- void Control::Measure(const Size& available_size, const AdditionalMeasureInfo& additional_info)
- {
- SetDesiredSize(OnMeasureCore(available_size, additional_info));
- }
-
- void Control::Layout(const Rect& rect, const AdditionalLayoutInfo& additional_info)
- {
- auto my_additional_info = additional_info;
- my_additional_info.total_offset.x += rect.left;
- my_additional_info.total_offset.y += rect.top;
- position_cache_.lefttop_position_absolute.x = my_additional_info.total_offset.x;
- position_cache_.lefttop_position_absolute.y = my_additional_info.total_offset.y;
-
- SetRect(rect);
- OnLayoutCore(Rect(Point::Zero(), rect.GetSize()), my_additional_info);
- }
-
- Size Control::GetDesiredSize() const
- {
- return desired_size_;
- }
-
- void Control::SetDesiredSize(const Size& desired_size)
- {
- desired_size_ = desired_size;
- }
-
- inline void Shrink(Rect& rect, const Thickness& thickness)
- {
- rect.left += thickness.left;
- rect.top += thickness.top;
- rect.width -= thickness.GetHorizontalTotal();
- rect.height -= thickness.GetVerticalTotal();
- }
-
- Rect Control::GetRect(const RectRange range)
- {
- if (GetSize() == Size::Zero())
- return Rect();
-
- const auto layout_params = GetLayoutParams();
-
- auto result = Rect(Point::Zero(), GetSize());
-
- if (range == RectRange::Margin)
- return result;
-
- Shrink(result, layout_params->margin);
-
- if (range == RectRange::FullBorder)
- return result;
-
- if (is_bordered_)
- Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f));
-
- if (range == RectRange::HalfBorder)
- return result;
-
- if (is_bordered_)
- Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f));
-
- if (range == RectRange::Padding)
- return result;
-
- Shrink(result, layout_params->padding);
-
- return result;
- }
-
- Point Control::TransformPoint(const Point& point, const RectRange from, const RectRange to)
- {
- const auto rect_from = GetRect(from);
- const auto rect_to = GetRect(to);
- auto p = point;
- p.x += rect_from.left;
- p.y += rect_from.top;
- p.x -= rect_to.left;
- p.y -= rect_to.top;
- return p;
- }
-
- void Control::UpdateBorder()
- {
- RegenerateGeometryInfo();
- InvalidateLayout();
- InvalidateDraw();
- }
-
- void Control::SetBordered(const bool bordered)
- {
- if (bordered != is_bordered_)
- {
- is_bordered_ = bordered;
- UpdateBorder();
- }
- }
-
void Control::SetCursor(const Cursor::Ptr& cursor)
{
if (cursor != cursor_)
@@ -444,289 +111,41 @@ namespace cru::ui
}
- void Control::OnInternalParentChanged(Control* old_internal_parent, Control* new_internal_parent)
- {
-
- }
-
void Control::OnAttachToWindow(Window* window)
{
- window_ = window;
- }
-
- void Control::OnDetachToWindow(Window * window)
- {
- window_ = nullptr;
- }
-
- void Control::OnDrawDecoration(ID2D1DeviceContext* device_context)
- {
-#ifdef CRU_DEBUG_LAYOUT
- if (GetWindow()->IsDebugLayout())
- {
- if (padding_geometry_ != nullptr)
- device_context->FillGeometry(padding_geometry_.Get(), UiManager::GetInstance()->GetPredefineResources()->debug_layout_padding_brush.Get());
- if (margin_geometry_ != nullptr)
- device_context->FillGeometry(margin_geometry_.Get(), UiManager::GetInstance()->GetPredefineResources()->debug_layout_margin_brush.Get());
- device_context->DrawRectangle(Convert(GetRect(RectRange::Margin)), UiManager::GetInstance()->GetPredefineResources()->debug_layout_out_border_brush.Get());
- }
-#endif
-
- if (is_bordered_ && geometry_info_.border_geometry != nullptr)
- device_context->DrawGeometry(
- geometry_info_.border_geometry.Get(),
- GetBorderProperty().GetBrush().Get(),
- GetBorderProperty().GetStrokeWidth(),
- GetBorderProperty().GetStrokeStyle().Get()
- );
- }
-
- void Control::OnDrawCore(ID2D1DeviceContext* device_context)
- {
- const auto ground_geometry = geometry_info_.padding_content_geometry;
- //draw background.
- if (ground_geometry != nullptr && background_brush_ != nullptr)
- device_context->FillGeometry(ground_geometry.Get(), background_brush_.Get());
- const auto padding_rect = GetRect(RectRange::Padding);
- graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(padding_rect.left, padding_rect.top),
- [this](ID2D1DeviceContext* device_context)
- {
- events::DrawEventArgs args(this, this, device_context);
- draw_background_event.Raise(args);
- });
-
-
- const auto rect = GetRect(RectRange::Content);
- graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top),
- [this](ID2D1DeviceContext* device_context)
- {
- events::DrawEventArgs args(this, this, device_context);
- draw_content_event.Raise(args);
- });
-
-
- //draw foreground.
- if (ground_geometry != nullptr && foreground_brush_ != nullptr)
- device_context->FillGeometry(ground_geometry.Get(), foreground_brush_.Get());
- graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(padding_rect.left, padding_rect.top),
- [this](ID2D1DeviceContext* device_context)
- {
- events::DrawEventArgs args(this, this, device_context);
- draw_foreground_event.Raise(args);
- });
- }
-
- void Control::OnRectChange(const Rect& old_rect, const Rect& new_rect)
- {
}
- void Control::RegenerateGeometryInfo()
+ void Control::OnDetachToWindow(Window * window)
{
- if (IsBordered())
- {
- const auto bound_rect = GetRect(RectRange::HalfBorder);
- const auto bound_rounded_rect = D2D1::RoundedRect(Convert(bound_rect),
- GetBorderProperty().GetRadiusX(),
- GetBorderProperty().GetRadiusY());
-
- Microsoft::WRL::ComPtr<ID2D1RoundedRectangleGeometry> geometry;
- ThrowIfFailed(
- graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(bound_rounded_rect, &geometry)
- );
- geometry_info_.border_geometry = std::move(geometry);
-
- const auto padding_rect = GetRect(RectRange::Padding);
- const auto in_border_rounded_rect = D2D1::RoundedRect(Convert(padding_rect),
- GetBorderProperty().GetRadiusX() - GetBorderProperty().GetStrokeWidth() / 2.0f,
- GetBorderProperty().GetRadiusY() - GetBorderProperty().GetStrokeWidth() / 2.0f);
-
- Microsoft::WRL::ComPtr<ID2D1RoundedRectangleGeometry> geometry2;
- ThrowIfFailed(
- graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(in_border_rounded_rect, &geometry2)
- );
- geometry_info_.padding_content_geometry = geometry2;
-
-
- Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> geometry3;
- ThrowIfFailed(
- graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(GetRect(RectRange::Content)), &geometry3)
- );
- Microsoft::WRL::ComPtr<ID2D1PathGeometry> geometry4;
- ThrowIfFailed(
- graph::GraphManager::GetInstance()->GetD2D1Factory()->CreatePathGeometry(&geometry4)
- );
- Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink;
- geometry4->Open(&sink);
- ThrowIfFailed(
- geometry3->CombineWithGeometry(geometry2.Get(), D2D1_COMBINE_MODE_INTERSECT, D2D1::Matrix3x2F::Identity(), sink.Get())
- );
- sink->Close();
- geometry_info_.content_geometry = std::move(geometry4);
- }
- else
- {
- const auto bound_rect = GetRect(RectRange::Padding);
- Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> geometry;
- ThrowIfFailed(
- graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(bound_rect), &geometry)
- );
- geometry_info_.border_geometry = geometry;
- geometry_info_.padding_content_geometry = std::move(geometry);
-
- Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> geometry2;
- ThrowIfFailed(
- graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(GetRect(RectRange::Content)), &geometry2)
- );
- geometry_info_.content_geometry = std::move(geometry2);
- }
- //TODO: generate debug geometry
-#ifdef CRU_DEBUG_LAYOUT
- margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder));
- padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content));
-#endif
}
void Control::OnMouseClickBegin(MouseButton button)
{
- }
- void Control::OnMouseClickEnd(MouseButton button)
- {
}
- inline Size ThicknessToSize(const Thickness& thickness)
- {
- return Size(thickness.left + thickness.right, thickness.top + thickness.bottom);
- }
-
- Size Control::OnMeasureCore(const Size& available_size, const AdditionalMeasureInfo& additional_info)
+ void Control::OnMouseClickEnd(MouseButton button)
{
- const auto layout_params = GetLayoutParams();
-
- if (!layout_params->Validate())
- throw std::runtime_error("LayoutParams is not valid. Please check it.");
-
- auto my_additional_info = additional_info;
-
- if (layout_params->width.mode == MeasureMode::Content)
- my_additional_info.horizontal_stretchable = false;
- else if (layout_params->width.mode == MeasureMode::Exactly)
- my_additional_info.horizontal_stretchable = true;
- // if stretch, then inherent parent's value
-
- if (layout_params->height.mode == MeasureMode::Content)
- my_additional_info.vertical_stretchable = false;
- else if (layout_params->height.mode == MeasureMode::Exactly)
- my_additional_info.vertical_stretchable = true;
- // if stretch, then inherent parent's value
-
- auto border_size = Size::Zero();
- if (is_bordered_)
- {
- const auto border_width = GetBorderProperty().GetStrokeWidth();
- border_size = Size(border_width * 2.0f, border_width * 2.0f);
- }
-
- // the total size of padding, border and margin
- const auto outer_size = ThicknessToSize(layout_params->padding) +
- ThicknessToSize(layout_params->margin) + border_size;
-
-
- auto&& get_content_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float outer_length) -> float
- {
- float length;
- if (layout_length.mode == MeasureMode::Exactly)
- length = layout_length.length;
- else if (available_length > outer_length)
- length = available_length - outer_length;
- else
- length = 0;
- return Coerce(length, layout_length.min, layout_length.max);
- };
-
- // if padding, margin and border exceeded, then content size is 0.
- const auto content_measure_size = Size(
- get_content_measure_length(layout_params->width, available_size.width, outer_size.width),
- get_content_measure_length(layout_params->height, available_size.height, outer_size.height)
- );
-
- const auto content_actual_size = OnMeasureContent(content_measure_size, my_additional_info);
-
-
-
- auto&& calculate_final_length = [](const bool stretch, const std::optional<float> min_length, const float measure_length, const float actual_length) -> float
- {
- // only use measure length when stretch and actual length is smaller than measure length, that is "stretch"
- if (stretch && actual_length < measure_length)
- return measure_length;
- return Coerce(actual_length, min_length, std::nullopt);
- };
-
- const auto final_size = Size(
- calculate_final_length(my_additional_info.horizontal_stretchable, layout_params->width.min, content_measure_size.width, content_actual_size.width),
- calculate_final_length(my_additional_info.vertical_stretchable, layout_params->height.min, content_measure_size.height, content_actual_size.height)
- ) + outer_size;
-
- return final_size;
}
- void Control::OnLayoutCore(const Rect& rect, const AdditionalLayoutInfo& additional_info)
- {
- const auto layout_params = GetLayoutParams();
-
- auto border_width = 0.0f;
- if (is_bordered_)
- {
- border_width = GetBorderProperty().GetStrokeWidth();
- }
-
- const Rect content_rect(
- rect.left + layout_params->padding.left + layout_params->margin.right + border_width,
- rect.top + layout_params->padding.top + layout_params->margin.top + border_width,
- rect.width - layout_params->padding.GetHorizontalTotal() - layout_params->margin.GetHorizontalTotal() - border_width * 2.0f,
- rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() - border_width * 2.0f
- );
-
- if (content_rect.width < 0.0)
- throw std::runtime_error(Format("Width to layout must sufficient. But in {}, width for content is {}.", ToUtf8String(GetControlType()), content_rect.width));
- if (content_rect.height < 0.0)
- throw std::runtime_error(Format("Height to layout must sufficient. But in {}, height for content is {}.", ToUtf8String(GetControlType()), content_rect.height));
-
- OnLayoutContent(content_rect, additional_info);
- }
const std::vector<Control*> NoChildControl::empty_control_vector{};
- std::list<Control*> GetAncestorList(Control* control)
- {
- std::list<Control*> l;
- while (control != nullptr)
- {
- l.push_front(control);
- control = control->GetInternalParent();
- }
- return l;
- }
-
- void NoChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
- {
- }
-
- SingleChildControl::SingleChildControl() : child_vector_{nullptr}, child_(child_vector_[0])
+ ContentControl::ContentControl() : child_vector_{nullptr}, child_(child_vector_[0])
{
}
- SingleChildControl::~SingleChildControl()
+ ContentControl::~ContentControl()
{
delete child_;
}
- void SingleChildControl::SetChild(Control* child)
+ void ContentControl::SetChild(Control* child)
{
if (child == child_)
return;
@@ -736,66 +155,25 @@ namespace cru::ui
child_ = child;
if (old_child)
{
- old_child->SetInternalParent(nullptr);
+ old_child->SetParent(nullptr);
old_child->SetDescendantWindow(nullptr);
}
if (child)
{
- child->SetInternalParent(this);
+ child->SetParent(this);
child->SetDescendantWindow(window);
}
OnChildChanged(old_child, child);
}
- void SingleChildControl::OnChildChanged(Control* old_child, Control* new_child)
+ void ContentControl::OnChildChanged(Control* old_child, Control* new_child)
{
}
- Size SingleChildControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info)
+ void ControlAddChildCheck(Control* control)
{
- auto child_size = Size::Zero();
- if (child_)
- {
- child_->Measure(available_size, additional_info);
- child_size = child_->GetDesiredSize();
- }
-
- return child_size;
- }
-
- void SingleChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info)
- {
- if (child_)
- {
- const auto layout_params = child_->GetLayoutParams();
- const auto size = child_->GetDesiredSize();
-
- auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float
- {
- switch (alignment)
- {
- case Alignment::Center:
- return anchor + (layout_length - control_length) / 2;
- case Alignment::Start:
- return anchor;
- case Alignment::End:
- return anchor + layout_length - control_length;
- default:
- UnreachableCode();
- }
- };
-
- child_->Layout(Rect(Point(
- calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width),
- calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height)
- ), size), additional_info);
- }
- }
-
- void AddChildCheck(Control* control)
- {
- if (control->GetInternalParent() != nullptr)
+ if (control->GetParent() != nullptr)
throw std::invalid_argument("The control already has a parent.");
if (dynamic_cast<Window*>(control))
@@ -810,11 +188,11 @@ namespace cru::ui
void MultiChildControl::AddChild(Control* control)
{
- AddChildCheck(control);
+ ControlAddChildCheck(control);
children_.push_back(control);
- control->SetInternalParent(this);
+ control->SetParent(this);
control->SetDescendantWindow(GetWindow());
OnAddChild(control);
@@ -822,14 +200,14 @@ namespace cru::ui
void MultiChildControl::AddChild(Control* control, const int position)
{
- AddChildCheck(control);
+ ControlAddChildCheck(control);
if (position < 0 || static_cast<decltype(children_.size())>(position) > this->children_.size())
throw std::invalid_argument("The position is out of range.");
children_.insert(this->children_.cbegin() + position, control);
- control->SetInternalParent(this);
+ control->SetParent(this);
control->SetDescendantWindow(GetWindow());
OnAddChild(control);
@@ -843,7 +221,7 @@ namespace cru::ui
children_.erase(i);
- child->SetInternalParent(nullptr);
+ child->SetParent(nullptr);
child->SetDescendantWindow(nullptr);
OnRemoveChild(child);
@@ -859,7 +237,7 @@ namespace cru::ui
children_.erase(i);
- child->SetInternalParent(nullptr);
+ child->SetParent(nullptr);
child->SetDescendantWindow(nullptr);
OnRemoveChild(child);
@@ -875,6 +253,17 @@ namespace cru::ui
}
+ std::list<Control*> GetAncestorList(Control* control)
+ {
+ std::list<Control*> l;
+ while (control != nullptr)
+ {
+ l.push_front(control);
+ control = control->GetParent();
+ }
+ return l;
+ }
+
Control* FindLowestCommonAncestor(Control * left, Control * right)
{
if (left == nullptr || right == nullptr)
diff --git a/src/ui/control.hpp b/src/ui/control.hpp
index 6abcc365..8e69fb07 100644
--- a/src/ui/control.hpp
+++ b/src/ui/control.hpp
@@ -12,49 +12,21 @@
#include "ui_base.hpp"
#include "layout_base.hpp"
#include "events/ui_event.hpp"
-#include "border_property.hpp"
#include "cursor.hpp"
#include "any_map.hpp"
+#include "input_util.hpp"
namespace cru::ui
{
class Window;
- struct AdditionalMeasureInfo
- {
- bool horizontal_stretchable = true;
- bool vertical_stretchable = true;
- };
-
- struct AdditionalLayoutInfo
- {
- Point total_offset = Point::Zero();
- };
-
- //the position cache
- struct ControlPositionCache
- {
- //The lefttop relative to the ancestor.
- Point lefttop_position_absolute = Point::Zero();
- };
-
-
class Control : public Object
{
friend class Window;
protected:
- struct GeometryInfo
- {
- Microsoft::WRL::ComPtr<ID2D1Geometry> border_geometry = nullptr;
- Microsoft::WRL::ComPtr<ID2D1Geometry> padding_content_geometry = nullptr;
- Microsoft::WRL::ComPtr<ID2D1Geometry> content_geometry = nullptr;
- };
-
-
- protected:
- Control();
+ Control() = default;
public:
Control(const Control& other) = delete;
Control(Control&& other) = delete;
@@ -63,53 +35,35 @@ namespace cru::ui
~Control() override = default;
public:
-
- //*************** region: tree ***************
virtual StringView GetControlType() const = 0;
- virtual const std::vector<Control*>& GetInternalChildren() const = 0;
-
- Control* GetParent() const
- {
- return parent_ == nullptr ? internal_parent_ : parent_;
- }
-
- Control* GetInternalParent() const
- {
- return internal_parent_;
- }
+ //*************** region: tree ***************
+ public:
//Get the window if attached, otherwise, return nullptr.
Window* GetWindow() const
{
return window_;
}
- void SetParent(Control* parent);
+ Control* GetParent() const
+ {
+ return parent_;
+ }
- void SetInternalParent(Control* internal_parent);
+ void SetParent(Control* parent);
void SetDescendantWindow(Window* window);
-
//Traverse the tree rooted the control including itself.
void TraverseDescendants(const std::function<void(Control*)>& predicate);
- //*************** region: position and size ***************
-
- //Get the lefttop relative to its parent.
- virtual Point GetOffset();
-
- //Get the actual size.
- virtual Size GetSize();
-
- // If offset changes, call RefreshDescendantPositionCache.
- virtual void SetRect(const Rect& rect);
+ private:
+ static void TraverseDescendantsInternal(Control* control, const std::function<void(Control*)>& predicate);
- //Get lefttop relative to ancestor. This is only valid when
- //attached to window. Notice that the value is cached.
- //You can invalidate and recalculate it by calling "InvalidatePositionCache".
- Point GetPositionAbsolute() const;
+ //*************** region: position ***************
+ public:
+ virtual Point GetPositionInWindow() const = 0;
//Local point to absolute point.
Point ControlToWindow(const Point& point) const;
@@ -117,122 +71,19 @@ namespace cru::ui
//Absolute point to local point.
Point WindowToControl(const Point& point) const;
- void RefreshDescendantPositionCache();
-
- private:
- static void RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute);
-
- public:
-
- // Default implement in Control is test point in border geometry's
- // fill and stroke with width of border.
- virtual bool IsPointInside(const Point& point);
-
- // Get the top control among all descendants (including self) in local coordinate.
- virtual Control* HitTest(const Point& point);
-
- //*************** region: graphic ***************
-
- bool IsClipContent() const
- {
- return clip_content_;
- }
-
- void SetClipContent(bool clip);
-
- //Draw this control and its child controls.
- void Draw(ID2D1DeviceContext* device_context);
-
- virtual void InvalidateDraw();
-
- Microsoft::WRL::ComPtr<ID2D1Brush> GetForegroundBrush() const
- {
- return foreground_brush_;
- }
-
- void SetForegroundBrush(Microsoft::WRL::ComPtr<ID2D1Brush> foreground_brush)
- {
- foreground_brush_ = std::move(foreground_brush);
- InvalidateDraw();
- }
-
- Microsoft::WRL::ComPtr<ID2D1Brush> GetBackgroundBrush() const
- {
- return background_brush_;
- }
-
- void SetBackgroundBrush(Microsoft::WRL::ComPtr<ID2D1Brush> background_brush)
- {
- background_brush_ = std::move(background_brush);
- InvalidateDraw();
- }
//*************** region: focus ***************
-
+ public:
bool RequestFocus();
bool HasFocus();
- bool IsFocusOnPressed() const
- {
- return is_focus_on_pressed_;
- }
-
- void SetFocusOnPressed(const bool value)
- {
- is_focus_on_pressed_ = value;
- }
-
- //*************** region: layout ***************
-
- void InvalidateLayout();
-
- void Measure(const Size& available_size, const AdditionalMeasureInfo& additional_info);
-
- void Layout(const Rect& rect, const AdditionalLayoutInfo& additional_info);
-
- Size GetDesiredSize() const;
-
- void SetDesiredSize(const Size& desired_size);
-
- BasicLayoutParams* GetLayoutParams()
- {
- return &layout_params_;
- }
-
- Rect GetRect(RectRange range);
-
- Point TransformPoint(const Point& point, RectRange from = RectRange::Margin, RectRange to = RectRange::Content);
-
- //*************** region: border ***************
-
- BorderProperty& GetBorderProperty()
- {
- return border_property_;
- }
-
- void UpdateBorder();
-
- bool IsBordered() const
- {
- return is_bordered_;
- }
-
- void SetBordered(bool bordered);
-
-
- //*************** region: additional properties ***************
- AnyMap* GetAdditionalPropertyMap()
- {
- return &additional_property_map_;
- }
-
//*************** region: cursor ***************
// If cursor is set to null, then it uses parent's cursor.
// Window's cursor can't be null.
-
+ public:
Cursor::Ptr GetCursor() const
{
return cursor_;
@@ -241,7 +92,16 @@ namespace cru::ui
void SetCursor(const Cursor::Ptr& cursor);
+ //*************** region: additional properties ***************
+ public:
+ AnyMap* GetAdditionalPropertyMap()
+ {
+ return &additional_property_map_;
+ }
+
+
//*************** region: events ***************
+ public:
//Raised when mouse enter the control.
events::RoutedEvent<events::MouseEventArgs> mouse_enter_event;
//Raised when mouse is leave the control.
@@ -264,95 +124,29 @@ namespace cru::ui
events::RoutedEvent<events::FocusChangeEventArgs> get_focus_event;
events::RoutedEvent<events::FocusChangeEventArgs> lose_focus_event;
- Event<events::DrawEventArgs> draw_content_event;
- Event<events::DrawEventArgs> draw_background_event;
- Event<events::DrawEventArgs> draw_foreground_event;
-
- //*************** region: tree event ***************
+ //*************** region: tree ***************
protected:
- virtual void OnParentChanged(Control* old_parent, Control* new_parent);
-
- virtual void OnInternalParentChanged(Control* old_internal_parent, Control* new_internal_parent);
+ virtual const std::vector<Control*>& GetInternalChildren() const = 0;
- //Invoked when the control is attached to a window. Overrides should invoke base.
+ virtual void OnParentChanged(Control* old_parent, Control* new_parent);
virtual void OnAttachToWindow(Window* window);
- //Invoked when the control is detached to a window. Overrides should invoke base.
virtual void OnDetachToWindow(Window* window);
- //*************** region: graphic event ***************
- private:
- void OnDrawDecoration(ID2D1DeviceContext* device_context);
- void OnDrawCore(ID2D1DeviceContext* device_context);
-
-
- //*************** region: position and size event ***************
- protected:
- virtual void OnRectChange(const Rect& old_rect, const Rect& new_rect);
-
- void RegenerateGeometryInfo();
-
- const GeometryInfo& GetGeometryInfo() const
- {
- return geometry_info_;
- }
-
-
- //*************** region: mouse event ***************
+ //*************** region: additional mouse event ***************
protected:
virtual void OnMouseClickBegin(MouseButton button);
virtual void OnMouseClickEnd(MouseButton button);
- //*************** region: layout ***************
- private:
- Size OnMeasureCore(const Size& available_size, const AdditionalMeasureInfo& additional_info);
- void OnLayoutCore(const Rect& rect, const AdditionalLayoutInfo& additional_info);
-
- protected:
- virtual Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) = 0;
- virtual void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) = 0;
-
private:
Window * window_ = nullptr;
- Control* parent_ = nullptr; // when parent and internal parent are the same, parent_ is nullptr.
- Control * internal_parent_ = nullptr;
-
- Rect rect_{};
-
- ControlPositionCache position_cache_{};
-
- std::unordered_map<MouseButton, bool> is_mouse_click_valid_map_
- {
- { MouseButton::Left, true },
- { MouseButton::Middle, true },
- { MouseButton::Right, true }
- }; // used for clicking determination
-
- BasicLayoutParams layout_params_{};
- Size desired_size_ = Size::Zero();
-
- bool is_bordered_ = false;
- BorderProperty border_property_;
-
- GeometryInfo geometry_info_{};
-
- bool clip_content_ = false;
+ Control* parent_ = nullptr;
- Microsoft::WRL::ComPtr<ID2D1Brush> foreground_brush_ = nullptr;
- Microsoft::WRL::ComPtr<ID2D1Brush> background_brush_ = nullptr;
+ Cursor::Ptr cursor_{};
AnyMap additional_property_map_{};
-
- bool is_focus_on_pressed_ = true;
-
-#ifdef CRU_DEBUG_LAYOUT
- Microsoft::WRL::ComPtr<ID2D1Geometry> margin_geometry_;
- Microsoft::WRL::ComPtr<ID2D1Geometry> padding_geometry_;
-#endif
-
- Cursor::Ptr cursor_{};
};
@@ -372,31 +166,24 @@ namespace cru::ui
NoChildControl& operator=(NoChildControl&& other) = delete;
~NoChildControl() override = default;
+ protected:
const std::vector<Control*>& GetInternalChildren() const override final
{
return empty_control_vector;
}
-
- protected:
- void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override;
};
- class SingleChildControl : public Control
+ class ContentControl : public Control
{
protected:
- SingleChildControl();
+ ContentControl();
public:
- SingleChildControl(const SingleChildControl& other) = delete;
- SingleChildControl(SingleChildControl&& other) = delete;
- SingleChildControl& operator=(const SingleChildControl& other) = delete;
- SingleChildControl& operator=(SingleChildControl&& other) = delete;
- ~SingleChildControl() override;
-
- const std::vector<Control*>& GetInternalChildren() const override final
- {
- return child_vector_;
- }
+ ContentControl(const ContentControl& other) = delete;
+ ContentControl(ContentControl&& other) = delete;
+ ContentControl& operator=(const ContentControl& other) = delete;
+ ContentControl& operator=(ContentControl&& other) = delete;
+ ~ContentControl() override;
Control* GetChild() const
{
@@ -406,12 +193,14 @@ namespace cru::ui
void SetChild(Control* child);
protected:
+ const std::vector<Control*>& GetInternalChildren() const override final
+ {
+ return child_vector_;
+ }
+
// Override should call base.
virtual void OnChildChanged(Control* old_child, Control* new_child);
- Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override;
- void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override;
-
private:
std::vector<Control*> child_vector_;
Control*& child_;
@@ -452,9 +241,7 @@ namespace cru::ui
void RemoveChild(int position);
protected:
- //Invoked when a child is added. Overrides should invoke base.
virtual void OnAddChild(Control* child);
- //Invoked when a child is removed. Overrides should invoke base.
virtual void OnRemoveChild(Control* child);
private:
diff --git a/src/ui/controls/button.hpp b/src/ui/controls/button.hpp
index 82694fe8..6436f7c0 100644
--- a/src/ui/controls/button.hpp
+++ b/src/ui/controls/button.hpp
@@ -9,7 +9,7 @@
namespace cru::ui::controls
{
- class Button : public SingleChildControl
+ class Button : public ContentControl
{
public:
static constexpr auto control_type = L"Button";
diff --git a/src/ui/controls/list_item.hpp b/src/ui/controls/list_item.hpp
index a50b2496..bf8f8d8e 100644
--- a/src/ui/controls/list_item.hpp
+++ b/src/ui/controls/list_item.hpp
@@ -10,7 +10,7 @@
namespace cru::ui::controls
{
- class ListItem : public SingleChildControl
+ class ListItem : public ContentControl
{
public:
static constexpr auto control_type = L"ListItem";
diff --git a/src/ui/controls/scroll_control.hpp b/src/ui/controls/scroll_control.hpp
index 7138add6..84ebca30 100644
--- a/src/ui/controls/scroll_control.hpp
+++ b/src/ui/controls/scroll_control.hpp
@@ -17,7 +17,7 @@ namespace cru::ui::controls
// Done: API
// Done: ScrollBar
// Done: MouseEvent
- class ScrollControl : public SingleChildControl
+ class ScrollControl : public ContentControl
{
private:
struct ScrollBarInfo
diff --git a/src/ui/render/linear_layout_render_object.cpp b/src/ui/render/linear_layout_render_object.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/ui/render/linear_layout_render_object.cpp
diff --git a/src/ui/render/linear_layout_render_object.hpp b/src/ui/render/linear_layout_render_object.hpp
new file mode 100644
index 00000000..6f70f09b
--- /dev/null
+++ b/src/ui/render/linear_layout_render_object.hpp
@@ -0,0 +1 @@
+#pragma once
diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp
index 0828fc9c..c2aaeb62 100644
--- a/src/ui/render/render_object.cpp
+++ b/src/ui/render/render_object.cpp
@@ -3,96 +3,88 @@
namespace cru::ui::render
{
- void RenderObject::SetRenderHost(IRenderHost* new_render_host)
- {
- if (new_render_host == render_host_)
- return;
-
- const auto old = render_host_;
- render_host_ = new_render_host;
- OnRenderHostChanged(old, new_render_host);
- }
-
- void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, IRenderHost* new_render_host)
- {
-
- }
-
- void RenderObject::InvalidateRenderHost()
- {
- if (render_host_ != nullptr)
- render_host_->InvalidateRender();
- }
-
- void StrokeRenderObject::SetStrokeWidth(const float new_stroke_width)
- {
- if (stroke_width_ == new_stroke_width)
- return;
-
- stroke_width_ = new_stroke_width;
- InvalidateRenderHost();
- }
-
- void StrokeRenderObject::SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> new_brush)
- {
- if (brush_ == new_brush)
- return;
-
- brush_ = std::move(new_brush);
- InvalidateRenderHost();
- }
-
- void StrokeRenderObject::SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> new_stroke_style)
- {
- if (stroke_style_ == new_stroke_style)
- return;
-
- stroke_style_ = std::move(new_stroke_style);
- InvalidateRenderHost();
- }
-
- void FillRenderObject::SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> new_brush)
- {
- if (brush_ == new_brush)
- return;
-
- brush_ = std::move(new_brush);
- InvalidateRenderHost();
- }
-
- namespace details
- {
- template class ShapeRenderObject<Rect>;
- template class ShapeRenderObject<RoundedRect>;
- template class ShapeRenderObject<Ellipse>;
- }
-
- namespace details
- {
- template ShapeStrokeRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::DrawRectangle>;
- template ShapeStrokeRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::DrawRoundedRectangle>;
- template ShapeStrokeRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::DrawEllipse>;
- }
-
- namespace details
- {
- template ShapeFillRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::FillRectangle>;
- template ShapeFillRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::FillRoundedRectangle>;
- template ShapeFillRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::FillEllipse>;
- }
-
- void CustomDrawHandlerRenderObject::SetDrawHandler(DrawHandler new_draw_handler)
- {
- if (draw_handler_ == nullptr && new_draw_handler == nullptr)
- return;
-
- draw_handler_ = std::move(new_draw_handler);
- InvalidateRenderHost();
- }
-
- void CustomDrawHandlerRenderObject::Draw(ID2D1RenderTarget* render_target)
- {
- if (draw_handler_ != nullptr)
- draw_handler_(render_target);
- }
+void RenderObject::SetRenderHost(IRenderHost* new_render_host)
+{
+ if (new_render_host == render_host_) return;
+
+ const auto old = render_host_;
+ render_host_ = new_render_host;
+ OnRenderHostChanged(old, new_render_host);
+}
+
+void RenderObject::AddChild(RenderObject* render_object, const int position)
+{
+ if (render_object->GetParent() != nullptr)
+ throw std::invalid_argument("Render object already has a parent.");
+
+ if (position < 0)
+ throw std::invalid_argument("Position index is less than 0.");
+
+ if (static_cast<std::vector<RenderObject*>::size_type>(position) >
+ children_.size())
+ throw std::invalid_argument("Position index is out of bound.");
+
+ children_.insert(children_.cbegin() + position, render_object);
+ render_object->SetParent(this);
+ OnAddChild(render_object, position);
+}
+
+void RenderObject::RemoveChild(const int position)
+{
+ if (position < 0)
+ throw std::invalid_argument("Position index is less than 0.");
+
+ if (static_cast<std::vector<RenderObject*>::size_type>(position) >=
+ children_.size())
+ throw std::invalid_argument("Position index is out of bound.");
+
+ const auto i = children_.cbegin() + position;
+ const auto removed_child = *i;
+ children_.erase(i);
+ removed_child->SetParent(nullptr);
+ OnRemoveChild(removed_child, position);
+}
+
+
+void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host,
+ IRenderHost* new_render_host)
+{
+}
+
+void RenderObject::InvalidateRenderHostPaint() const
+{
+ if (render_host_ != nullptr) render_host_->InvalidatePaint();
+}
+
+void RenderObject::InvalidateRenderHostLayout() const
+{
+ if (render_host_ != nullptr) render_host_->InvalidateLayout();
+}
+
+void RenderObject::OnParentChanged(RenderObject* old_parent,
+ RenderObject* new_parent)
+{
+}
+
+
+void RenderObject::OnAddChild(RenderObject* new_child, int position)
+{
+}
+
+void RenderObject::OnRemoveChild(RenderObject* removed_child, int position)
+{
+}
+
+void RenderObject::SetParent(RenderObject* new_parent)
+{
+ const auto old_parent = parent_;
+ parent_ = new_parent;
+ OnParentChanged(old_parent, new_parent);
+}
+
+
+void LinearLayoutRenderObject::Measure(const MeasureConstraint& constraint)
+{
+
}
+} // namespace cru::ui::render
diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp
index 31745be5..00f761d1 100644
--- a/src/ui/render/render_object.hpp
+++ b/src/ui/render/render_object.hpp
@@ -2,256 +2,108 @@
#include "pre.hpp"
+#include <optional>
+#include <vector>
#include "system_headers.hpp"
-#include <functional>
-#include <cassert>
#include "base.hpp"
#include "ui/ui_base.hpp"
-#include "ui/d2d_util.hpp"
namespace cru::ui::render
{
- /* About Render Object
- *
- * Render object is a concrete subclass of RenderObject class.
- * It represents a painting action on a d2d render target. By
- * overriding "Draw" virtual method, it can customize its painting
- * action.
- */
-
-
- struct IRenderHost : Interface
- {
- virtual void InvalidateRender() = 0;
- };
-
-
- class RenderObject : public Object
- {
- protected:
- RenderObject() = default;
- public:
- RenderObject(const RenderObject& other) = delete;
- RenderObject(RenderObject&& other) = delete;
- RenderObject& operator=(const RenderObject& other) = delete;
- RenderObject& operator=(RenderObject&& other) = delete;
- ~RenderObject() override = default;
-
- virtual void Draw(ID2D1RenderTarget* render_target) = 0;
-
- IRenderHost* GetRenderHost() const
- {
- return render_host_;
- }
-
- void SetRenderHost(IRenderHost* new_render_host);
-
- protected:
- virtual void OnRenderHostChanged(IRenderHost* old_render_host, IRenderHost* new_render_host);
-
- void InvalidateRenderHost();
-
- private:
- IRenderHost* render_host_ = nullptr;
- };
-
-
- class StrokeRenderObject : public virtual RenderObject
- {
- protected:
- StrokeRenderObject() = default;
- public:
- StrokeRenderObject(const StrokeRenderObject& other) = delete;
- StrokeRenderObject(StrokeRenderObject&& other) = delete;
- StrokeRenderObject& operator=(const StrokeRenderObject& other) = delete;
- StrokeRenderObject& operator=(StrokeRenderObject&& other) = delete;
- ~StrokeRenderObject() override = default;
-
- float GetStrokeWidth() const
- {
- return stroke_width_;
- }
-
- void SetStrokeWidth(float new_stroke_width);
-
- Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const
- {
- return brush_;
- }
-
- void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> new_brush);
-
- Microsoft::WRL::ComPtr<ID2D1StrokeStyle> GetStrokeStyle() const
- {
- return stroke_style_;
- }
-
- void SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> new_stroke_style);
-
- private:
- float stroke_width_ = 1.0f;
- Microsoft::WRL::ComPtr<ID2D1Brush> brush_ = nullptr;
- Microsoft::WRL::ComPtr<ID2D1StrokeStyle> stroke_style_ = nullptr;
- };
-
-
- class FillRenderObject : public virtual RenderObject
- {
- protected:
- FillRenderObject() = default;
- public:
- FillRenderObject(const FillRenderObject& other) = delete;
- FillRenderObject(FillRenderObject&& other) = delete;
- FillRenderObject& operator=(const FillRenderObject& other) = delete;
- FillRenderObject& operator=(FillRenderObject&& other) = delete;
- ~FillRenderObject() override = default;
-
- Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const
- {
- return brush_;
- }
-
- void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> new_brush);
-
- private:
- Microsoft::WRL::ComPtr<ID2D1Brush> brush_ = nullptr;
- };
-
-
- namespace details
- {
- template <typename TShapeType>
- class ShapeRenderObject : public virtual RenderObject
- {
- public:
- using ShapeType = TShapeType;
- protected:
- ShapeRenderObject() = default;
- public:
- ShapeRenderObject(const ShapeRenderObject& other) = delete;
- ShapeRenderObject& operator=(const ShapeRenderObject& other) = delete;
- ShapeRenderObject(ShapeRenderObject&& other) = delete;
- ShapeRenderObject& operator=(ShapeRenderObject&& other) = delete;
- ~ShapeRenderObject() override = default;
-
- ShapeType GetShape() const
- {
- return shape_;
- }
-
- void SetShape(const ShapeType& new_shape)
- {
- if (new_shape == shape_)
- return;
-
- shape_ = new_shape;
- InvalidateRenderHost();
- }
-
- private:
- ShapeType shape_;
- };
+struct MeasureConstraint
+{
+ std::optional<float> min_width;
+ std::optional<float> min_height;
+ std::optional<float> max_width;
+ std::optional<float> max_height;
+};
- extern template class ShapeRenderObject<Rect>;
- extern template class ShapeRenderObject<RoundedRect>;
- extern template class ShapeRenderObject<Ellipse>;
- }
+struct LayoutConstraint
+{
+ float preferred_width;
+ float preferred_height;
+};
- using RectangleRenderObject = details::ShapeRenderObject<Rect>;
- using RoundedRectangleRenderObject = details::ShapeRenderObject<RoundedRect>;
- using EllipseRenderObject = details::ShapeRenderObject<Ellipse>;
+struct IRenderHost : Interface
+{
+ virtual void InvalidatePaint() = 0;
+ virtual void InvalidateLayout() = 0;
+};
- namespace details
- {
- template<typename TShapeType, typename TD2D1ShapeType, void (ID2D1RenderTarget::*draw_function)(const TD2D1ShapeType&, ID2D1Brush*, float, ID2D1StrokeStyle*)>
- class ShapeStrokeRenderObject : public ShapeRenderObject<TShapeType>, public StrokeRenderObject
- {
- public:
- ShapeStrokeRenderObject() = default;
- ShapeStrokeRenderObject(const ShapeStrokeRenderObject& other) = delete;
- ShapeStrokeRenderObject& operator=(const ShapeStrokeRenderObject& other) = delete;
- ShapeStrokeRenderObject(ShapeStrokeRenderObject&& other) = delete;
- ShapeStrokeRenderObject& operator=(ShapeStrokeRenderObject&& other) = delete;
- ~ShapeStrokeRenderObject() = default;
+// features:
+// 1. tree
+// 2. layout
+// 3. paint
+// 3. hit test
+class RenderObject : public Object
+{
+protected:
+ RenderObject() = default;
- protected:
- void Draw(ID2D1RenderTarget* render_target) override
- {
- const auto brush = GetBrush();
- if (brush != nullptr)
- (render_target->*draw_function)(Convert(GetShape()), brush.Get(), GetStrokeWidth(), GetStrokeStyle().Get());
- }
- };
+public:
+ RenderObject(const RenderObject& other) = delete;
+ RenderObject(RenderObject&& other) = delete;
+ RenderObject& operator=(const RenderObject& other) = delete;
+ RenderObject& operator=(RenderObject&& other) = delete;
+ ~RenderObject() override = default;
- extern template ShapeStrokeRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::DrawRectangle>;
- extern template ShapeStrokeRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::DrawRoundedRectangle>;
- extern template ShapeStrokeRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::DrawEllipse>;
- }
+ IRenderHost* GetRenderHost() const { return render_host_; }
+ void SetRenderHost(IRenderHost* new_render_host);
- using RectangleStrokeRenderObject = details::ShapeStrokeRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::DrawRectangle>;
- using RoundedRectangleStrokeRenderObject = details::ShapeStrokeRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::DrawRoundedRectangle>;
- using EllipseStrokeRenderObject = details::ShapeStrokeRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::DrawEllipse>;
+ RenderObject* GetParent() const { return parent_; }
+ const std::vector<RenderObject*>& GetChildren() const { return children_; }
+ void AddChild(RenderObject* render_object, int position);
+ void RemoveChild(int position);
- namespace details
- {
- template<typename TShapeType, typename TD2D1ShapeType, void (ID2D1RenderTarget::*fill_function)(const TD2D1ShapeType&, ID2D1Brush*)>
- class ShapeFillRenderObject : public ShapeRenderObject<TShapeType>, public StrokeRenderObject
- {
- public:
- ShapeFillRenderObject() = default;
- ShapeFillRenderObject(const ShapeFillRenderObject& other) = delete;
- ShapeFillRenderObject& operator=(const ShapeFillRenderObject& other) = delete;
- ShapeFillRenderObject(ShapeFillRenderObject&& other) = delete;
- ShapeFillRenderObject& operator=(ShapeFillRenderObject&& other) = delete;
- ~ShapeFillRenderObject() = default;
+ virtual void Measure(const MeasureConstraint& constraint) = 0;
+ virtual void Layout(const LayoutConstraint& constraint) = 0;
- protected:
- void Draw(ID2D1RenderTarget* render_target) override
- {
- const auto brush = GetBrush();
- if (brush != nullptr)
- (render_target->*fill_function)(Convert(GetShape()), brush.Get());
- }
- };
+ virtual void Draw(ID2D1RenderTarget* render_target) = 0;
- extern template ShapeFillRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::FillRectangle>;
- extern template ShapeFillRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::FillRoundedRectangle>;
- extern template ShapeFillRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::FillEllipse>;
- }
+ virtual void HitTest(const Point& point) = 0;
- using RectangleFillRenderObject = details::ShapeFillRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::FillRectangle>;
- using RoundedRectangleFillRenderObject = details::ShapeFillRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::FillRoundedRectangle>;
- using EllipseFillRenderObject = details::ShapeFillRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::FillEllipse>;
+protected:
+ virtual void OnRenderHostChanged(IRenderHost* old_render_host,
+ IRenderHost* new_render_host);
+ void InvalidateRenderHostPaint() const;
+ void InvalidateRenderHostLayout() const;
- class CustomDrawHandlerRenderObject : public RenderObject
- {
- public:
- using DrawHandler = std::function<void(ID2D1RenderTarget*)>;
+ virtual void OnParentChanged(RenderObject* old_parent,
+ RenderObject* new_parent);
- CustomDrawHandlerRenderObject() = default;
- CustomDrawHandlerRenderObject(const CustomDrawHandlerRenderObject& other) = delete;
- CustomDrawHandlerRenderObject& operator=(const CustomDrawHandlerRenderObject& other) = delete;
- CustomDrawHandlerRenderObject(CustomDrawHandlerRenderObject&& other) = delete;
- CustomDrawHandlerRenderObject& operator=(CustomDrawHandlerRenderObject&& other) = delete;
- ~CustomDrawHandlerRenderObject() override = default;
+ virtual void OnAddChild(RenderObject* new_child, int position);
+ virtual void OnRemoveChild(RenderObject* removed_child, int position);
- DrawHandler GetDrawHandler() const
- {
- return draw_handler_;
- }
+private:
+ void SetParent(RenderObject* new_parent);
- void SetDrawHandler(DrawHandler new_draw_handler);
+private:
+ IRenderHost* render_host_ = nullptr;
+ RenderObject* parent_ = nullptr;
+ std::vector<RenderObject*> children_;
+};
- protected:
- void Draw(ID2D1RenderTarget* render_target) override;
- private:
- DrawHandler draw_handler_{};
- };
-}
+class LinearLayoutRenderObject : public RenderObject
+{
+public:
+ LinearLayoutRenderObject() = default;
+ LinearLayoutRenderObject(const LinearLayoutRenderObject& other) = delete;
+ LinearLayoutRenderObject& operator=(const LinearLayoutRenderObject& other) =
+ delete;
+ LinearLayoutRenderObject(LinearLayoutRenderObject&& other) = delete;
+ LinearLayoutRenderObject& operator=(LinearLayoutRenderObject&& other) =
+ delete;
+ ~LinearLayoutRenderObject() = default;
+
+ void Measure(const MeasureConstraint& constraint) override;
+
+private:
+};
+} // namespace cru::ui::render
diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp
index c26bfe0e..17cb9acb 100644
--- a/src/ui/ui_base.hpp
+++ b/src/ui/ui_base.hpp
@@ -8,7 +8,7 @@
namespace cru::ui
{
- struct Point
+ struct Point final
{
constexpr static Point Zero()
{
@@ -33,7 +33,7 @@ namespace cru::ui
}
- struct Size
+ struct Size final
{
constexpr static Size Zero()
{
@@ -68,7 +68,7 @@ namespace cru::ui
}
- struct Thickness
+ struct Thickness final
{
constexpr static Thickness Zero()
{
@@ -122,7 +122,7 @@ namespace cru::ui
float bottom;
};
- constexpr bool operator == (const Thickness& left, const Thickness& right)
+ constexpr bool operator==(const Thickness& left, const Thickness& right)
{
return left.left == right.left &&
left.top == right.top &&
@@ -130,13 +130,13 @@ namespace cru::ui
left.bottom == right.bottom;
}
- constexpr bool operator != (const Thickness& left, const Thickness& right)
+ constexpr bool operator!=(const Thickness& left, const Thickness& right)
{
return !(left == right);
}
- struct Rect
+ struct Rect final
{
constexpr Rect() = default;
constexpr Rect(const float left, const float top, const float width, const float height)
@@ -228,7 +228,7 @@ namespace cru::ui
}
- struct RoundedRect
+ struct RoundedRect final
{
constexpr RoundedRect() = default;
constexpr RoundedRect(const Rect& rect, const float radius_x, const float radius_y)
@@ -239,17 +239,18 @@ namespace cru::ui
float radius_y = 0.0f;
};
- constexpr bool operator == (const RoundedRect& left, const RoundedRect& right)
+ constexpr bool operator==(const RoundedRect& left, const RoundedRect& right)
{
return left.rect == right.rect && left.radius_x == right.radius_x && left.radius_y == right.radius_y;
}
- constexpr bool operator != (const RoundedRect& left, const RoundedRect& right)
+ constexpr bool operator!=(const RoundedRect& left, const RoundedRect& right)
{
return !(left == right);
}
- struct Ellipse
+
+ struct Ellipse final
{
constexpr Ellipse() = default;
constexpr Ellipse(const Point& center, const float radius_x, const float radius_y)
@@ -270,26 +271,18 @@ namespace cru::ui
float radius_y = 0.0f;
};
- constexpr bool operator == (const Ellipse& left, const Ellipse& right)
+ constexpr bool operator==(const Ellipse& left, const Ellipse& right)
{
return left.center == right.center && left.radius_x == right.radius_x && left.radius_y == right.radius_y;
}
- constexpr bool operator != (const Ellipse& left, const Ellipse& right)
+ constexpr bool operator!=(const Ellipse& left, const Ellipse& right)
{
return !(left == right);
}
- enum class MouseButton
- {
- Left,
- Right,
- Middle
- };
-
-
- struct TextRange
+ struct TextRange final
{
constexpr static std::optional<TextRange> FromTwoSides(unsigned first, unsigned second)
{
@@ -317,8 +310,4 @@ namespace cru::ui
unsigned position = 0;
unsigned count = 0;
};
-
- bool IsKeyDown(int virtual_code);
- bool IsKeyToggled(int virtual_code);
- bool IsAnyMouseButtonDown();
}
diff --git a/src/ui/window.cpp b/src/ui/window.cpp
index 86fa4436..51b3f628 100644
--- a/src/ui/window.cpp
+++ b/src/ui/window.cpp
@@ -7,29 +7,6 @@
namespace cru::ui
{
- WindowClass::WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance)
- : name_(name)
- {
- WNDCLASSEX window_class;
- window_class.cbSize = sizeof(WNDCLASSEX);
-
- window_class.style = CS_HREDRAW | CS_VREDRAW;
- window_class.lpfnWndProc = window_proc;
- window_class.cbClsExtra = 0;
- window_class.cbWndExtra = 0;
- window_class.hInstance = h_instance;
- window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
- window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
- window_class.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
- window_class.lpszMenuName = NULL;
- window_class.lpszClassName = name.c_str();
- window_class.hIconSm = NULL;
-
- atom_ = RegisterClassEx(&window_class);
- if (atom_ == 0)
- throw std::runtime_error("Failed to create window class.");
- }
-
LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
auto window = WindowManager::GetInstance()->FromHandle(hWnd);
diff --git a/src/ui/window.hpp b/src/ui/window.hpp
index e96d4d92..d3374684 100644
--- a/src/ui/window.hpp
+++ b/src/ui/window.hpp
@@ -17,32 +17,6 @@ namespace cru::graph
namespace cru::ui
{
- class WindowClass : public Object
- {
- public:
- WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance);
- WindowClass(const WindowClass& other) = delete;
- WindowClass(WindowClass&& other) = delete;
- WindowClass& operator=(const WindowClass& other) = delete;
- WindowClass& operator=(WindowClass&& other) = delete;
- ~WindowClass() override = default;
-
-
- const wchar_t* GetName() const
- {
- return name_.c_str();
- }
-
- ATOM GetAtom() const
- {
- return atom_;
- }
-
- private:
- String name_;
- ATOM atom_;
- };
-
class WindowManager : public Object
{
public:
@@ -85,7 +59,7 @@ namespace cru::ui
- class Window final : public SingleChildControl
+ class Window final : public ContentControl
{
friend class WindowManager;
public:
diff --git a/src/ui/window_class.cpp b/src/ui/window_class.cpp
new file mode 100644
index 00000000..456d9492
--- /dev/null
+++ b/src/ui/window_class.cpp
@@ -0,0 +1,25 @@
+#include "window_class.hpp"
+
+namespace cru::ui {
+WindowClass::WindowClass(const String& name, WNDPROC window_proc,
+ HINSTANCE h_instance)
+ : name_(name) {
+ WNDCLASSEX window_class;
+ window_class.cbSize = sizeof(WNDCLASSEX);
+
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.lpfnWndProc = window_proc;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = h_instance;
+ window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ window_class.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
+ window_class.lpszMenuName = NULL;
+ window_class.lpszClassName = name.c_str();
+ window_class.hIconSm = NULL;
+
+ atom_ = ::RegisterClassExW(&window_class);
+ if (atom_ == 0) throw std::runtime_error("Failed to create window class.");
+}
+} // namespace cru::ui
diff --git a/src/ui/window_class.hpp b/src/ui/window_class.hpp
new file mode 100644
index 00000000..66babd94
--- /dev/null
+++ b/src/ui/window_class.hpp
@@ -0,0 +1,26 @@
+#pragma once
+#include "pre.hpp"
+
+#include "system_headers.hpp"
+
+#include "base.hpp"
+
+namespace cru::ui {
+class WindowClass : public Object {
+ public:
+ WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance);
+ WindowClass(const WindowClass& other) = delete;
+ WindowClass(WindowClass&& other) = delete;
+ WindowClass& operator=(const WindowClass& other) = delete;
+ WindowClass& operator=(WindowClass&& other) = delete;
+ ~WindowClass() override = default;
+
+ const wchar_t* GetName() const { return name_.c_str(); }
+
+ ATOM GetAtom() const { return atom_; }
+
+ private:
+ String name_;
+ ATOM atom_;
+};
+} // namespace cru::ui
diff --git a/src/util/any_map.cpp b/src/util/any_map.cpp
new file mode 100644
index 00000000..c49464d3
--- /dev/null
+++ b/src/util/any_map.cpp
@@ -0,0 +1,30 @@
+#include "any_map.hpp"
+
+namespace cru::util {
+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.
+ }
+}
+} // namespace cru::util
diff --git a/src/util/any_map.hpp b/src/util/any_map.hpp
new file mode 100644
index 00000000..d82167d2
--- /dev/null
+++ b/src/util/any_map.hpp
@@ -0,0 +1,94 @@
+#pragma once
+#include "pre.hpp"
+
+#include <any>
+#include <functional>
+#include <optional>
+#include <typeinfo>
+#include <unordered_map>
+
+#include "base.hpp"
+#include "format.hpp"
+
+namespace cru::util {
+// 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, T&& value) {
+ auto& p = map_[key];
+ p.first = std::make_any<T>(std::forward<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;
+};
+} // namespace cru::util
diff --git a/src/util/format.hpp b/src/util/format.hpp
new file mode 100644
index 00000000..874c5b43
--- /dev/null
+++ b/src/util/format.hpp
@@ -0,0 +1,94 @@
+#pragma once
+#include "pre.hpp"
+
+#include "base.hpp"
+
+namespace cru::util {
+namespace details {
+constexpr StringView PlaceHolder(type_tag<String>) { return StringView(L"{}"); }
+
+constexpr MultiByteStringView PlaceHolder(type_tag<MultiByteString>) {
+ return MultiByteStringView("{}");
+}
+
+template <typename TString>
+void FormatInternal(TString& string) {
+ const auto find_result = string.find(PlaceHolder(type_tag<TString>{}));
+ if (find_result != TString::npos)
+ throw std::invalid_argument("There is more placeholders than args.");
+}
+
+template <typename TString, typename T, typename... TRest>
+void FormatInternal(TString& string, const T& arg, const TRest&... args) {
+ const auto find_result = string.find(PlaceHolder(type_tag<TString>{}));
+ if (find_result == TString::npos)
+ throw std::invalid_argument("There is less placeholders than args.");
+
+ string.replace(find_result, 2, FormatToString(arg, type_tag<TString>{}));
+ FormatInternal<TString>(string, args...);
+}
+} // namespace details
+
+template <typename... T>
+String Format(const StringView& format, const T&... args) {
+ String result(format);
+ details::FormatInternal<String>(result, args...);
+ return result;
+}
+
+template <typename... T>
+MultiByteString Format(const MultiByteStringView& format, const T&... args) {
+ MultiByteString result(format);
+ details::FormatInternal<MultiByteString>(result, args...);
+ return result;
+}
+
+#define CRU_FORMAT_NUMBER(type) \
+ inline String FormatToString(const type number, type_tag<String>) { \
+ return std::to_wstring(number); \
+ } \
+ inline MultiByteString FormatToString(const type number, \
+ type_tag<MultiByteString>) { \
+ return std::to_string(number); \
+ }
+
+CRU_FORMAT_NUMBER(int)
+CRU_FORMAT_NUMBER(short)
+CRU_FORMAT_NUMBER(long)
+CRU_FORMAT_NUMBER(long long)
+CRU_FORMAT_NUMBER(unsigned int)
+CRU_FORMAT_NUMBER(unsigned short)
+CRU_FORMAT_NUMBER(unsigned long)
+CRU_FORMAT_NUMBER(unsigned long long)
+CRU_FORMAT_NUMBER(float)
+CRU_FORMAT_NUMBER(double)
+
+#undef CRU_FORMAT_NUMBER
+
+inline StringView FormatToString(const String& string, type_tag<String>) {
+ return string;
+}
+
+inline MultiByteString FormatToString(const MultiByteString& string,
+ type_tag<MultiByteString>) {
+ return string;
+}
+
+inline StringView FormatToString(const StringView& string, type_tag<String>) {
+ return string;
+}
+
+inline MultiByteStringView FormatToString(const MultiByteStringView& string,
+ type_tag<MultiByteString>) {
+ return string;
+}
+
+inline StringView FormatToString(const wchar_t* string, type_tag<String>) {
+ return StringView(string);
+}
+
+inline MultiByteStringView FormatToString(const char* string,
+ type_tag<MultiByteString>) {
+ return MultiByteString(string);
+}
+} // namespace cru::util
diff --git a/src/util/math_util.hpp b/src/util/math_util.hpp
new file mode 100644
index 00000000..01348641
--- /dev/null
+++ b/src/util/math_util.hpp
@@ -0,0 +1,51 @@
+#pragma once
+#include "pre.hpp"
+
+#include <optional>
+#include <type_traits>
+
+namespace cru::util {
+template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+float Coerce(const T n, const std::optional<T> min,
+ const std::optional<T> max) {
+ if (min.has_value() && n < min.value()) return min.value();
+ if (max.has_value() && n > max.value()) return max.value();
+ return n;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+float Coerce(const T n, const T min, const T max) {
+ if (n < min) return min;
+ if (n > max) return max;
+ return n;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+float Coerce(const T n, const std::nullopt_t, const std::optional<T> max) {
+ if (max.has_value() && n > max.value()) return max.value();
+ return n;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+float Coerce(const T n, const std::optional<T> min, const std::nullopt_t) {
+ if (min.has_value() && n < min.value()) return min.value();
+ return n;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+float Coerce(const T n, const std::nullopt_t, const T max) {
+ if (n > max) return max;
+ return n;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+float Coerce(const T n, const T min, const std::nullopt_t) {
+ if (n < min) return min;
+ return n;
+}
+
+template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+T AtLeast0(const T value) {
+ return value < static_cast<T>(0) ? static_cast<T>(0) : value;
+}
+} // namespace cru::util
diff --git a/src/util/string_util.cpp b/src/util/string_util.cpp
new file mode 100644
index 00000000..3c765259
--- /dev/null
+++ b/src/util/string_util.cpp
@@ -0,0 +1,21 @@
+#include "string_util.hpp"
+
+#include "system_headers.hpp"
+#include "exception.hpp"
+
+namespace cru::util {
+MultiByteString ToUtf8String(const StringView& string) {
+ if (string.empty()) return MultiByteString();
+
+ const auto length = ::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1,
+ nullptr, 0, nullptr, nullptr);
+ MultiByteString result;
+ result.reserve(length);
+ if (::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, result.data(),
+ static_cast<int>(result.capacity()), nullptr,
+ nullptr) == 0)
+ throw Win32Error(::GetLastError(),
+ "Failed to convert wide string to UTF-8.");
+ return result;
+}
+}
diff --git a/src/util/string_util.hpp b/src/util/string_util.hpp
new file mode 100644
index 00000000..6d060089
--- /dev/null
+++ b/src/util/string_util.hpp
@@ -0,0 +1,8 @@
+#pragma once
+#include "pre.hpp"
+
+#include "base.hpp"
+
+namespace cru::util {
+MultiByteString ToUtf8String(const StringView& string);
+}