From 769a4db24b64a2baf20960858ae24461328e5b7a Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 10 Dec 2018 23:29:54 +0800 Subject: ... --- src/ui/control.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/ui/control.cpp') diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 3987e818..9388c719 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -8,7 +8,7 @@ #include "graph/graph.hpp" #include "exception.hpp" #include "cru_debug.hpp" -#include "convert_util.hpp" +#include "d2d_util.hpp" #include "math_util.hpp" #ifdef CRU_DEBUG_LAYOUT -- cgit v1.2.3 From 5dc738a57930271194bd86673eb86f149096a7b2 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 19 Mar 2019 16:21:54 +0800 Subject: ... --- .clang-format | 1 + CruUI.vcxproj | 18 +- CruUI.vcxproj.filters | 54 +- snippets/vc++snippets.snippet | 124 ++++- src/any_map.cpp | 33 -- src/any_map.hpp | 103 ---- src/application.cpp | 192 ++++---- src/application.hpp | 177 ++++--- src/base.cpp | 20 - src/base.hpp | 91 ++-- src/cru_debug.cpp | 10 +- src/cru_debug.hpp | 67 ++- src/cru_event.hpp | 134 +++-- src/exception.cpp | 76 +-- src/exception.hpp | 94 ++-- src/format.hpp | 110 ----- src/math_util.hpp | 69 --- src/pre.hpp | 5 +- src/system_headers.hpp | 2 - src/timer.cpp | 105 ++-- src/timer.hpp | 90 ++-- src/ui/control.cpp | 681 ++------------------------ src/ui/control.hpp | 301 ++---------- src/ui/controls/button.hpp | 2 +- src/ui/controls/list_item.hpp | 2 +- src/ui/controls/scroll_control.hpp | 2 +- src/ui/render/linear_layout_render_object.cpp | 0 src/ui/render/linear_layout_render_object.hpp | 1 + src/ui/render/render_object.cpp | 176 ++++--- src/ui/render/render_object.hpp | 302 +++--------- src/ui/ui_base.hpp | 39 +- src/ui/window.cpp | 23 - src/ui/window.hpp | 28 +- src/ui/window_class.cpp | 25 + src/ui/window_class.hpp | 26 + src/util/any_map.cpp | 30 ++ src/util/any_map.hpp | 94 ++++ src/util/format.hpp | 94 ++++ src/util/math_util.hpp | 51 ++ src/util/string_util.cpp | 21 + src/util/string_util.hpp | 8 + 41 files changed, 1260 insertions(+), 2221 deletions(-) create mode 100644 .clang-format delete mode 100644 src/any_map.cpp delete mode 100644 src/any_map.hpp delete mode 100644 src/base.cpp delete mode 100644 src/format.hpp delete mode 100644 src/math_util.hpp create mode 100644 src/ui/render/linear_layout_render_object.cpp create mode 100644 src/ui/render/linear_layout_render_object.hpp create mode 100644 src/ui/window_class.cpp create mode 100644 src/ui/window_class.hpp create mode 100644 src/util/any_map.cpp create mode 100644 src/util/any_map.hpp create mode 100644 src/util/format.hpp create mode 100644 src/util/math_util.hpp create mode 100644 src/util/string_util.cpp create mode 100644 src/util/string_util.hpp (limited to 'src/ui/control.cpp') 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 @@ - + + - @@ -136,9 +136,11 @@ - - - + + + + + @@ -150,10 +152,12 @@ + + @@ -178,6 +182,7 @@ + @@ -186,6 +191,9 @@ + + + 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 @@ Source Files - - Source Files - Source Files @@ -75,9 +72,6 @@ Source Files - - Source Files - Source Files @@ -96,6 +90,21 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -158,9 +167,6 @@ Header Files - - Header Files - Header Files @@ -170,9 +176,6 @@ Header Files - - Header Files - Header Files @@ -185,9 +188,6 @@ Header Files - - Header Files - Header Files @@ -197,6 +197,27 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -257,4 +278,7 @@ + + + \ 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 @@ Deleted Copy Constructor/Assignment crupest Declare a deleted copy constructor and a deleted copy assignment operator for a class. - dcopy + deletecopy Expansion @@ -26,12 +26,38 @@ + +
+ Default Copy Constructor/Assignment + crupest + Declare a default copy constructor and a default copy assignment operator for a class. + defaultcopy + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
Deleted Move Constructor/Assignment crupest Declare a deleted move constructor and a deleted move assignment operator for a class. - dmove + deletemove Expansion @@ -52,4 +78,98 @@ + +
+ Default Move Constructor/Assignment + crupest + Declare a default move constructor and a default move assignment operator for a class. + defaultmove + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
+ +
+ Default Copyable/Movable Class + crupest + Define a class with default constructor, destructor, move/copy constructor/assignment-operator. + defaultclass + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
+ +
+ Default Noncopyable/Nonmovable Class + crupest + Define a class with default constructor, destructor and deleted move/copy constructor/assignment-operator. + objectclass + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
\ 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& 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 -#include -#include -#include -#include - -#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; - - 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 - std::optional 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(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 - T GetValue(const String& key) const - { - const auto optional_value = GetOptionalValue(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 - void SetValue(const String& key, T&& value) - { - auto& p = map_[key]; - p.first = std::make_any(std::forward(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& listener_list, const std::any& value); - - private: - std::unordered_map>> map_{}; - std::unordered_map 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(god_window_class_name, GodWndProc, h_instance); + god_window_class_ = std::make_unique(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 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*>(w_param); + (*p_action)(); + delete p_action; + return 0; } - - std::optional 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*>(w_param); - (*p_action)(); - delete p_action; - return 0; - } - case WM_TIMER: - { - const auto id = static_cast(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(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(this); + god_window_ = std::make_unique(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(msg.wParam); - } + return static_cast(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& action) { - //copy the action to a safe place - auto p_action_copy = new std::function(action); +void InvokeLater(const std::function& action) { + // copy the action to a safe place + auto p_action_copy = new std::function(action); - if (PostMessageW(Application::GetInstance()->GetGodWindow()->GetHandle(), invoke_later_message_id, reinterpret_cast(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(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 #include #include -#include -#include #include +#include +#include "system_headers.hpp" #include "base.hpp" @@ -16,103 +14,92 @@ #include #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 HandleGodWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param); - - private: - std::unique_ptr 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>> - T* ResolveSingleton(const std::function& 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(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 HandleGodWindowMessage(HWND hwnd, int msg, + WPARAM w_param, LPARAM l_param); + + private: + std::unique_ptr 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 >> + T* ResolveSingleton(const std::function& 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(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(singleton)); - singleton_list_.push_back(singleton); - return singleton; - } + auto singleton = creator(this); + singleton_map_.emplace(index, static_cast(singleton)); + singleton_list_.push_back(singleton); + return singleton; + } - private: - HINSTANCE h_instance_; + private: + HINSTANCE h_instance_; - std::unique_ptr god_window_; + std::unique_ptr god_window_; - std::unordered_map singleton_map_; - std::list singleton_list_; // used for reverse destroy. + std::unordered_map singleton_map_; + std::list singleton_list_; // used for reverse destroy. #ifdef CRU_DEBUG - std::unordered_set singleton_type_set_; // used for detecting dead recursion. + std::unordered_set + singleton_type_set_; // used for detecting dead recursion. #endif - }; - +}; - void InvokeLater(const std::function& action); -} +void InvokeLater(const std::function& 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(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 +#include #include +#include #include -#include -namespace cru -{ - template 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; - - 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 +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; + +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 #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& 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(after - before); - DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count())); - } +inline void DebugTime(const std::function& 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(after - before); + DebugMessage(util::Format(L"{}: {}ms.\n", hint_message, duration.count())); +} - template - TReturn DebugTime(const std::function& 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(after - before); - DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count())); - return std::move(result); - } +template +TReturn DebugTime(const std::function& 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(after - before); + DebugMessage(util::Format(L"{}: {}ms.\n", hint_message, duration.count())); + return std::move(result); +} #else - inline void DebugTime(const std::function& action, const StringView& hint_message) - { - action(); - } +inline void DebugTime(const std::function& action, + const StringView& hint_message) { + action(); +} - template - TReturn DebugTime(const std::function& action, const StringView& hint_message) - { - return action(); - } -#endif +template +TReturn DebugTime(const std::function& 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 #include #include +#include #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 - class Event - { - public: - static_assert(std::is_base_of_v, - "TArgsType must be subclass of BasicEventArgs."); - - - using ArgsType = TArgsType; - using EventHandler = std::function; - 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 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 +class Event { + public: + static_assert(std::is_base_of_v, + "TArgsType must be subclass of BasicEventArgs."); + + using ArgsType = TArgsType; + using EventHandler = std::function; + 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 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 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 additional_message) - : runtime_error(HResultMakeMessage(h_result, std::nullopt)), h_result_(h_result) - { - - } - - inline std::string Win32MakeMessage(DWORD error_code, std::optional 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 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 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 additional_message) + : runtime_error(HResultMakeMessage(h_result, std::nullopt)), + h_result_(h_result) {} + +inline std::string Win32MakeMessage( + DWORD error_code, std::optional 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 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 +#include "system_headers.hpp" #include "base.hpp" - namespace cru { - class HResultError : public std::runtime_error - { - public: - explicit HResultError(HRESULT h_result, std::optional 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 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 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 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) - { - return StringView(L"{}"); - } - - constexpr MultiByteStringView PlaceHolder(type_tag) - { - return MultiByteStringView("{}"); - } - - template - void FormatInternal(TString& string) - { - const auto find_result = string.find(PlaceHolder(type_tag{})); - if (find_result != TString::npos) - throw std::invalid_argument("There is more placeholders than args."); - } - - template - void FormatInternal(TString& string, const T& arg, const TRest&... args) - { - const auto find_result = string.find(PlaceHolder(type_tag{})); - if (find_result == TString::npos) - throw std::invalid_argument("There is less placeholders than args."); - - string.replace(find_result, 2, FormatToString(arg, type_tag{})); - FormatInternal(string, args...); - } - } - - template - String Format(const StringView& format, const T&... args) - { - String result(format); - details::FormatInternal(result, args...); - return result; - } - - template - MultiByteString Format(const MultiByteStringView& format, const T&... args) - { - MultiByteString result(format); - details::FormatInternal(result, args...); - return result; - } - -#define CRU_FORMAT_NUMBER(type) \ - inline String FormatToString(const type number, type_tag) \ - { \ - return std::to_wstring(number); \ - } \ - inline MultiByteString FormatToString(const type number, type_tag) \ - { \ - 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) - { - return string; - } - - inline MultiByteString FormatToString(const MultiByteString& string, type_tag) - { - return string; - } - - inline StringView FormatToString(const StringView& string, type_tag) - { - return string; - } - - inline MultiByteStringView FormatToString(const MultiByteStringView& string, type_tag) - { - return string; - } - - inline StringView FormatToString(const wchar_t* string, type_tag) - { - return StringView(string); - } - - inline MultiByteStringView FormatToString(const char* string, type_tag) - { - 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 -#include - -namespace cru -{ - template >> - float Coerce(const T n, const std::optional min, const std::optional max) - { - if (min.has_value() && n < min.value()) - return min.value(); - if (max.has_value() && n > max.value()) - return max.value(); - return n; - } - - template >> - float Coerce(const T n, const T min, const T max) - { - if (n < min) - return min; - if (n > max) - return max; - return n; - } - - template >> - float Coerce(const T n, const std::nullopt_t, const std::optional max) - { - if (max.has_value() && n > max.value()) - return max.value(); - return n; - } - - template >> - float Coerce(const T n, const std::optional min, const std::nullopt_t) - { - if (min.has_value() && n < min.value()) - return min.value(); - return n; - } - - template >> - float Coerce(const T n, const std::nullopt_t, const T max) - { - if (n > max) - return max; - return n; - } - - template >> - float Coerce(const T n, const T min, const std::nullopt_t) - { - if (n < min) - return min; - return n; - } - - template >> - T AtLeast0(const T value) - { - return value < static_cast(0) ? static_cast(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 #include +#include #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([](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> 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(milliseconds.count()), false, action); - return TimerTask(id); - } - - TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action) - { - const auto id = TimerManager::GetInstance()->CreateTimer(static_cast(milliseconds.count()), true, action); - return TimerTask(id); - } +namespace cru { +TimerManager* TimerManager::GetInstance() { + return Application::GetInstance()->ResolveSingleton( + [](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> 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(milliseconds.count()), false, action); + return TimerTask(id); +} + +TimerTask SetInterval(std::chrono::milliseconds milliseconds, + const TimerAction& action) { + const auto id = TimerManager::GetInstance()->CreateTimer( + static_cast(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 #include #include +#include #include +#include "system_headers.hpp" #include "base.hpp" -namespace cru -{ - using TimerAction = std::function; +namespace cru { +using TimerAction = std::function; + +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> GetAction(UINT_PTR id); + UINT_PTR CreateTimer(UINT milliseconds, bool loop, const TimerAction& action); + void KillTimer(UINT_PTR id); + std::optional> GetAction(UINT_PTR id); - private: - std::map> map_{}; - UINT_PTR current_count_ = 0; - }; + private: + std::map> 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& predicate) - { - predicate(control); - for (auto c: control->GetInternalChildren()) - TraverseDescendantsInternal(c, predicate); - } - void Control::TraverseDescendants(const std::function& 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& predicate) { -#ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr CalculateSquareRingGeometry(const Rect& out, const Rect& in) - { - const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); - Microsoft::WRL::ComPtr out_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry)); - Microsoft::WRL::ComPtr in_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry)); - Microsoft::WRL::ComPtr result_geometry; - ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry)); - Microsoft::WRL::ComPtr 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 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 geometry2; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(in_border_rounded_rect, &geometry2) - ); - geometry_info_.padding_content_geometry = geometry2; - - - Microsoft::WRL::ComPtr geometry3; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(GetRect(RectRange::Content)), &geometry3) - ); - Microsoft::WRL::ComPtr geometry4; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreatePathGeometry(&geometry4) - ); - Microsoft::WRL::ComPtr 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 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 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 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 NoChildControl::empty_control_vector{}; - std::list GetAncestorList(Control* control) - { - std::list 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(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(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 GetAncestorList(Control* control) + { + std::list 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 border_geometry = nullptr; - Microsoft::WRL::ComPtr padding_content_geometry = nullptr; - Microsoft::WRL::ComPtr 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& 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& 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& 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 GetForegroundBrush() const - { - return foreground_brush_; - } - - void SetForegroundBrush(Microsoft::WRL::ComPtr foreground_brush) - { - foreground_brush_ = std::move(foreground_brush); - InvalidateDraw(); - } - - Microsoft::WRL::ComPtr GetBackgroundBrush() const - { - return background_brush_; - } - - void SetBackgroundBrush(Microsoft::WRL::ComPtr 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 mouse_enter_event; //Raised when mouse is leave the control. @@ -264,95 +124,29 @@ namespace cru::ui events::RoutedEvent get_focus_event; events::RoutedEvent lose_focus_event; - Event draw_content_event; - Event draw_background_event; - Event 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& 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 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 foreground_brush_ = nullptr; - Microsoft::WRL::ComPtr background_brush_ = nullptr; + Cursor::Ptr cursor_{}; AnyMap additional_property_map_{}; - - bool is_focus_on_pressed_ = true; - -#ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr margin_geometry_; - Microsoft::WRL::ComPtr 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& 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& 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& 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 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 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 new_brush) - { - if (brush_ == new_brush) - return; - - brush_ = std::move(new_brush); - InvalidateRenderHost(); - } - - void StrokeRenderObject::SetStrokeStyle(Microsoft::WRL::ComPtr new_stroke_style) - { - if (stroke_style_ == new_stroke_style) - return; - - stroke_style_ = std::move(new_stroke_style); - InvalidateRenderHost(); - } - - void FillRenderObject::SetBrush(Microsoft::WRL::ComPtr new_brush) - { - if (brush_ == new_brush) - return; - - brush_ = std::move(new_brush); - InvalidateRenderHost(); - } - - namespace details - { - template class ShapeRenderObject; - template class ShapeRenderObject; - template class ShapeRenderObject; - } - - namespace details - { - template ShapeStrokeRenderObject; - template ShapeStrokeRenderObject; - template ShapeStrokeRenderObject; - } - - namespace details - { - template ShapeFillRenderObject; - template ShapeFillRenderObject; - template ShapeFillRenderObject; - } - - 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::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::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 +#include #include "system_headers.hpp" -#include -#include #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 GetBrush() const - { - return brush_; - } - - void SetBrush(Microsoft::WRL::ComPtr new_brush); - - Microsoft::WRL::ComPtr GetStrokeStyle() const - { - return stroke_style_; - } - - void SetStrokeStyle(Microsoft::WRL::ComPtr new_stroke_style); - - private: - float stroke_width_ = 1.0f; - Microsoft::WRL::ComPtr brush_ = nullptr; - Microsoft::WRL::ComPtr 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 GetBrush() const - { - return brush_; - } - - void SetBrush(Microsoft::WRL::ComPtr new_brush); - - private: - Microsoft::WRL::ComPtr brush_ = nullptr; - }; - - - namespace details - { - template - 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 min_width; + std::optional min_height; + std::optional max_width; + std::optional max_height; +}; - extern template class ShapeRenderObject; - extern template class ShapeRenderObject; - extern template class ShapeRenderObject; - } +struct LayoutConstraint +{ + float preferred_width; + float preferred_height; +}; - using RectangleRenderObject = details::ShapeRenderObject; - using RoundedRectangleRenderObject = details::ShapeRenderObject; - using EllipseRenderObject = details::ShapeRenderObject; +struct IRenderHost : Interface +{ + virtual void InvalidatePaint() = 0; + virtual void InvalidateLayout() = 0; +}; - namespace details - { - template - class ShapeStrokeRenderObject : public ShapeRenderObject, 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; - extern template ShapeStrokeRenderObject; - extern template ShapeStrokeRenderObject; - } + IRenderHost* GetRenderHost() const { return render_host_; } + void SetRenderHost(IRenderHost* new_render_host); - using RectangleStrokeRenderObject = details::ShapeStrokeRenderObject; - using RoundedRectangleStrokeRenderObject = details::ShapeStrokeRenderObject; - using EllipseStrokeRenderObject = details::ShapeStrokeRenderObject; + RenderObject* GetParent() const { return parent_; } + const std::vector& GetChildren() const { return children_; } + void AddChild(RenderObject* render_object, int position); + void RemoveChild(int position); - namespace details - { - template - class ShapeFillRenderObject : public ShapeRenderObject, 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; - extern template ShapeFillRenderObject; - extern template ShapeFillRenderObject; - } + virtual void HitTest(const Point& point) = 0; - using RectangleFillRenderObject = details::ShapeFillRenderObject; - using RoundedRectangleFillRenderObject = details::ShapeFillRenderObject; - using EllipseFillRenderObject = details::ShapeFillRenderObject; +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; + 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 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 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& 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 +#include +#include +#include +#include + +#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; + + 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 + std::optional 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(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 + T GetValue(const String& key) const { + const auto optional_value = GetOptionalValue(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 + void SetValue(const String& key, T&& value) { + auto& p = map_[key]; + p.first = std::make_any(std::forward(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& listener_list, + const std::any& value); + + private: + std::unordered_map>> + map_{}; + std::unordered_map 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) { return StringView(L"{}"); } + +constexpr MultiByteStringView PlaceHolder(type_tag) { + return MultiByteStringView("{}"); +} + +template +void FormatInternal(TString& string) { + const auto find_result = string.find(PlaceHolder(type_tag{})); + if (find_result != TString::npos) + throw std::invalid_argument("There is more placeholders than args."); +} + +template +void FormatInternal(TString& string, const T& arg, const TRest&... args) { + const auto find_result = string.find(PlaceHolder(type_tag{})); + if (find_result == TString::npos) + throw std::invalid_argument("There is less placeholders than args."); + + string.replace(find_result, 2, FormatToString(arg, type_tag{})); + FormatInternal(string, args...); +} +} // namespace details + +template +String Format(const StringView& format, const T&... args) { + String result(format); + details::FormatInternal(result, args...); + return result; +} + +template +MultiByteString Format(const MultiByteStringView& format, const T&... args) { + MultiByteString result(format); + details::FormatInternal(result, args...); + return result; +} + +#define CRU_FORMAT_NUMBER(type) \ + inline String FormatToString(const type number, type_tag) { \ + return std::to_wstring(number); \ + } \ + inline MultiByteString FormatToString(const type number, \ + type_tag) { \ + 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) { + return string; +} + +inline MultiByteString FormatToString(const MultiByteString& string, + type_tag) { + return string; +} + +inline StringView FormatToString(const StringView& string, type_tag) { + return string; +} + +inline MultiByteStringView FormatToString(const MultiByteStringView& string, + type_tag) { + return string; +} + +inline StringView FormatToString(const wchar_t* string, type_tag) { + return StringView(string); +} + +inline MultiByteStringView FormatToString(const char* string, + type_tag) { + 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 +#include + +namespace cru::util { +template >> +float Coerce(const T n, const std::optional min, + const std::optional max) { + if (min.has_value() && n < min.value()) return min.value(); + if (max.has_value() && n > max.value()) return max.value(); + return n; +} + +template >> +float Coerce(const T n, const T min, const T max) { + if (n < min) return min; + if (n > max) return max; + return n; +} + +template >> +float Coerce(const T n, const std::nullopt_t, const std::optional max) { + if (max.has_value() && n > max.value()) return max.value(); + return n; +} + +template >> +float Coerce(const T n, const std::optional min, const std::nullopt_t) { + if (min.has_value() && n < min.value()) return min.value(); + return n; +} + +template >> +float Coerce(const T n, const std::nullopt_t, const T max) { + if (n > max) return max; + return n; +} + +template >> +float Coerce(const T n, const T min, const std::nullopt_t) { + if (n < min) return min; + return n; +} + +template >> +T AtLeast0(const T value) { + return value < static_cast(0) ? static_cast(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(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); +} -- cgit v1.2.3 From 62080f78c900aa41e456aa37a1825310121de881 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 20 Mar 2019 17:31:22 +0800 Subject: ... --- CruUI.vcxproj | 4 - CruUI.vcxproj.filters | 12 - src/ui/border_property.cpp | 23 -- src/ui/border_property.hpp | 87 ------ src/ui/control.cpp | 402 ++++++++++----------------- src/ui/control.hpp | 564 +++++++++++++++----------------------- src/ui/controls/linear_layout.cpp | 10 +- src/ui/d2d_util.hpp | 2 - src/ui/layout_base.cpp | 6 - src/ui/layout_base.hpp | 102 ------- src/ui/render/render_object.cpp | 19 -- src/ui/render/render_object.hpp | 20 +- src/ui/window.hpp | 377 ++++++++++++------------- 13 files changed, 560 insertions(+), 1068 deletions(-) delete mode 100644 src/ui/border_property.cpp delete mode 100644 src/ui/border_property.hpp delete mode 100644 src/ui/layout_base.cpp delete mode 100644 src/ui/layout_base.hpp (limited to 'src/ui/control.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index bd321715..f4fada4d 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -127,7 +127,6 @@ - @@ -141,7 +140,6 @@ - @@ -151,7 +149,6 @@ - @@ -180,7 +177,6 @@ - diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 25923c76..1b7038d2 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -33,9 +33,6 @@ Source Files - - Source Files - Source Files @@ -60,9 +57,6 @@ Source Files - - Source Files - Source Files @@ -131,18 +125,12 @@ Header Files - - Header Files - Header Files Header Files - - Header Files - Header Files diff --git a/src/ui/border_property.cpp b/src/ui/border_property.cpp deleted file mode 100644 index b79bb482..00000000 --- a/src/ui/border_property.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "border_property.hpp" - -#include "ui_manager.hpp" - -namespace cru::ui -{ - BorderProperty::BorderProperty(): BorderProperty(UiManager::GetInstance()->GetPredefineResources()->border_property_brush) - { - - } - - BorderProperty::BorderProperty(Microsoft::WRL::ComPtr brush): brush_(std::move(brush)) - { - - } - - BorderProperty::BorderProperty(Microsoft::WRL::ComPtr brush, const float width, const float radius_x, - const float radius_y, Microsoft::WRL::ComPtr stroke_style) : - brush_(std::move(brush)), stroke_width_(width), radius_x_(radius_x), radius_y_(radius_y), stroke_style_(std::move(stroke_style)) - { - - } -} diff --git a/src/ui/border_property.hpp b/src/ui/border_property.hpp deleted file mode 100644 index 4dee0e0f..00000000 --- a/src/ui/border_property.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "system_headers.hpp" - -#include "base.hpp" - - -namespace cru::ui -{ - class BorderProperty final - { - public: - BorderProperty(); - explicit BorderProperty(Microsoft::WRL::ComPtr brush); - BorderProperty(Microsoft::WRL::ComPtr brush, float width, float radius_x, float radius_y, Microsoft::WRL::ComPtr stroke_style = nullptr); - BorderProperty(const BorderProperty& other) = default; - BorderProperty(BorderProperty&& other) = default; - BorderProperty& operator=(const BorderProperty& other) = default; - BorderProperty& operator=(BorderProperty&& other) = default; - ~BorderProperty() = default; - - - Microsoft::WRL::ComPtr GetBrush() const - { - return brush_; - } - - float GetStrokeWidth() const - { - return stroke_width_; - } - - Microsoft::WRL::ComPtr GetStrokeStyle() const - { - return stroke_style_; - } - - float GetRadiusX() const - { - return radius_x_; - } - - float GetRadiusY() const - { - return radius_y_; - } - - void SetBrush(Microsoft::WRL::ComPtr brush) - { - Require(brush == nullptr, "Brush of BorderProperty mustn't be null."); - brush_ = std::move(brush); - } - - void SetStrokeWidth(const float stroke_width) - { - Require(stroke_width >= 0.0f, "Stroke width must be no less than 0."); - stroke_width_ = stroke_width; - } - - void SetStrokeStyle(Microsoft::WRL::ComPtr stroke_style) - { - stroke_style_ = std::move(stroke_style); - } - - void SetRadiusX(const float radius_x) - { - Require(radius_x >= 0.0f, "Radius-x must be no less than 0."); - radius_x_ = radius_x; - } - - void SetRadiusY(const float radius_y) - { - Require(radius_y >= 0.0f, "Radius-y must be no less than 0."); - radius_y_ = radius_y; - } - - private: - Microsoft::WRL::ComPtr brush_; - float stroke_width_ = 1.0f; - float radius_x_ = 0.0f; - float radius_y_ = 0.0f; - Microsoft::WRL::ComPtr stroke_style_ = nullptr; - }; -} diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 617c50c7..ee2abad0 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -3,292 +3,192 @@ #include #include -#include "window.hpp" #include "application.hpp" -#include "graph/graph.hpp" -#include "exception.hpp" #include "cru_debug.hpp" #include "d2d_util.hpp" +#include "exception.hpp" +#include "graph/graph.hpp" #include "math_util.hpp" +#include "window.hpp" #ifdef CRU_DEBUG_LAYOUT #include "ui_manager.hpp" #endif -namespace cru::ui -{ - - void Control::SetParent(Control* parent) - { - const auto old_parent = GetParent(); - parent_ = parent; - const auto new_parent = GetParent(); - if (old_parent != new_parent) - OnParentChanged(old_parent, new_parent); - } - - void Control::SetDescendantWindow(Window* window) - { - if (window == nullptr && window_ == nullptr) - return; - - //You can only attach or detach window. - assert((window != nullptr && window_ == nullptr) || (window == nullptr && window_ != nullptr)); - - if (window == nullptr) - { - const auto old = window_; - TraverseDescendants([old](Control* control) - { - control->window_ = nullptr; - control->OnDetachToWindow(old); - }); - } - else - TraverseDescendants([window](Control* control) - { - control->window_ = window; - control->OnAttachToWindow(window); - }); - } - - void Control::TraverseDescendants(const std::function& predicate) - { - TraverseDescendantsInternal(this, predicate); - } - - void Control::TraverseDescendantsInternal(Control * control, const std::function& predicate) - { - predicate(control); - for (auto c: control->GetInternalChildren()) - TraverseDescendantsInternal(c, predicate); - } - - - Point Control::ControlToWindow(const Point& point) const - { - const auto position = GetPositionInWindow(); - return Point(point.x + position.x, point.y + position.y); - } - - Point Control::WindowToControl(const Point & point) const - { - const auto position = GetPositionInWindow(); - return Point(point.x - position.x, point.y - position.y); - } - - bool Control::RequestFocus() - { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->RequestFocusFor(this); - } - - bool Control::HasFocus() - { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->GetFocusControl() == this; - } - - void Control::SetCursor(const Cursor::Ptr& cursor) - { - if (cursor != cursor_) - { - cursor_ = cursor; - const auto window = GetWindow(); - if (window && window->GetMouseHoverControl() == this) - window->UpdateCursor(); - } - } - - void Control::OnParentChanged(Control* old_parent, Control* new_parent) - { - - } - - void Control::OnAttachToWindow(Window* window) - { - - } - - void Control::OnDetachToWindow(Window * window) - { - - } - - void Control::OnMouseClickBegin(MouseButton button) - { - - } - - void Control::OnMouseClickEnd(MouseButton button) - { - - } - - - const std::vector NoChildControl::empty_control_vector{}; +namespace cru::ui { +void Control::_SetParent(Control* parent) { + const auto old_parent = GetParent(); + parent_ = parent; + const auto new_parent = GetParent(); + if (old_parent != new_parent) OnParentChanged(old_parent, new_parent); +} - ContentControl::ContentControl() : child_vector_{nullptr}, child_(child_vector_[0]) - { +void Control::_SetDescendantWindow(Window* window) { + if (window == nullptr && window_ == nullptr) return; + + // You can only attach or detach window. + assert((window != nullptr && window_ == nullptr) || + (window == nullptr && window_ != nullptr)); + + if (window == nullptr) { + const auto old = window_; + TraverseDescendants([old](Control* control) { + control->window_ = nullptr; + control->OnDetachToWindow(old); + }); + } else + TraverseDescendants([window](Control* control) { + control->window_ = window; + control->OnAttachToWindow(window); + }); +} - } +void Control::TraverseDescendants( + const std::function& predicate) { + TraverseDescendantsInternal(this, predicate); +} - ContentControl::~ContentControl() - { - delete child_; - } +void Control::TraverseDescendantsInternal( + Control* control, const std::function& predicate) { + predicate(control); + for (auto c : control->GetChildren()) + TraverseDescendantsInternal(c, predicate); +} +bool Control::RequestFocus() { + auto window = GetWindow(); + if (window == nullptr) return false; - void ContentControl::SetChild(Control* child) - { - if (child == child_) - return; + return window->RequestFocusFor(this); +} - const auto window = GetWindow(); - const auto old_child = child_; - child_ = child; - if (old_child) - { - old_child->SetParent(nullptr); - old_child->SetDescendantWindow(nullptr); - } - if (child) - { - child->SetParent(this); - child->SetDescendantWindow(window); - } - OnChildChanged(old_child, child); - } +bool Control::HasFocus() { + auto window = GetWindow(); + if (window == nullptr) return false; - void ContentControl::OnChildChanged(Control* old_child, Control* new_child) - { + return window->GetFocusControl() == this; +} - } +void Control::SetCursor(const Cursor::Ptr& cursor) { + if (cursor != cursor_) { + cursor_ = cursor; + const auto window = GetWindow(); + if (window && window->GetMouseHoverControl() == this) + window->UpdateCursor(); + } +} - void ControlAddChildCheck(Control* control) - { - if (control->GetParent() != nullptr) - throw std::invalid_argument("The control already has a parent."); +void Control::OnParentChanged(Control* old_parent, Control* new_parent) {} - if (dynamic_cast(control)) - throw std::invalid_argument("Can't add a window as child."); - } +void Control::OnAttachToWindow(Window* window) {} - MultiChildControl::~MultiChildControl() - { - for (const auto child : children_) - delete child; - } +void Control::OnDetachToWindow(Window* window) {} - void MultiChildControl::AddChild(Control* control) - { - ControlAddChildCheck(control); +void Control::OnMouseClickBegin(MouseButton button) {} - children_.push_back(control); +void Control::OnMouseClickEnd(MouseButton button) {} - control->SetParent(this); - control->SetDescendantWindow(GetWindow()); +const std::vector NoChildControl::empty_control_vector{}; - OnAddChild(control); - } +ContentControl::ContentControl() + : child_vector_{nullptr}, child_(child_vector_[0]) {} - void MultiChildControl::AddChild(Control* control, const int position) - { - ControlAddChildCheck(control); +ContentControl::~ContentControl() { delete child_; } - if (position < 0 || static_cast(position) > this->children_.size()) - throw std::invalid_argument("The position is out of range."); +void ContentControl::SetChild(Control* child) { + if (child == child_) return; - children_.insert(this->children_.cbegin() + position, control); + const auto window = GetWindow(); + const auto old_child = child_; + child_ = child; + if (old_child) { + old_child->_SetParent(nullptr); + old_child->_SetDescendantWindow(nullptr); + } + if (child) { + child->_SetParent(this); + child->_SetDescendantWindow(window); + } + OnChildChanged(old_child, child); +} - control->SetParent(this); - control->SetDescendantWindow(GetWindow()); +void ContentControl::OnChildChanged(Control* old_child, Control* new_child) {} - OnAddChild(control); - } +void ControlAddChildCheck(Control* control) { + if (control->GetParent() != nullptr) + throw std::invalid_argument("The control already has a parent."); - void MultiChildControl::RemoveChild(Control* child) - { - const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child); - if (i == this->children_.cend()) - throw std::invalid_argument("The argument child is not a child of this control."); + if (dynamic_cast(control)) + throw std::invalid_argument("Can't add a window as child."); +} + +MultiChildControl::~MultiChildControl() { + for (const auto child : children_) delete child; +} + +void MultiChildControl::AddChild(Control* control, const int position) { + ControlAddChildCheck(control); - children_.erase(i); + if (position < 0 || static_cast(position) > + this->children_.size()) + throw std::invalid_argument("The position is out of range."); - child->SetParent(nullptr); - child->SetDescendantWindow(nullptr); + children_.insert(this->children_.cbegin() + position, control); + + control->_SetParent(this); + control->_SetDescendantWindow(GetWindow()); + + OnAddChild(control); +} - OnRemoveChild(child); - } +void MultiChildControl::RemoveChild(const int position) { + if (position < 0 || static_castchildren_.size())>(position) >= + this->children_.size()) + throw std::invalid_argument("The position is out of range."); - void MultiChildControl::RemoveChild(const int position) - { - if (position < 0 || static_castchildren_.size())>(position) >= this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - const auto i = children_.cbegin() + position; - const auto child = *i; - - children_.erase(i); - - child->SetParent(nullptr); - child->SetDescendantWindow(nullptr); - - OnRemoveChild(child); - } - - void MultiChildControl::OnAddChild(Control* child) - { - - } - - void MultiChildControl::OnRemoveChild(Control* child) - { - - } - - std::list GetAncestorList(Control* control) - { - std::list l; - while (control != nullptr) - { - l.push_front(control); - control = control->GetParent(); - } - return l; - } + const auto i = children_.cbegin() + position; + const auto child = *i; - Control* FindLowestCommonAncestor(Control * left, Control * right) - { - if (left == nullptr || right == nullptr) - return nullptr; + children_.erase(i); - auto&& left_list = GetAncestorList(left); - auto&& right_list = GetAncestorList(right); - - // the root is different - if (left_list.front() != right_list.front()) - return nullptr; + child->_SetParent(nullptr); + child->_SetDescendantWindow(nullptr); + + OnRemoveChild(child); +} + +void MultiChildControl::OnAddChild(Control* child) {} + +void MultiChildControl::OnRemoveChild(Control* child) {} + +std::list GetAncestorList(Control* control) { + std::list l; + while (control != nullptr) { + l.push_front(control); + control = control->GetParent(); + } + return l; +} - // find the last same control or the last control (one is ancestor of the other) - auto left_i = left_list.cbegin(); - auto right_i = right_list.cbegin(); - while (true) - { - if (left_i == left_list.cend()) - return *(--left_i); - if (right_i == right_list.cend()) - return *(--right_i); - if (*left_i != *right_i) - return *(--left_i); - ++left_i; - ++right_i; - } - } +Control* FindLowestCommonAncestor(Control* left, Control* right) { + if (left == nullptr || right == nullptr) return nullptr; + + auto&& left_list = GetAncestorList(left); + auto&& right_list = GetAncestorList(right); + + // the root is different + if (left_list.front() != right_list.front()) return nullptr; + + // find the last same control or the last control (one is ancestor of the + // other) + auto left_i = left_list.cbegin(); + auto right_i = right_list.cbegin(); + while (true) { + if (left_i == left_list.cend()) return *(--left_i); + if (right_i == right_list.cend()) return *(--right_i); + if (*left_i != *right_i) return *(--left_i); + ++left_i; + ++right_i; + } } +} // namespace cru::ui diff --git a/src/ui/control.hpp b/src/ui/control.hpp index 8e69fb07..e85d0e6d 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -1,357 +1,243 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" -#include #include +#include #include +#include "system_headers.hpp" #include "base.hpp" -#include "ui_base.hpp" -#include "layout_base.hpp" -#include "events/ui_event.hpp" #include "cursor.hpp" -#include "any_map.hpp" +#include "events/ui_event.hpp" #include "input_util.hpp" +#include "ui_base.hpp" -namespace cru::ui -{ - class Window; - - - class Control : public Object - { - friend class Window; - - protected: - Control() = default; - public: - Control(const Control& other) = delete; - Control(Control&& other) = delete; - Control& operator=(const Control& other) = delete; - Control& operator=(Control&& other) = delete; - ~Control() override = default; - - public: - virtual StringView GetControlType() const = 0; - - - //*************** region: tree *************** - public: - //Get the window if attached, otherwise, return nullptr. - Window* GetWindow() const - { - return window_; - } - - Control* GetParent() const - { - return parent_; - } - - void SetParent(Control* parent); - - void SetDescendantWindow(Window* window); - - //Traverse the tree rooted the control including itself. - void TraverseDescendants(const std::function& predicate); - - private: - static void TraverseDescendantsInternal(Control* control, const std::function& predicate); - - //*************** region: position *************** - public: - virtual Point GetPositionInWindow() const = 0; - - //Local point to absolute point. - Point ControlToWindow(const Point& point) const; - - //Absolute point to local point. - Point WindowToControl(const Point& point) const; - - - - //*************** region: focus *************** - public: - bool RequestFocus(); - - bool HasFocus(); - - - //*************** 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_; - } - - 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 mouse_enter_event; - //Raised when mouse is leave the control. - events::RoutedEvent mouse_leave_event; - //Raised when mouse is move in the control. - events::RoutedEvent mouse_move_event; - //Raised when a mouse button is pressed in the control. - events::RoutedEvent mouse_down_event; - //Raised when a mouse button is released in the control. - events::RoutedEvent mouse_up_event; - //Raised when a mouse button is pressed in the control and released in the control with mouse not leaving it between two operations. - events::RoutedEvent mouse_click_event; - - events::RoutedEvent mouse_wheel_event; - - events::RoutedEvent key_down_event; - events::RoutedEvent key_up_event; - events::RoutedEvent char_event; - - events::RoutedEvent get_focus_event; - events::RoutedEvent lose_focus_event; - - - //*************** region: tree *************** - protected: - virtual const std::vector& GetInternalChildren() const = 0; - - virtual void OnParentChanged(Control* old_parent, Control* new_parent); - virtual void OnAttachToWindow(Window* window); - virtual void OnDetachToWindow(Window* window); - - - //*************** region: additional mouse event *************** - protected: - virtual void OnMouseClickBegin(MouseButton button); - virtual void OnMouseClickEnd(MouseButton button); - - - private: - Window * window_ = nullptr; - Control* parent_ = nullptr; - - Cursor::Ptr cursor_{}; - - AnyMap additional_property_map_{}; - }; +namespace cru::ui { +class Window; +class Control : public Object { + friend class Window; - - class NoChildControl : public Control - { - private: - // used in GetInternalChildren. - static const std::vector empty_control_vector; + protected: + Control() = default; - protected: - NoChildControl() = default; - public: - NoChildControl(const NoChildControl& other) = delete; - NoChildControl(NoChildControl&& other) = delete; - NoChildControl& operator=(const NoChildControl& other) = delete; - NoChildControl& operator=(NoChildControl&& other) = delete; - ~NoChildControl() override = default; - - protected: - const std::vector& GetInternalChildren() const override final - { - return empty_control_vector; - } - }; - - - class ContentControl : public Control - { - protected: - ContentControl(); - public: - 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 - { - return child_; - } - - void SetChild(Control* child); - - protected: - const std::vector& GetInternalChildren() const override final - { - return child_vector_; - } - - // Override should call base. - virtual void OnChildChanged(Control* old_child, Control* new_child); - - private: - std::vector child_vector_; - Control*& child_; - }; - - - class MultiChildControl : public Control - { - protected: - MultiChildControl() = default; - public: - MultiChildControl(const MultiChildControl& other) = delete; - MultiChildControl(MultiChildControl&& other) = delete; - MultiChildControl& operator=(const MultiChildControl& other) = delete; - MultiChildControl& operator=(MultiChildControl&& other) = delete; - ~MultiChildControl() override; - - const std::vector& GetInternalChildren() const override final - { - return children_; - } - - const std::vector& GetChildren() const - { - return children_; - } - - //Add a child at tail. - void AddChild(Control* control); - - //Add a child before the position. - void AddChild(Control* control, int position); - - //Remove a child. - void RemoveChild(Control* child); - - //Remove a child at specified position. - void RemoveChild(int position); - - protected: - virtual void OnAddChild(Control* child); - virtual void OnRemoveChild(Control* child); - - private: - std::vector children_; - }; - - - - //*************** region: event dispatcher helper *************** - - // Dispatch the event. - // - // This will raise routed event of the control and its parent and parent's - // parent ... (until "last_receiver" if it's not nullptr) with appropriate args. - // - // First tunnel from top to bottom possibly stopped by "handled" flag in EventArgs. - // Second bubble from bottom to top possibly stopped by "handled" flag in EventArgs. - // Last direct to each control. - // - // Args is of type "EventArgs". The first init argument is "sender", which is - // automatically bound to each receiving control. The second init argument is - // "original_sender", which is unchanged. And "args" will be perfectly forwarded - // as the rest arguments. - template - void DispatchEvent(Control* const original_sender, events::RoutedEvent Control::* event_ptr, Control* const last_receiver, Args&&... args) - { - std::list receive_list; - - auto parent = original_sender; - while (parent != last_receiver) - { - receive_list.push_back(parent); - parent = parent->GetInternalParent(); - } - - auto handled = false; - - //tunnel - for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) - { - EventArgs event_args(*i, original_sender, std::forward(args)...); - (*i->*event_ptr).tunnel.Raise(event_args); - if (event_args.IsHandled()) - { - handled = true; - break; - } - } - - //bubble - if (!handled) - { - for (auto i : receive_list) - { - EventArgs event_args(i, original_sender, std::forward(args)...); - (i->*event_ptr).bubble.Raise(event_args); - if (event_args.IsHandled()) - break; - } - } - - //direct - for (auto i : receive_list) - { - EventArgs event_args(i, original_sender, std::forward(args)...); - (i->*event_ptr).direct.Raise(event_args); - } + public: + Control(const Control& other) = delete; + Control(Control&& other) = delete; + Control& operator=(const Control& other) = delete; + Control& operator=(Control&& other) = delete; + ~Control() override = default; + + public: + virtual StringView GetControlType() const = 0; + + //*************** region: tree *************** + public: + // Get the window if attached, otherwise, return nullptr. + Window* GetWindow() const { return window_; } + + Control* GetParent() const { return parent_; } + + virtual const std::vector& GetChildren() const = 0; + + // Traverse the tree rooted the control including itself. + void TraverseDescendants(const std::function& predicate); + + void _SetParent(Control* parent); + void _SetDescendantWindow(Window* window); + + private: + static void TraverseDescendantsInternal( + Control* control, const std::function& predicate); + + //*************** region: focus *************** + public: + bool RequestFocus(); + + bool HasFocus(); + + //*************** 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_; } + + void SetCursor(const Cursor::Ptr& cursor); + + //*************** region: events *************** + public: + // Raised when mouse enter the control. + events::RoutedEvent mouse_enter_event; + // Raised when mouse is leave the control. + events::RoutedEvent mouse_leave_event; + // Raised when mouse is move in the control. + events::RoutedEvent mouse_move_event; + // Raised when a mouse button is pressed in the control. + events::RoutedEvent mouse_down_event; + // Raised when a mouse button is released in the control. + events::RoutedEvent mouse_up_event; + // Raised when a mouse button is pressed in the control and released in the + // control with mouse not leaving it between two operations. + events::RoutedEvent mouse_click_event; + + events::RoutedEvent mouse_wheel_event; + + events::RoutedEvent key_down_event; + events::RoutedEvent key_up_event; + events::RoutedEvent char_event; + + events::RoutedEvent get_focus_event; + events::RoutedEvent lose_focus_event; + + //*************** region: tree *************** + protected: + virtual void OnParentChanged(Control* old_parent, Control* new_parent); + virtual void OnAttachToWindow(Window* window); + virtual void OnDetachToWindow(Window* window); + + //*************** region: additional mouse event *************** + protected: + virtual void OnMouseClickBegin(MouseButton button); + virtual void OnMouseClickEnd(MouseButton button); + + private: + Window* window_ = nullptr; + Control* parent_ = nullptr; + + Cursor::Ptr cursor_{}; +}; + +class NoChildControl : public Control { + private: + static const std::vector empty_control_vector; + + protected: + NoChildControl() = default; + + public: + NoChildControl(const NoChildControl& other) = delete; + NoChildControl(NoChildControl&& other) = delete; + NoChildControl& operator=(const NoChildControl& other) = delete; + NoChildControl& operator=(NoChildControl&& other) = delete; + ~NoChildControl() override = default; + + protected: + const std::vector& GetChildren() const override final { + return empty_control_vector; + } +}; + +class ContentControl : public Control { + protected: + ContentControl(); + + public: + ContentControl(const ContentControl& other) = delete; + ContentControl(ContentControl&& other) = delete; + ContentControl& operator=(const ContentControl& other) = delete; + ContentControl& operator=(ContentControl&& other) = delete; + ~ContentControl() override; + + const std::vector& GetChildren() const override final { + return child_vector_; + } + Control* GetChild() const { return child_; } + void SetChild(Control* child); + + protected: + virtual void OnChildChanged(Control* old_child, Control* new_child); + + private: + std::vector child_vector_; + Control*& child_; +}; + +class MultiChildControl : public Control { + protected: + MultiChildControl() = default; + + public: + MultiChildControl(const MultiChildControl& other) = delete; + MultiChildControl(MultiChildControl&& other) = delete; + MultiChildControl& operator=(const MultiChildControl& other) = delete; + MultiChildControl& operator=(MultiChildControl&& other) = delete; + ~MultiChildControl() override; + + const std::vector& GetChildren() const override final { + return children_; + } + + void AddChild(Control* control, int position); + + void RemoveChild(int position); + + protected: + virtual void OnAddChild(Control* child); + virtual void OnRemoveChild(Control* child); + + private: + std::vector children_; +}; + +//*************** region: event dispatcher helper *************** + +// Dispatch the event. +// +// This will raise routed event of the control and its parent and parent's +// parent ... (until "last_receiver" if it's not nullptr) with appropriate args. +// +// First tunnel from top to bottom possibly stopped by "handled" flag in +// EventArgs. Second bubble from bottom to top possibly stopped by "handled" +// flag in EventArgs. Last direct to each control. +// +// Args is of type "EventArgs". The first init argument is "sender", which is +// automatically bound to each receiving control. The second init argument is +// "original_sender", which is unchanged. And "args" will be perfectly forwarded +// as the rest arguments. +template +void DispatchEvent(Control* const original_sender, + events::RoutedEvent Control::*event_ptr, + Control* const last_receiver, Args&&... args) { + std::list receive_list; + + auto parent = original_sender; + while (parent != last_receiver) { + receive_list.push_back(parent); + parent = parent->GetInternalParent(); + } + + auto handled = false; + + // tunnel + for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) { + EventArgs event_args(*i, original_sender, std::forward(args)...); + (*i->*event_ptr).tunnel.Raise(event_args); + if (event_args.IsHandled()) { + handled = true; + break; } - - - //*************** region: tree helper *************** - - // Find the lowest common ancestor. - // Return nullptr if "left" and "right" are not in the same tree. - Control* FindLowestCommonAncestor(Control* left, Control* right); - - - //*************** region: create helper *************** - - template - TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, Args&&... args) - { - static_assert(std::is_base_of_v, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward(args)...); - control->GetLayoutParams()->width = width; - control->GetLayoutParams()->height = height; - return control; + } + + // bubble + if (!handled) { + for (auto i : receive_list) { + EventArgs event_args(i, original_sender, std::forward(args)...); + (i->*event_ptr).bubble.Raise(event_args); + if (event_args.IsHandled()) break; } + } - template - TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args) - { - static_assert(std::is_base_of_v, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward(args)...); - control->GetLayoutParams()->padding = padding; - control->GetLayoutParams()->margin = margin; - return control; - } + // direct + for (auto i : receive_list) { + EventArgs event_args(i, original_sender, std::forward(args)...); + (i->*event_ptr).direct.Raise(event_args); + } +} - template - TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, const Thickness& padding, const Thickness& margin, Args&&... args) - { - static_assert(std::is_base_of_v, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward(args)...); - control->GetLayoutParams()->width = width; - control->GetLayoutParams()->height = height; - control->GetLayoutParams()->padding = padding; - control->GetLayoutParams()->margin = margin; - return control; - } +//*************** region: tree helper *************** - using ControlList = std::initializer_list; -} +// Find the lowest common ancestor. +// Return nullptr if "left" and "right" are not in the same tree. +Control* FindLowestCommonAncestor(Control* left, Control* right); + +} // namespace cru::ui diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp index d3fdc9b5..c3de7ca3 100644 --- a/src/ui/controls/linear_layout.cpp +++ b/src/ui/controls/linear_layout.cpp @@ -27,7 +27,7 @@ namespace cru::ui::controls // First measure Content and Exactly and count Stretch. if (orientation_ == Orientation::Horizontal) - for(auto control: GetInternalChildren()) + for(auto control: GetChildren()) { const auto mode = control->GetLayoutParams()->width.mode; if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) @@ -42,7 +42,7 @@ namespace cru::ui::controls stretch_control_list.push_back(control); } else - for(auto control: GetInternalChildren()) + for(auto control: GetChildren()) { const auto mode = control->GetLayoutParams()->height.mode; if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) @@ -82,7 +82,7 @@ namespace cru::ui::controls if (orientation_ == Orientation::Horizontal) { - for (auto control : GetInternalChildren()) + for (auto control : GetChildren()) { if (control->GetLayoutParams()->height.mode == MeasureMode::Stretch) { @@ -93,7 +93,7 @@ namespace cru::ui::controls } else { - for (auto control : GetInternalChildren()) + for (auto control : GetChildren()) { if (control->GetLayoutParams()->width.mode == MeasureMode::Stretch) { @@ -110,7 +110,7 @@ namespace cru::ui::controls void LinearLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) { float current_main_side_anchor = 0; - for(auto control: GetInternalChildren()) + for(auto control: GetChildren()) { const auto layout_params = control->GetLayoutParams(); const auto size = control->GetDesiredSize(); diff --git a/src/ui/d2d_util.hpp b/src/ui/d2d_util.hpp index 5f2e10b2..94ef6223 100644 --- a/src/ui/d2d_util.hpp +++ b/src/ui/d2d_util.hpp @@ -1,6 +1,4 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" #include "system_headers.hpp" diff --git a/src/ui/layout_base.cpp b/src/ui/layout_base.cpp deleted file mode 100644 index 5898a623..00000000 --- a/src/ui/layout_base.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "layout_base.hpp" - -namespace cru::ui -{ - -} diff --git a/src/ui/layout_base.hpp b/src/ui/layout_base.hpp deleted file mode 100644 index 527d9f98..00000000 --- a/src/ui/layout_base.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "ui_base.hpp" - -namespace cru::ui -{ - enum class Alignment - { - Center, - Start, - End - }; - - enum class MeasureMode - { - Exactly, - Content, - Stretch - }; - - enum class RectRange - { - Content, // content excluding padding, border and margin - Padding, // only including content and padding - HalfBorder, // including content, padding and half border - FullBorder, // including content, padding and full border - Margin // including content, padding, border and margin - }; - - struct LayoutSideParams final - { - constexpr static LayoutSideParams Exactly(const float length, const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Exactly, length, alignment); - } - - constexpr static LayoutSideParams Content(const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Content, 0, alignment); - } - - constexpr static LayoutSideParams Stretch(const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Stretch, 0, alignment); - } - - constexpr LayoutSideParams() = default; - - constexpr explicit LayoutSideParams(const MeasureMode mode, const float length, const Alignment alignment) - : length(length), mode(mode), alignment(alignment) - { - - } - - constexpr bool Validate() const - { - if (length < 0.0) - return false; - if (min.has_value() && min.value() < 0.0) - return false; - if (max.has_value() && max.value() < 0.0) - return false; - if (min.has_value() && max.has_value() && min.value() > max.value()) - return false; - return true; - } - - // only used in exactly mode, specify the exactly side length of content. - float length = 0.0; - MeasureMode mode = MeasureMode::Content; - Alignment alignment = Alignment::Center; - - // min and max specify the min/max side length of content. - // they are used as hint and respect the actual size that content needs. - // when mode is exactly, length is coerced into the min-max range. - std::optional min = std::nullopt; - std::optional max = std::nullopt; - }; - - struct BasicLayoutParams final - { - BasicLayoutParams() = default; - BasicLayoutParams(const BasicLayoutParams&) = default; - BasicLayoutParams(BasicLayoutParams&&) = default; - BasicLayoutParams& operator = (const BasicLayoutParams&) = default; - BasicLayoutParams& operator = (BasicLayoutParams&&) = default; - ~BasicLayoutParams() = default; - - bool Validate() const - { - return width.Validate() && height.Validate() && margin.Validate() && padding.Validate(); - } - - LayoutSideParams width; - LayoutSideParams height; - Thickness padding; - Thickness margin; - }; -} diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 0035d1be..0a0e693c 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -3,14 +3,6 @@ #include "cru_debug.hpp" 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::AddChild(RenderObject* render_object, const int position) { if (render_object->GetParent() != nullptr) throw std::invalid_argument("Render object already has a parent."); @@ -52,17 +44,6 @@ void RenderObject::Layout(const Rect& rect) { OnLayoutCore(Rect{Point::Zero(), rect.GetSize()}); } -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) {} diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index aeba1457..34f5dcee 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -8,11 +8,11 @@ #include "base.hpp" #include "ui/ui_base.hpp" +namespace cru::ui { +class Control; +} + namespace cru::ui::render { -struct IRenderHost : Interface { - virtual void InvalidatePaint() = 0; - virtual void InvalidateLayout() = 0; -}; class RenderObject : public Object { protected: @@ -25,8 +25,8 @@ class RenderObject : public Object { RenderObject& operator=(RenderObject&& other) = delete; ~RenderObject() override = default; - IRenderHost* GetRenderHost() const { return render_host_; } - void SetRenderHost(IRenderHost* new_render_host); + Control* GetAttachedControl() const { return control_; } + void SetAttachedControl(Control* new_control) { control_ = new_control; } RenderObject* GetParent() const { return parent_; } @@ -58,12 +58,6 @@ class RenderObject : public Object { virtual RenderObject* HitTest(const Point& point) = 0; protected: - virtual void OnRenderHostChanged(IRenderHost* old_render_host, - IRenderHost* new_render_host); - - void InvalidateRenderHostPaint() const; - void InvalidateRenderHostLayout() const; - virtual void OnParentChanged(RenderObject* old_parent, RenderObject* new_parent); @@ -80,7 +74,7 @@ class RenderObject : public Object { void OnLayoutCore(const Rect& rect); private: - IRenderHost* render_host_ = nullptr; + Control* control_ = nullptr; RenderObject* parent_ = nullptr; std::vector children_{}; diff --git a/src/ui/window.hpp b/src/ui/window.hpp index d3374684..ac26de22 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -3,276 +3,243 @@ // ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" #include #include +#include "system_headers.hpp" #include "control.hpp" #include "events/ui_event.hpp" -namespace cru::graph -{ - class WindowRenderTarget; +namespace cru::graph { +class WindowRenderTarget; } -namespace cru::ui -{ - class WindowManager : public Object - { - public: - static WindowManager* GetInstance(); - private: - WindowManager(); - public: - WindowManager(const WindowManager& other) = delete; - WindowManager(WindowManager&& other) = delete; - WindowManager& operator=(const WindowManager& other) = delete; - WindowManager& operator=(WindowManager&& other) = delete; - ~WindowManager() override = default; +namespace cru::ui { +class WindowManager : public Object { + public: + static WindowManager* GetInstance(); + private: + WindowManager(); - //Get the general window class for creating ordinary window. - WindowClass* GetGeneralWindowClass() const - { - return general_window_class_.get(); - } + public: + WindowManager(const WindowManager& other) = delete; + WindowManager(WindowManager&& other) = delete; + WindowManager& operator=(const WindowManager& other) = delete; + WindowManager& operator=(WindowManager&& other) = delete; + ~WindowManager() override = default; - //Register a window newly created. - //This function adds the hwnd to hwnd-window map. - //It should be called immediately after a window was created. - void RegisterWindow(HWND hwnd, Window* window); + // Get the general window class for creating ordinary window. + WindowClass* GetGeneralWindowClass() const { + return general_window_class_.get(); + } - //Unregister a window that is going to be destroyed. - //This function removes the hwnd from the hwnd-window map. - //It should be called immediately before a window is going to be destroyed, - void UnregisterWindow(HWND hwnd); + // Register a window newly created. + // This function adds the hwnd to hwnd-window map. + // It should be called immediately after a window was created. + void RegisterWindow(HWND hwnd, Window* window); - //Return a pointer to the Window object related to the HWND or nullptr if the hwnd is not in the map. - Window* FromHandle(HWND hwnd); + // Unregister a window that is going to be destroyed. + // This function removes the hwnd from the hwnd-window map. + // It should be called immediately before a window is going to be destroyed, + void UnregisterWindow(HWND hwnd); - std::vector GetAllWindows() const; + // Return a pointer to the Window object related to the HWND or nullptr if the + // hwnd is not in the map. + Window* FromHandle(HWND hwnd); - private: - std::unique_ptr general_window_class_; - std::map window_map_; - }; + std::vector GetAllWindows() const; + private: + std::unique_ptr general_window_class_; + std::map window_map_; +}; +class Window final : public ContentControl { + friend class WindowManager; - class Window final : public ContentControl - { - friend class WindowManager; - public: - static constexpr auto control_type = L"Window"; + public: + static constexpr auto control_type = L"Window"; - public: - static Window* CreateOverlapped(); - static Window* CreatePopup(Window* parent, bool caption = false); + public: + static Window* CreateOverlapped(); + static Window* CreatePopup(Window* parent, bool caption = false); - private: - struct tag_overlapped_constructor {}; - struct tag_popup_constructor {}; + private: + struct tag_overlapped_constructor {}; + struct tag_popup_constructor {}; - explicit Window(tag_overlapped_constructor); - Window(tag_popup_constructor, Window* parent, bool caption); + explicit Window(tag_overlapped_constructor); + Window(tag_popup_constructor, Window* parent, bool caption); - void BeforeCreateHwnd(); - void AfterCreateHwnd(WindowManager* window_manager); + void BeforeCreateHwnd(); + void AfterCreateHwnd(WindowManager* window_manager); - public: - Window(const Window& other) = delete; - Window(Window&& other) = delete; - Window& operator=(const Window& other) = delete; - Window& operator=(Window&& other) = delete; - ~Window() override; + public: + Window(const Window& other) = delete; + Window(Window&& other) = delete; + Window& operator=(const Window& other) = delete; + Window& operator=(Window&& other) = delete; + ~Window() override; - public: - StringView GetControlType() const override final; + public: + StringView GetControlType() const override final; - void SetDeleteThisOnDestroy(bool value); + void SetDeleteThisOnDestroy(bool value); - //*************** region: handle *************** + //*************** region: handle *************** - //Get the handle of the window. Return null if window is invalid. - HWND GetWindowHandle() const - { - return hwnd_; - } + // Get the handle of the window. Return null if window is invalid. + HWND GetWindowHandle() const { return hwnd_; } - //Return if the window is still valid, that is, hasn't been closed or destroyed. - bool IsWindowValid() const - { - return hwnd_ != nullptr; - } + // Return if the window is still valid, that is, hasn't been closed or + // destroyed. + bool IsWindowValid() const { return hwnd_ != nullptr; } + //*************** region: window operations *************** - //*************** region: window operations *************** + Window* GetParentWindow() const { return parent_window_; } - Window* GetParentWindow() const - { - return parent_window_; - } + // Close and destroy the window if the window is valid. + void Close(); - //Close and destroy the window if the window is valid. - void Close(); + // Send a repaint message to the window's message queue which may make the + // window repaint. + void InvalidateDraw() override final; - //Send a repaint message to the window's message queue which may make the window repaint. - void InvalidateDraw() override final; + // Show the window. + void Show(); - //Show the window. - void Show(); + // Hide thw window. + void Hide(); - //Hide thw window. - void Hide(); + // Get the client size. + Size GetClientSize(); - //Get the client size. - Size GetClientSize(); + // Set the client size and repaint. + void SetClientSize(const Size& size); - //Set the client size and repaint. - void SetClientSize(const Size& size); + // Get the rect of the window containing frame. + // The lefttop of the rect is relative to screen lefttop. + Rect GetWindowRect(); - //Get the rect of the window containing frame. - //The lefttop of the rect is relative to screen lefttop. - Rect GetWindowRect(); + // Set the rect of the window containing frame. + // The lefttop of the rect is relative to screen lefttop. + void SetWindowRect(const Rect& rect); - //Set the rect of the window containing frame. - //The lefttop of the rect is relative to screen lefttop. - void SetWindowRect(const Rect& rect); + // Set the lefttop of the window relative to screen. + void SetWindowPosition(const Point& position); - //Set the lefttop of the window relative to screen. - void SetWindowPosition(const Point& position); + Point PointToScreen(const Point& point); - Point PointToScreen(const Point& point); + Point PointFromScreen(const Point& point); - Point PointFromScreen(const Point& point); + // Handle the raw window message. + // Return true if the message is handled and get the result through "result" + // argument. Return false if the message is not handled. + bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, + LRESULT& result); - //Handle the raw window message. - //Return true if the message is handled and get the result through "result" argument. - //Return false if the message is not handled. - bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT& result); + //*************** region: mouse *************** - //*************** region: mouse *************** + Point GetMousePosition(); - Point GetMousePosition(); + Control* GetMouseHoverControl() const { return mouse_hover_control_; } - Control* GetMouseHoverControl() const - { - return mouse_hover_control_; - } + //*************** region: layout *************** - //*************** region: position and size *************** + void WindowInvalidateLayout(); - //Always return (0, 0) for a window. - Point GetOffset() override final; + void Relayout(); - //Get the size of client area for a window. - Size GetSize() override final; + void SetSizeFitContent(const Size& max_size = Size(1000, 1000)); - //This method has no effect for a window. Use SetClientSize instead. - void SetRect(const Rect& size) override final; + //*************** region: focus *************** - //Override. If point is in client area, it is in window. - bool IsPointInside(const Point& point) override final; + // Request focus for specified control. + bool RequestFocusFor(Control* control); - //*************** region: layout *************** + // Get the control that has focus. + Control* GetFocusControl(); - void WindowInvalidateLayout(); + //*************** region: mouse capture *************** - void Relayout(); + Control* CaptureMouseFor(Control* control); + Control* ReleaseCurrentMouseCapture(); - void SetSizeFitContent(const Size& max_size = Size(1000, 1000)); + //*************** region: cursor *************** + void UpdateCursor(); + //*************** region: debug *************** +#ifdef CRU_DEBUG_LAYOUT + bool IsDebugLayout() const { return debug_layout_; } - //*************** region: focus *************** + void SetDebugLayout(bool value); +#endif - //Request focus for specified control. - bool RequestFocusFor(Control* control); + public: + //*************** region: events *************** + Event activated_event; + Event deactivated_event; - //Get the control that has focus. - Control* GetFocusControl(); + Event native_message_event; + private: + //*************** region: native operations *************** - //*************** region: mouse capture *************** + // Get the client rect in pixel. + RECT GetClientRectPixel(); - Control* CaptureMouseFor(Control* control); - Control* ReleaseCurrentMouseCapture(); + bool IsMessageInQueue(UINT message); - - //*************** region: cursor *************** - void UpdateCursor(); + void SetCursorInternal(HCURSOR cursor); - //*************** region: debug *************** -#ifdef CRU_DEBUG_LAYOUT - bool IsDebugLayout() const - { - return debug_layout_; - } + //*************** region: native messages *************** - void SetDebugLayout(bool value); -#endif - - public: - //*************** region: events *************** - Event activated_event; - Event deactivated_event; - - Event native_message_event; - - private: - //*************** region: native operations *************** - - //Get the client rect in pixel. - RECT GetClientRectPixel(); - - bool IsMessageInQueue(UINT message); - - void SetCursorInternal(HCURSOR cursor); - - - //*************** region: native messages *************** - - void OnDestroyInternal(); - void OnPaintInternal(); - void OnResizeInternal(int new_width, int new_height); - - void OnSetFocusInternal(); - void OnKillFocusInternal(); - - void OnMouseMoveInternal(POINT point); - void OnMouseLeaveInternal(); - void OnMouseDownInternal(MouseButton button, POINT point); - void OnMouseUpInternal(MouseButton button, POINT point); - - void OnMouseWheelInternal(short delta, POINT point); - void OnKeyDownInternal(int virtual_code); - void OnKeyUpInternal(int virtual_code); - void OnCharInternal(wchar_t c); - - void OnActivatedInternal(); - void OnDeactivatedInternal(); - - //*************** region: event dispatcher helper *************** - - void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point); - - private: - bool delete_this_on_destroy_ = true; - - HWND hwnd_ = nullptr; - Window* parent_window_ = nullptr; - std::shared_ptr render_target_{}; - - Control* mouse_hover_control_ = nullptr; - - bool window_focus_ = false; - Control* focus_control_ = this; // "focus_control_" can't be nullptr - - Control* mouse_capture_control_ = nullptr; - - bool is_layout_invalid_ = false; + void OnDestroyInternal(); + void OnPaintInternal(); + void OnResizeInternal(int new_width, int new_height); + + void OnSetFocusInternal(); + void OnKillFocusInternal(); + + void OnMouseMoveInternal(POINT point); + void OnMouseLeaveInternal(); + void OnMouseDownInternal(MouseButton button, POINT point); + void OnMouseUpInternal(MouseButton button, POINT point); + + void OnMouseWheelInternal(short delta, POINT point); + void OnKeyDownInternal(int virtual_code); + void OnKeyUpInternal(int virtual_code); + void OnCharInternal(wchar_t c); + + void OnActivatedInternal(); + void OnDeactivatedInternal(); + + //*************** region: event dispatcher helper *************** + + void DispatchMouseHoverControlChangeEvent(Control* old_control, + Control* new_control, + const Point& point); + + private: + bool delete_this_on_destroy_ = true; + + HWND hwnd_ = nullptr; + Window* parent_window_ = nullptr; + std::shared_ptr render_target_{}; + + Control* mouse_hover_control_ = nullptr; + + bool window_focus_ = false; + Control* focus_control_ = this; // "focus_control_" can't be nullptr + + Control* mouse_capture_control_ = nullptr; + + bool is_layout_invalid_ = false; #ifdef CRU_DEBUG_LAYOUT - bool debug_layout_ = false; + bool debug_layout_ = false; #endif - }; -} +}; +} // namespace cru::ui -- cgit v1.2.3 From 616ebd78b543876388cb3d64f108abea041d4983 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 20 Mar 2019 19:26:55 +0800 Subject: ... --- CruUI.vcxproj | 24 +-- CruUI.vcxproj.filters | 72 ++----- src/ui/control.cpp | 25 +-- src/ui/control.hpp | 28 +-- src/ui/controls/button.cpp | 34 ---- src/ui/controls/button.hpp | 44 ---- src/ui/controls/flex_layout.cpp | 19 ++ src/ui/controls/flex_layout.hpp | 37 ++++ src/ui/controls/frame_layout.cpp | 70 ------- src/ui/controls/frame_layout.hpp | 40 ---- src/ui/controls/linear_layout.cpp | 151 -------------- src/ui/controls/linear_layout.hpp | 50 ----- src/ui/controls/list_item.cpp | 63 ------ src/ui/controls/list_item.hpp | 62 ------ src/ui/controls/popup_menu.cpp | 58 ------ src/ui/controls/popup_menu.hpp | 23 --- src/ui/controls/scroll_control.cpp | 384 ----------------------------------- src/ui/controls/scroll_control.hpp | 152 -------------- src/ui/controls/text_block.cpp | 30 +-- src/ui/controls/text_block.hpp | 62 +++--- src/ui/controls/text_box.cpp | 198 ------------------ src/ui/controls/text_box.hpp | 47 ----- src/ui/controls/text_control.cpp | 196 ------------------ src/ui/controls/text_control.hpp | 92 --------- src/ui/controls/toggle_button.cpp | 117 ----------- src/ui/controls/toggle_button.hpp | 57 ------ src/ui/render/text_render_object.hpp | 6 +- src/ui/ui_manager.cpp | 147 +++++--------- src/ui/ui_manager.hpp | 153 +++++--------- 29 files changed, 255 insertions(+), 2186 deletions(-) delete mode 100644 src/ui/controls/button.cpp delete mode 100644 src/ui/controls/button.hpp create mode 100644 src/ui/controls/flex_layout.cpp create mode 100644 src/ui/controls/flex_layout.hpp delete mode 100644 src/ui/controls/frame_layout.cpp delete mode 100644 src/ui/controls/frame_layout.hpp delete mode 100644 src/ui/controls/linear_layout.cpp delete mode 100644 src/ui/controls/linear_layout.hpp delete mode 100644 src/ui/controls/list_item.cpp delete mode 100644 src/ui/controls/list_item.hpp delete mode 100644 src/ui/controls/popup_menu.cpp delete mode 100644 src/ui/controls/popup_menu.hpp delete mode 100644 src/ui/controls/scroll_control.cpp delete mode 100644 src/ui/controls/scroll_control.hpp delete mode 100644 src/ui/controls/text_box.cpp delete mode 100644 src/ui/controls/text_box.hpp delete mode 100644 src/ui/controls/text_control.cpp delete mode 100644 src/ui/controls/text_control.hpp delete mode 100644 src/ui/controls/toggle_button.cpp delete mode 100644 src/ui/controls/toggle_button.hpp (limited to 'src/ui/control.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 9718355d..2954ecc8 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -128,26 +128,14 @@ - - - - - - - - + - - - - - - + @@ -169,12 +157,8 @@ - - - - - - + + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 40525798..35a97194 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -39,21 +39,6 @@ Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files @@ -66,18 +51,6 @@ Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files @@ -99,6 +72,12 @@ Source Files + + Source Files + + + Source Files + @@ -107,24 +86,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files @@ -164,18 +125,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files @@ -209,6 +158,12 @@ Header Files + + Header Files + + + Header Files + @@ -256,9 +211,6 @@ Source Files - - Source Files - diff --git a/src/ui/control.cpp b/src/ui/control.cpp index ee2abad0..98986d3c 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -1,20 +1,9 @@ #include "control.hpp" -#include #include -#include "application.hpp" -#include "cru_debug.hpp" -#include "d2d_util.hpp" -#include "exception.hpp" -#include "graph/graph.hpp" -#include "math_util.hpp" #include "window.hpp" -#ifdef CRU_DEBUG_LAYOUT -#include "ui_manager.hpp" -#endif - namespace cru::ui { void Control::_SetParent(Control* parent) { @@ -122,11 +111,11 @@ void ControlAddChildCheck(Control* control) { throw std::invalid_argument("Can't add a window as child."); } -MultiChildControl::~MultiChildControl() { +Layout::~Layout() { for (const auto child : children_) delete child; } -void MultiChildControl::AddChild(Control* control, const int position) { +void Layout::AddChild(Control* control, const int position) { ControlAddChildCheck(control); if (position < 0 || static_cast(position) > @@ -138,10 +127,10 @@ void MultiChildControl::AddChild(Control* control, const int position) { control->_SetParent(this); control->_SetDescendantWindow(GetWindow()); - OnAddChild(control); + OnAddChild(control, position); } -void MultiChildControl::RemoveChild(const int position) { +void Layout::RemoveChild(const int position) { if (position < 0 || static_castchildren_.size())>(position) >= this->children_.size()) throw std::invalid_argument("The position is out of range."); @@ -154,12 +143,12 @@ void MultiChildControl::RemoveChild(const int position) { child->_SetParent(nullptr); child->_SetDescendantWindow(nullptr); - OnRemoveChild(child); + OnRemoveChild(child, position); } -void MultiChildControl::OnAddChild(Control* child) {} +void Layout::OnAddChild(Control* child, int position) {} -void MultiChildControl::OnRemoveChild(Control* child) {} +void Layout::OnRemoveChild(Control* child, int position) {} std::list GetAncestorList(Control* control) { std::list l; diff --git a/src/ui/control.hpp b/src/ui/control.hpp index e85d0e6d..5f8ac02a 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -1,9 +1,6 @@ #pragma once #include "pre.hpp" -#include -#include -#include #include "system_headers.hpp" #include "base.hpp" @@ -15,6 +12,10 @@ namespace cru::ui { class Window; +namespace render { +class RenderObject; +} + class Control : public Object { friend class Window; @@ -50,6 +51,9 @@ class Control : public Object { static void TraverseDescendantsInternal( Control* control, const std::function& predicate); + public: + virtual render::RenderObject* GetRenderObject() const = 0; + //*************** region: focus *************** public: bool RequestFocus(); @@ -152,16 +156,16 @@ class ContentControl : public Control { Control*& child_; }; -class MultiChildControl : public Control { +class Layout : public Control { protected: - MultiChildControl() = default; + Layout() = default; public: - MultiChildControl(const MultiChildControl& other) = delete; - MultiChildControl(MultiChildControl&& other) = delete; - MultiChildControl& operator=(const MultiChildControl& other) = delete; - MultiChildControl& operator=(MultiChildControl&& other) = delete; - ~MultiChildControl() override; + Layout(const Layout& other) = delete; + Layout(Layout&& other) = delete; + Layout& operator=(const Layout& other) = delete; + Layout& operator=(Layout&& other) = delete; + ~Layout() override; const std::vector& GetChildren() const override final { return children_; @@ -172,8 +176,8 @@ class MultiChildControl : public Control { void RemoveChild(int position); protected: - virtual void OnAddChild(Control* child); - virtual void OnRemoveChild(Control* child); + virtual void OnAddChild(Control* child, int position); + virtual void OnRemoveChild(Control* child, int position); private: std::vector children_; diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp deleted file mode 100644 index d4537f54..00000000 --- a/src/ui/controls/button.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "button.hpp" - -#include "graph/graph.hpp" -#include "ui/ui_manager.hpp" - -namespace cru::ui::controls -{ - Button::Button() : - normal_border_{UiManager::GetInstance()->GetPredefineResources()->button_normal_border}, - pressed_border_{UiManager::GetInstance()->GetPredefineResources()->button_press_border} - { - SetBordered(true); - GetBorderProperty() = normal_border_; - - SetCursor(cursors::hand); - } - - StringView Button::GetControlType() const - { - return control_type; - } - - void Button::OnMouseClickBegin(MouseButton button) - { - GetBorderProperty() = pressed_border_; - UpdateBorder(); - } - - void Button::OnMouseClickEnd(MouseButton button) - { - GetBorderProperty() = normal_border_; - UpdateBorder(); - } -} diff --git a/src/ui/controls/button.hpp b/src/ui/controls/button.hpp deleted file mode 100644 index 6436f7c0..00000000 --- a/src/ui/controls/button.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include - -#include "ui/control.hpp" - -namespace cru::ui::controls -{ - class Button : public ContentControl - { - public: - static constexpr auto control_type = L"Button"; - - static Button* Create(Control* child = nullptr) - { - const auto button = new Button(); - button->SetChild(child); - return button; - } - - protected: - Button(); - - public: - Button(const Button& other) = delete; - Button(Button&& other) = delete; - Button& operator=(const Button& other) = delete; - Button& operator=(Button&& other) = delete; - ~Button() override = default; - - StringView GetControlType() const override final; - - protected: - void OnMouseClickBegin(MouseButton button) override final; - void OnMouseClickEnd(MouseButton button) override final; - - private: - BorderProperty normal_border_; - BorderProperty pressed_border_; - }; -} diff --git a/src/ui/controls/flex_layout.cpp b/src/ui/controls/flex_layout.cpp new file mode 100644 index 00000000..ebe61a6d --- /dev/null +++ b/src/ui/controls/flex_layout.cpp @@ -0,0 +1,19 @@ +#include "flex_layout.hpp" + +#include "ui/render/flex_layout_render_object.hpp" + +namespace cru::ui::controls { +using render::FlexLayoutRenderObject; + +FlexLayout::FlexLayout() { render_object_ = new FlexLayoutRenderObject(); } + +FlexLayout::~FlexLayout() { delete render_object_; } + +void FlexLayout::OnAddChild(Control* child, int position) { + render_object_->AddChild(child->GetRenderObject(), position); +} + +void FlexLayout::OnRemoveChild(Control* child, int position) { + render_object_->RemoveChild(position); +} +} // namespace cru::ui::controls diff --git a/src/ui/controls/flex_layout.hpp b/src/ui/controls/flex_layout.hpp new file mode 100644 index 00000000..6acd25dc --- /dev/null +++ b/src/ui/controls/flex_layout.hpp @@ -0,0 +1,37 @@ +#pragma once +#include "pre.hpp" + +#include "ui/control.hpp" + +namespace cru::ui::render { +class FlexLayoutRenderObject; +} + +namespace cru::ui::controls { + +class FlexLayout : public Layout { + public: + static constexpr auto control_type = L"FlexLayout"; + + public: + FlexLayout(); + FlexLayout(const FlexLayout& other) = delete; + FlexLayout(FlexLayout&& other) = delete; + FlexLayout& operator=(const FlexLayout& other) = delete; + FlexLayout& operator=(FlexLayout&& other) = delete; + ~FlexLayout() override; + + StringView GetControlType() const override final { return control_type; } + + render::RenderObject* GetRenderObject() const override { + return render_object_; + } + + protected: + void OnAddChild(Control* child, int position) override; + void OnRemoveChild(Control* child, int position) override; + + private: + render::FlexLayoutRenderObject* render_object_; +}; +} // namespace cru::ui::controls diff --git a/src/ui/controls/frame_layout.cpp b/src/ui/controls/frame_layout.cpp deleted file mode 100644 index d68bc338..00000000 --- a/src/ui/controls/frame_layout.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "frame_layout.hpp" - -namespace cru::ui::controls -{ - FrameLayout::FrameLayout() = default; - - FrameLayout::~FrameLayout() = default; - - StringView FrameLayout::GetControlType() const - { - return control_type; - } - - Size FrameLayout::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - auto max_child_size = Size::Zero(); - for (auto control: GetChildren()) - { - control->Measure(available_size, additional_info); - const auto&& size = control->GetDesiredSize(); - if (max_child_size.width < size.width) - max_child_size.width = size.width; - if (max_child_size.height < size.height) - max_child_size.height = size.height; - } - - // coerce size fro stretch. - for (auto control: GetChildren()) - { - auto size = control->GetDesiredSize(); - const auto layout_params = control->GetLayoutParams(); - if (layout_params->width.mode == MeasureMode::Stretch) - size.width = max_child_size.width; - if (layout_params->height.mode == MeasureMode::Stretch) - size.height = max_child_size.height; - control->SetDesiredSize(size); - } - - return max_child_size; - } - - void FrameLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - for (auto control: GetChildren()) - { - const auto layout_params = control->GetLayoutParams(); - const auto size = control->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(); - } - }; - - control->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); - } - } -} diff --git a/src/ui/controls/frame_layout.hpp b/src/ui/controls/frame_layout.hpp deleted file mode 100644 index c2d6f0d6..00000000 --- a/src/ui/controls/frame_layout.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include - -#include "ui/control.hpp" - -namespace cru::ui::controls -{ - class FrameLayout : public MultiChildControl - { - public: - static constexpr auto control_type = L"FrameLayout"; - - static FrameLayout* Create(const std::initializer_list& children = std::initializer_list{}) - { - const auto layout = new FrameLayout(); - for (auto child : children) - layout->AddChild(child); - return layout; - } - - protected: - FrameLayout(); - public: - FrameLayout(const FrameLayout& other) = delete; - FrameLayout(FrameLayout&& other) = delete; - FrameLayout& operator=(const FrameLayout& other) = delete; - FrameLayout& operator=(FrameLayout&& other) = delete; - ~FrameLayout() override; - - StringView GetControlType() const override final; - - protected: - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override; - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override; - }; -} diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp deleted file mode 100644 index c3de7ca3..00000000 --- a/src/ui/controls/linear_layout.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "linear_layout.hpp" - -#include - -#include "math_util.hpp" - -namespace cru::ui::controls -{ - LinearLayout::LinearLayout(const Orientation orientation) - : orientation_(orientation) - { - - } - - StringView LinearLayout::GetControlType() const - { - return control_type; - } - - Size LinearLayout::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - auto actual_size_for_children = Size::Zero(); - - float secondary_side_child_max_length = 0; - - std::list stretch_control_list; - - // First measure Content and Exactly and count Stretch. - if (orientation_ == Orientation::Horizontal) - for(auto control: GetChildren()) - { - const auto mode = control->GetLayoutParams()->width.mode; - if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) - { - Size current_available_size(AtLeast0(available_size.width - actual_size_for_children.width), available_size.height); - control->Measure(current_available_size, additional_info); - const auto size = control->GetDesiredSize(); - actual_size_for_children.width += size.width; - secondary_side_child_max_length = std::max(size.height, secondary_side_child_max_length); - } - else - stretch_control_list.push_back(control); - } - else - for(auto control: GetChildren()) - { - const auto mode = control->GetLayoutParams()->height.mode; - if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) - { - Size current_available_size(available_size.width, AtLeast0(available_size.height - actual_size_for_children.height)); - control->Measure(current_available_size, additional_info); - const auto size = control->GetDesiredSize(); - actual_size_for_children.height += size.height; - secondary_side_child_max_length = std::max(size.width, secondary_side_child_max_length); - } - else - stretch_control_list.push_back(control); - } - - if (orientation_ == Orientation::Horizontal) - { - const auto available_width = AtLeast0(available_size.width - actual_size_for_children.width) / stretch_control_list.size(); - for (const auto control : stretch_control_list) - { - control->Measure(Size(available_width, available_size.height), additional_info); - const auto size = control->GetDesiredSize(); - actual_size_for_children.width += size.width; - secondary_side_child_max_length = std::max(size.height, secondary_side_child_max_length); - } - } - else - { - const auto available_height = AtLeast0(available_size.height - actual_size_for_children.height) / stretch_control_list.size(); - for (const auto control : stretch_control_list) - { - control->Measure(Size(available_size.width, available_height), additional_info); - const auto size = control->GetDesiredSize(); - actual_size_for_children.height += size.height; - secondary_side_child_max_length = std::max(size.width, secondary_side_child_max_length); - } - } - - if (orientation_ == Orientation::Horizontal) - { - for (auto control : GetChildren()) - { - if (control->GetLayoutParams()->height.mode == MeasureMode::Stretch) - { - control->SetDesiredSize(Size(control->GetDesiredSize().width, secondary_side_child_max_length)); - } - } - actual_size_for_children.height = secondary_side_child_max_length; - } - else - { - for (auto control : GetChildren()) - { - if (control->GetLayoutParams()->width.mode == MeasureMode::Stretch) - { - control->SetDesiredSize(Size(secondary_side_child_max_length, control->GetDesiredSize().height)); - } - } - - actual_size_for_children.width = secondary_side_child_max_length; - } - - return actual_size_for_children; - } - - void LinearLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - float current_main_side_anchor = 0; - for(auto control: GetChildren()) - { - const auto layout_params = control->GetLayoutParams(); - const auto size = control->GetDesiredSize(); - const auto alignment = orientation_ == Orientation::Horizontal ? layout_params->height.alignment : layout_params->width.alignment; - - auto&& calculate_secondary_side_anchor = [alignment](const float layout_length, const float control_length) -> float - { - switch (alignment) - { - case Alignment::Center: - return (layout_length - control_length) / 2; - case Alignment::Start: - return 0; - case Alignment::End: - return layout_length - control_length; - default: - UnreachableCode(); - } - }; - - auto&& calculate_rect = [rect, size](const float anchor_left, const float anchor_top) - { - return Rect(Point(rect.left + anchor_left, rect.top + anchor_top), size); - }; - - if (orientation_ == Orientation::Horizontal) - { - control->Layout(calculate_rect(current_main_side_anchor, calculate_secondary_side_anchor(rect.height, size.height)), additional_info); - current_main_side_anchor += size.width; - } - else - { - control->Layout(calculate_rect(calculate_secondary_side_anchor(rect.width, size.width), current_main_side_anchor), additional_info); - current_main_side_anchor += size.height; - } - } - } -} diff --git a/src/ui/controls/linear_layout.hpp b/src/ui/controls/linear_layout.hpp deleted file mode 100644 index ceb1c4e6..00000000 --- a/src/ui/controls/linear_layout.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "ui/control.hpp" - -namespace cru::ui::controls -{ - // Min length of main side in layout params is of no meaning. - // All children will layout from start and redundant length is blank. - class LinearLayout : public MultiChildControl - { - public: - static constexpr auto control_type = L"LinearLayout"; - - enum class Orientation - { - Horizontal, - Vertical - }; - - static LinearLayout* Create(const Orientation orientation = Orientation::Vertical, const std::initializer_list& children = std::initializer_list()) - { - const auto linear_layout = new LinearLayout(orientation); - for (const auto control : children) - linear_layout->AddChild(control); - return linear_layout; - } - - protected: - explicit LinearLayout(Orientation orientation = Orientation::Vertical); - - public: - LinearLayout(const LinearLayout& other) = delete; - LinearLayout(LinearLayout&& other) = delete; - LinearLayout& operator=(const LinearLayout& other) = delete; - LinearLayout& operator=(LinearLayout&& other) = delete; - ~LinearLayout() override = default; - - StringView GetControlType() const override final; - - protected: - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override; - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override; - - private: - Orientation orientation_; - }; -} diff --git a/src/ui/controls/list_item.cpp b/src/ui/controls/list_item.cpp deleted file mode 100644 index 6dd37fe9..00000000 --- a/src/ui/controls/list_item.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "list_item.hpp" - -#include "ui/ui_manager.hpp" -#include "ui/d2d_util.hpp" - -namespace cru::ui::controls -{ - ListItem::ListItem() - { - const auto predefine_resources = UiManager::GetInstance()->GetPredefineResources(); - - brushes_[State::Normal].border_brush = predefine_resources->list_item_normal_border_brush; - brushes_[State::Normal].fill_brush = predefine_resources->list_item_normal_fill_brush; - brushes_[State::Hover] .border_brush = predefine_resources->list_item_hover_border_brush; - brushes_[State::Hover] .fill_brush = predefine_resources->list_item_hover_fill_brush; - brushes_[State::Select].border_brush = predefine_resources->list_item_select_border_brush; - brushes_[State::Select].fill_brush = predefine_resources->list_item_select_fill_brush; - - draw_foreground_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - const auto rect = Rect(Point::Zero(), GetRect(RectRange::Padding).GetSize()); - device_context->FillRectangle(Convert(rect), brushes_[state_].fill_brush.Get()); - device_context->DrawRectangle(Convert(rect.Shrink(Thickness(0.5))), brushes_[state_].border_brush.Get(), 1); - }); - - mouse_enter_event.direct.AddHandler([this](events::MouseEventArgs& args) - { - if (GetState() == State::Select) - return; - - if (IsAnyMouseButtonDown()) - return; - - SetState(State::Hover); - }); - - mouse_leave_event.direct.AddHandler([this](events::MouseEventArgs& args) - { - if (GetState() == State::Select) - return; - - SetState(State::Normal); - }); - - mouse_click_event.direct.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - SetState(State::Select); - }); - } - - StringView ListItem::GetControlType() const - { - return control_type; - } - - void ListItem::SetState(const State state) - { - state_ = state; - InvalidateDraw(); - } -} diff --git a/src/ui/controls/list_item.hpp b/src/ui/controls/list_item.hpp deleted file mode 100644 index bf8f8d8e..00000000 --- a/src/ui/controls/list_item.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include -#include - -#include "ui/control.hpp" - -namespace cru::ui::controls -{ - class ListItem : public ContentControl - { - public: - static constexpr auto control_type = L"ListItem"; - - enum class State - { - Normal, - Hover, - Select - }; - - private: - struct StateBrush - { - Microsoft::WRL::ComPtr border_brush; - Microsoft::WRL::ComPtr fill_brush; - }; - - public: - static ListItem* Create(Control* child = nullptr) - { - const auto list_item = new ListItem(); - list_item->SetChild(child); - return list_item; - } - - private: - ListItem(); - public: - ListItem(const ListItem& other) = delete; - ListItem(ListItem&& other) = delete; - ListItem& operator=(const ListItem& other) = delete; - ListItem& operator=(ListItem&& other) = delete; - ~ListItem() override = default; - - StringView GetControlType() const override; - - State GetState() const - { - return state_; - } - - void SetState(State state); - - private: - State state_ = State::Normal; - std::map brushes_{}; - }; -} diff --git a/src/ui/controls/popup_menu.cpp b/src/ui/controls/popup_menu.cpp deleted file mode 100644 index fbe9039d..00000000 --- a/src/ui/controls/popup_menu.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "popup_menu.hpp" - -#include "ui/window.hpp" -#include "text_block.hpp" -#include "list_item.hpp" -#include "linear_layout.hpp" -#include "ui/events/ui_event.hpp" - -namespace cru::ui::controls -{ - Window* CreatePopupMenu(const Point& anchor, const std::vector& items, Window* parent) - { - const auto popup = Window::CreatePopup(parent); - - popup->lose_focus_event.bubble.AddHandler([popup](events::FocusChangeEventArgs& args) - { - if (args.IsWindow()) - popup->Close(); - }); - - const auto create_menu_item = [popup](const String& text, const std::function& action) -> ListItem* - { - auto text_block = TextBlock::Create(text); - text_block->GetLayoutParams()->width.alignment = Alignment::Start; - - auto list_item = CreateWithLayout( - LayoutSideParams::Stretch(Alignment::Center), - LayoutSideParams::Content(Alignment::Start), - text_block - ); - - list_item->mouse_click_event.bubble.AddHandler([popup, action](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - { - action(); - popup->Close(); - } - }); - - return list_item; - }; - - const auto menu = LinearLayout::Create(LinearLayout::Orientation::Vertical); - - menu->SetBordered(true); - - for (const auto& item : items) - menu->AddChild(create_menu_item(item.first, item.second)); - - popup->SetChild(menu); - - popup->SetSizeFitContent(); - popup->SetWindowPosition(anchor); - - return popup; - } -} diff --git a/src/ui/controls/popup_menu.hpp b/src/ui/controls/popup_menu.hpp deleted file mode 100644 index a2916590..00000000 --- a/src/ui/controls/popup_menu.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include -#include -#include - -#include "base.hpp" -#include "ui/ui_base.hpp" - -namespace cru::ui -{ - class Window; -} - -namespace cru::ui::controls -{ - using MenuItemInfo = std::pair>; - - Window* CreatePopupMenu(const Point& anchor, const std::vector& items, Window* parent = nullptr); -} diff --git a/src/ui/controls/scroll_control.cpp b/src/ui/controls/scroll_control.cpp deleted file mode 100644 index a202e355..00000000 --- a/src/ui/controls/scroll_control.cpp +++ /dev/null @@ -1,384 +0,0 @@ -#include "scroll_control.hpp" - -#include - -#include "cru_debug.hpp" -#include "ui/d2d_util.hpp" -#include "exception.hpp" -#include "math_util.hpp" -#include "ui/ui_manager.hpp" -#include "ui/window.hpp" - -namespace cru::ui::controls -{ - constexpr auto scroll_bar_width = 15.0f; - - ScrollControl::ScrollControl(const bool container) - { - SetClipContent(true); - - draw_foreground_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - const auto predefined = UiManager::GetInstance()->GetPredefineResources(); - - if (is_horizontal_scroll_bar_visible_) - { - device_context->FillRectangle( - Convert(horizontal_bar_info_.border), - predefined->scroll_bar_background_brush.Get() - ); - - device_context->FillRectangle( - Convert(horizontal_bar_info_.bar), - predefined->scroll_bar_brush.Get() - ); - - device_context->DrawLine( - Convert(horizontal_bar_info_.border.GetLeftTop()), - Convert(horizontal_bar_info_.border.GetRightTop()), - predefined->scroll_bar_border_brush.Get() - ); - } - - if (is_vertical_scroll_bar_visible_) - { - device_context->FillRectangle( - Convert(vertical_bar_info_.border), - predefined->scroll_bar_background_brush.Get() - ); - - device_context->FillRectangle( - Convert(vertical_bar_info_.bar), - predefined->scroll_bar_brush.Get() - ); - - device_context->DrawLine( - Convert(vertical_bar_info_.border.GetLeftTop()), - Convert(vertical_bar_info_.border.GetLeftBottom()), - predefined->scroll_bar_border_brush.Get() - ); - } - }); - - mouse_down_event.tunnel.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - { - const auto point = args.GetPoint(this); - if (is_vertical_scroll_bar_visible_ && vertical_bar_info_.bar.IsPointInside(point)) - { - GetWindow()->CaptureMouseFor(this); - is_pressing_scroll_bar_ = Orientation::Vertical; - pressing_delta_ = point.y - vertical_bar_info_.bar.top; - args.SetHandled(); - return; - } - - if (is_horizontal_scroll_bar_visible_ && horizontal_bar_info_.bar.IsPointInside(point)) - { - GetWindow()->CaptureMouseFor(this); - pressing_delta_ = point.x - horizontal_bar_info_.bar.left; - is_pressing_scroll_bar_ = Orientation::Horizontal; - args.SetHandled(); - return; - } - } - }); - - mouse_move_event.tunnel.AddHandler([this](events::MouseEventArgs& args) - { - const auto mouse_point = args.GetPoint(this); - - if (is_pressing_scroll_bar_ == Orientation::Horizontal) - { - const auto new_head_position = mouse_point.x - pressing_delta_; - const auto new_offset = new_head_position / horizontal_bar_info_.border.width * view_width_; - SetScrollOffset(new_offset, std::nullopt); - args.SetHandled(); - return; - } - - if (is_pressing_scroll_bar_ == Orientation::Vertical) - { - const auto new_head_position = mouse_point.y - pressing_delta_; - const auto new_offset = new_head_position / vertical_bar_info_.border.height * view_height_; - SetScrollOffset(std::nullopt, new_offset); - args.SetHandled(); - return; - } - }); - - mouse_up_event.tunnel.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left && is_pressing_scroll_bar_.has_value()) - { - GetWindow()->ReleaseCurrentMouseCapture(); - is_pressing_scroll_bar_ = std::nullopt; - args.SetHandled(); - } - }); - - mouse_wheel_event.bubble.AddHandler([this](events::MouseWheelEventArgs& args) - { - constexpr const auto view_delta = 30.0f; - - if (args.GetDelta() == 0.0f) - return; - - const auto content_rect = GetRect(RectRange::Content); - if (IsVerticalScrollEnabled() && GetScrollOffsetY() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewHeight() - content_rect.height))) - { - SetScrollOffset(std::nullopt, GetScrollOffsetY() - args.GetDelta() / WHEEL_DELTA * view_delta); - args.SetHandled(); - return; - } - - if (IsHorizontalScrollEnabled() && GetScrollOffsetX() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewWidth() - content_rect.width))) - { - SetScrollOffset(GetScrollOffsetX() - args.GetDelta() / WHEEL_DELTA * view_delta, std::nullopt); - args.SetHandled(); - return; - } - }); - } - - ScrollControl::~ScrollControl() - { - - } - - StringView ScrollControl::GetControlType() const - { - return control_type; - } - - void ScrollControl::SetHorizontalScrollEnabled(const bool enable) - { - horizontal_scroll_enabled_ = enable; - InvalidateLayout(); - InvalidateDraw(); - } - - void ScrollControl::SetVerticalScrollEnabled(const bool enable) - { - vertical_scroll_enabled_ = enable; - InvalidateLayout(); - InvalidateDraw(); - } - - void ScrollControl::SetHorizontalScrollBarVisibility(const ScrollBarVisibility visibility) - { - if (visibility != horizontal_scroll_bar_visibility_) - { - horizontal_scroll_bar_visibility_ = visibility; - switch (visibility) - { - case ScrollBarVisibility::Always: - is_horizontal_scroll_bar_visible_ = true; - break; - case ScrollBarVisibility::None: - is_horizontal_scroll_bar_visible_ = false; - break; - case ScrollBarVisibility::Auto: - UpdateScrollBarVisibility(); - } - InvalidateDraw(); - } - } - - void ScrollControl::SetVerticalScrollBarVisibility(const ScrollBarVisibility visibility) - { - if (visibility != vertical_scroll_bar_visibility_) - { - vertical_scroll_bar_visibility_ = visibility; - switch (visibility) - { - case ScrollBarVisibility::Always: - is_vertical_scroll_bar_visible_ = true; - break; - case ScrollBarVisibility::None: - is_vertical_scroll_bar_visible_ = false; - break; - case ScrollBarVisibility::Auto: - UpdateScrollBarVisibility(); - } - InvalidateDraw(); - } - - } - - void ScrollControl::SetScrollOffset(std::optional x, std::optional y) - { - CoerceAndSetOffsets(x.value_or(GetScrollOffsetX()), y.value_or(GetScrollOffsetY())); - } - - void ScrollControl::SetViewWidth(const float length) - { - view_width_ = length; - } - - void ScrollControl::SetViewHeight(const float length) - { - view_height_ = length; - } - - Size ScrollControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - const auto layout_params = GetLayoutParams(); - - auto available_size_for_children = available_size; - if (IsHorizontalScrollEnabled()) - { - if (layout_params->width.mode == MeasureMode::Content) - debug::DebugMessage(L"ScrollControl: Width measure mode is Content and horizontal scroll is enabled. So Stretch is used instead."); - - available_size_for_children.width = std::numeric_limits::max(); - } - - if (IsVerticalScrollEnabled()) - { - if (layout_params->height.mode == MeasureMode::Content) - debug::DebugMessage(L"ScrollControl: Height measure mode is Content and vertical scroll is enabled. So Stretch is used instead."); - - available_size_for_children.height = std::numeric_limits::max(); - } - - const auto child = GetChild(); - - auto size = Size::Zero(); - if (child) - { - child->Measure(available_size_for_children, AdditionalMeasureInfo{false, false}); - size = child->GetDesiredSize(); - } - - - auto result = size; - if (IsHorizontalScrollEnabled()) - { - SetViewWidth(size.width); - result.width = available_size.width; - } - if (IsVerticalScrollEnabled()) - { - SetViewHeight(size.height); - result.height = available_size.height; - } - - return result; - } - - void ScrollControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - auto layout_rect = rect; - - if (IsHorizontalScrollEnabled()) - layout_rect.width = GetViewWidth(); - if (IsVerticalScrollEnabled()) - layout_rect.height = GetViewHeight(); - - const auto child = GetChild(); - - 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( - IsHorizontalScrollEnabled() ? layout_rect.left + offset_x_ : calculate_anchor(layout_rect.left, layout_params->width.alignment, layout_rect.width, size.width), - IsVerticalScrollEnabled() ? layout_rect.top + offset_y_ : calculate_anchor(layout_rect.top, layout_params->height.alignment, layout_rect.height, size.height) - ), size), additional_info); - } - } - - void ScrollControl::OnRectChange(const Rect& old_rect, const Rect& new_rect) - { - UpdateScrollBarBorderInfo(); - CoerceAndSetOffsets(offset_x_, offset_y_, false); - UpdateScrollBarVisibility(); - } - - void ScrollControl::CoerceAndSetOffsets(const float offset_x, const float offset_y, const bool update_children) - { - const auto old_offset_x = offset_x_; - const auto old_offset_y = offset_y_; - - const auto content_rect = GetRect(RectRange::Content); - offset_x_ = Coerce(offset_x, 0.0f, AtLeast0(view_width_ - content_rect.width)); - offset_y_ = Coerce(offset_y, 0.0f, AtLeast0(view_height_ - content_rect.height)); - UpdateScrollBarBarInfo(); - - if (update_children) - { - if (const auto child = GetChild()) - { - const auto old_position = child->GetOffset(); - child->SetRect(Rect(Point( - old_position.x + old_offset_x - offset_x_, - old_position.y + old_offset_y - offset_y_ - ), child->GetSize())); - child->RefreshDescendantPositionCache(); - } - } - InvalidateDraw(); - } - - void ScrollControl::UpdateScrollBarVisibility() - { - const auto content_rect = GetRect(RectRange::Content); - if (GetHorizontalScrollBarVisibility() == ScrollBarVisibility::Auto) - is_horizontal_scroll_bar_visible_ = view_width_ > content_rect.width; - if (GetVerticalScrollBarVisibility() == ScrollBarVisibility::Auto) - is_vertical_scroll_bar_visible_ = view_height_ > content_rect.height; - } - - void ScrollControl::UpdateScrollBarBorderInfo() - { - const auto content_rect = GetRect(RectRange::Content); - horizontal_bar_info_.border = Rect(content_rect.left, content_rect.GetBottom() - scroll_bar_width, content_rect.width, scroll_bar_width); - vertical_bar_info_.border = Rect(content_rect.GetRight() - scroll_bar_width , content_rect.top, scroll_bar_width, content_rect.height); - } - - void ScrollControl::UpdateScrollBarBarInfo() - { - const auto content_rect = GetRect(RectRange::Content); - { - const auto& border = horizontal_bar_info_.border; - if (view_width_ <= content_rect.width) - horizontal_bar_info_.bar = border; - else - { - const auto bar_length = border.width * content_rect.width / view_width_; - const auto offset = border.width * offset_x_ / view_width_; - horizontal_bar_info_.bar = Rect(border.left + offset, border.top, bar_length, border.height); - } - } - { - const auto& border = vertical_bar_info_.border; - if (view_height_ <= content_rect.height) - vertical_bar_info_.bar = border; - else - { - const auto bar_length = border.height * content_rect.height / view_height_; - const auto offset = border.height * offset_y_ / view_height_; - vertical_bar_info_.bar = Rect(border.left, border.top + offset, border.width, bar_length); - } - } - } -} diff --git a/src/ui/controls/scroll_control.hpp b/src/ui/controls/scroll_control.hpp deleted file mode 100644 index 84ebca30..00000000 --- a/src/ui/controls/scroll_control.hpp +++ /dev/null @@ -1,152 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include -#include - -#include "ui/control.hpp" - -namespace cru::ui::controls -{ - // Done: OnMeasureContent - // Done: OnLayoutContent - // Done: HitTest(no need) - // Done: Draw(no need) - // Done: API - // Done: ScrollBar - // Done: MouseEvent - class ScrollControl : public ContentControl - { - private: - struct ScrollBarInfo - { - Rect border = Rect(); - Rect bar = Rect(); - }; - - enum class Orientation - { - Horizontal, - Vertical - }; - - public: - enum class ScrollBarVisibility - { - None, - Auto, - Always - }; - - static ScrollControl* Create(Control* child = nullptr) - { - const auto control = new ScrollControl(true); - control->SetChild(child); - return control; - } - - static constexpr auto control_type = L"ScrollControl"; - - protected: - explicit ScrollControl(bool container); - public: - ScrollControl(const ScrollControl& other) = delete; - ScrollControl(ScrollControl&& other) = delete; - ScrollControl& operator=(const ScrollControl& other) = delete; - ScrollControl& operator=(ScrollControl&& other) = delete; - ~ScrollControl() override; - - StringView GetControlType() const override final; - - bool IsHorizontalScrollEnabled() const - { - return horizontal_scroll_enabled_; - } - - void SetHorizontalScrollEnabled(bool enable); - - bool IsVerticalScrollEnabled() const - { - return vertical_scroll_enabled_; - } - - void SetVerticalScrollEnabled(bool enable); - - - ScrollBarVisibility GetHorizontalScrollBarVisibility() const - { - return horizontal_scroll_bar_visibility_; - } - - void SetHorizontalScrollBarVisibility(ScrollBarVisibility visibility); - - ScrollBarVisibility GetVerticalScrollBarVisibility() const - { - return vertical_scroll_bar_visibility_; - } - - void SetVerticalScrollBarVisibility(ScrollBarVisibility visibility); - - float GetViewWidth() const - { - return view_width_; - } - - float GetViewHeight() const - { - return view_height_; - } - - float GetScrollOffsetX() const - { - return offset_x_; - } - - float GetScrollOffsetY() const - { - return offset_y_; - } - - // nullopt for not set. value is auto-coerced. - void SetScrollOffset(std::optional x, std::optional y); - - protected: - void SetViewWidth(float length); - void SetViewHeight(float length); - - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override final; - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override final; - - void OnRectChange(const Rect& old_rect, const Rect& new_rect) override; - - private: - void CoerceAndSetOffsets(float offset_x, float offset_y, bool update_children = true); - void UpdateScrollBarVisibility(); - void UpdateScrollBarBorderInfo(); - void UpdateScrollBarBarInfo(); - - private: - bool horizontal_scroll_enabled_ = true; - bool vertical_scroll_enabled_ = true; - - ScrollBarVisibility horizontal_scroll_bar_visibility_ = ScrollBarVisibility::Auto; - ScrollBarVisibility vertical_scroll_bar_visibility_ = ScrollBarVisibility::Auto; - - bool is_horizontal_scroll_bar_visible_ = false; - bool is_vertical_scroll_bar_visible_ = false; - - float offset_x_ = 0.0f; - float offset_y_ = 0.0f; - - float view_width_ = 0.0f; - float view_height_ = 0.0f; - - ScrollBarInfo horizontal_bar_info_; - ScrollBarInfo vertical_bar_info_; - - std::optional is_pressing_scroll_bar_ = std::nullopt; - float pressing_delta_ = 0.0f; - }; -} diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp index 8276ce4e..123dbc86 100644 --- a/src/ui/controls/text_block.cpp +++ b/src/ui/controls/text_block.cpp @@ -1,19 +1,23 @@ #include "text_block.hpp" +#include "ui/render/text_render_object.hpp" #include "ui/ui_manager.hpp" -namespace cru::ui::controls -{ - TextBlock::TextBlock() : TextControl( - UiManager::GetInstance()->GetPredefineResources()->text_block_text_format, - UiManager::GetInstance()->GetPredefineResources()->text_block_text_brush - ) - { +namespace cru::ui::controls { +using render::TextRenderObject; - } - - StringView TextBlock::GetControlType() const - { - return control_type; - } +TextBlock::TextBlock() { + const auto predefined_resources = + UiManager::GetInstance()->GetPredefineResources(); + render_object_ = + new TextRenderObject(predefined_resources->text_block_text_brush, + predefined_resources->text_block_text_format, + predefined_resources->text_block_selection_brush); } + +TextBlock::~TextBlock() { delete render_object_; } + +String TextBlock::GetText() const { return render_object_->GetText(); } + +void TextBlock::SetText(const String& text) { render_object_->SetText(text); } +} // namespace cru::ui::controls diff --git a/src/ui/controls/text_block.hpp b/src/ui/controls/text_block.hpp index 66f5defa..ce8977a5 100644 --- a/src/ui/controls/text_block.hpp +++ b/src/ui/controls/text_block.hpp @@ -1,35 +1,35 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "text_control.hpp" - -namespace cru::ui::controls -{ - class TextBlock : public TextControl - { - public: - static constexpr auto control_type = L"TextBlock"; - - static TextBlock* Create(const String& text = L"") - { - const auto text_block = new TextBlock(); - text_block->SetText(text); - return text_block; - } - - protected: - TextBlock(); - public: - TextBlock(const TextBlock& other) = delete; - TextBlock(TextBlock&& other) = delete; - TextBlock& operator=(const TextBlock& other) = delete; - TextBlock& operator=(TextBlock&& other) = delete; - ~TextBlock() override = default; - - StringView GetControlType() const override final; - - using TextControl::SetSelectable; // Make this public. - }; +#include "ui/control.hpp" + +namespace cru::ui::render { +class TextRenderObject; } + +namespace cru::ui::controls { +class TextBlock : public NoChildControl { + public: + static constexpr auto control_type = L"TextBlock"; + + public: + TextBlock(); + TextBlock(const TextBlock& other) = delete; + TextBlock(TextBlock&& other) = delete; + TextBlock& operator=(const TextBlock& other) = delete; + TextBlock& operator=(TextBlock&& other) = delete; + ~TextBlock() override; + + StringView GetControlType() const override final { return control_type; } + + render::RenderObject* GetRenderObject() const override { + return render_object_; + } + + String GetText() const; + void SetText(const String& text); + + private: + render::TextRenderObject* render_object_; +}; +} // namespace cru::ui::controls diff --git a/src/ui/controls/text_box.cpp b/src/ui/controls/text_box.cpp deleted file mode 100644 index 893d6e8d..00000000 --- a/src/ui/controls/text_box.cpp +++ /dev/null @@ -1,198 +0,0 @@ -#include "text_box.hpp" - -#include -#include - -#include "graph/graph.hpp" -#include "exception.hpp" -#include "ui/ui_manager.hpp" - -namespace cru::ui::controls -{ - TextBox::TextBox() : TextControl( - UiManager::GetInstance()->GetPredefineResources()->text_box_text_format, - UiManager::GetInstance()->GetPredefineResources()->text_box_text_brush - ) - { - SetSelectable(true); - - caret_brush_ = UiManager::GetInstance()->GetPredefineResources()->text_box_caret_brush; - - GetBorderProperty() = UiManager::GetInstance()->GetPredefineResources()->text_box_border; - SetBordered(true); - - draw_content_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - if (is_caret_show_) - { - const auto caret_half_width = UiManager::GetInstance()->GetCaretInfo().half_caret_width; - FLOAT x, y; - DWRITE_HIT_TEST_METRICS metrics{}; - ThrowIfFailed(text_layout_->HitTestTextPosition(caret_position_, FALSE, &x, &y, &metrics)); - device_context->FillRectangle(D2D1::RectF(metrics.left - caret_half_width, metrics.top, metrics.left + caret_half_width, metrics.top + metrics.height), caret_brush_.Get()); - } - }); - - get_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args) - { - assert(!caret_timer_.has_value()); - is_caret_show_ = true; - caret_timer_ = SetInterval(UiManager::GetInstance()->GetCaretInfo().caret_blink_duration, [this] - { - is_caret_show_ = !is_caret_show_; - InvalidateDraw(); - }); - }); - - lose_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args) - { - assert(caret_timer_.has_value()); - caret_timer_->Cancel(); - caret_timer_ = std::nullopt; - is_caret_show_ = false; - }); - - key_down_event.bubble.AddHandler([this](events::KeyEventArgs& args) - { - if (args.GetVirtualCode() == VK_LEFT && caret_position_ > 0) - { - if (IsKeyDown(VK_SHIFT)) - { - if (GetCaretSelectionSide()) - ShiftLeftSelectionRange(-1); - else - ShiftRightSelectionRange(-1); - } - else - { - const auto selection = GetSelectedRange(); - if (selection.has_value()) - { - ClearSelection(); - caret_position_ = selection.value().position; - } - else - caret_position_--; - } - InvalidateDraw(); - } - - if (args.GetVirtualCode() == VK_RIGHT && caret_position_ < GetText().size()) - { - if (IsKeyDown(VK_SHIFT)) - { - if (GetCaretSelectionSide()) - ShiftLeftSelectionRange(1); - else - ShiftRightSelectionRange(1); - } - else - { - const auto selection = GetSelectedRange(); - if (selection.has_value()) - { - ClearSelection(); - caret_position_ = selection.value().position + selection.value().count; - } - else - caret_position_++; - } - } - }); - - char_event.bubble.AddHandler([this](events::CharEventArgs& args) - { - if (args.GetChar() == L'\b') - { - if (GetSelectedRange().has_value()) - { - const auto selection_range = GetSelectedRange().value(); - auto text = GetText(); - text.erase(text.cbegin() + selection_range.position, text.cbegin() + selection_range.position + selection_range.count); - SetText(text); - caret_position_ = selection_range.position; - ClearSelection(); - } - else - { - if (caret_position_ > 0) - { - auto text = GetText(); - if (!text.empty()) - { - const auto position = --caret_position_; - text.erase(text.cbegin() + position); - SetText(text); - } - } - } - return; - } - - if (std::iswprint(args.GetChar())) - { - if (GetSelectedRange().has_value()) - { - const auto selection_range = GetSelectedRange().value(); - auto text = GetText(); - text.erase(selection_range.position, selection_range.count); - text.insert(text.cbegin() + selection_range.position, args.GetChar()); - SetText(text); - caret_position_ = selection_range.position + 1; - ClearSelection(); - } - else - { - ClearSelection(); - const auto position = caret_position_++; - auto text = GetText(); - text.insert(text.cbegin() + position, { args.GetChar() }); - SetText(text); - } - } - }); - } - - TextBox::~TextBox() = default; - - StringView TextBox::GetControlType() const - { - return control_type; - } - - void TextBox::RequestChangeCaretPosition(const unsigned position) - { - caret_position_ = position; - InvalidateDraw(); - } - - bool TextBox::GetCaretSelectionSide() const - { - const auto selection = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); - if (selection.first == caret_position_) - return true; - if (selection.second == caret_position_) - return false; - assert(false); - return true; - } - - void TextBox::ShiftLeftSelectionRange(const int count) - { - const auto selection_range_side = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); - int new_left = selection_range_side.first + count; - new_left = new_left < 0 ? 0 : new_left; // at least 0 - caret_position_ = new_left; - SetSelectedRange(TextRange::FromTwoSides(static_cast(new_left), selection_range_side.second)); - } - - void TextBox::ShiftRightSelectionRange(const int count) - { - const auto selection_range_side = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); - int new_right = selection_range_side.second + count; - new_right = new_right < 0 ? 0 : new_right; // at least 0 - caret_position_ = new_right; - SetSelectedRange(TextRange::FromTwoSides(selection_range_side.first, static_cast(new_right))); - } -} diff --git a/src/ui/controls/text_box.hpp b/src/ui/controls/text_box.hpp deleted file mode 100644 index e5cd7545..00000000 --- a/src/ui/controls/text_box.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "text_control.hpp" -#include "timer.hpp" - -namespace cru::ui::controls -{ - class TextBox : public TextControl - { - public: - static constexpr auto control_type = L"TextBox"; - - static TextBox* Create() - { - return new TextBox(); - } - - protected: - TextBox(); - public: - TextBox(const TextBox& other) = delete; - TextBox(TextBox&& other) = delete; - TextBox& operator=(const TextBox& other) = delete; - TextBox& operator=(TextBox&& other) = delete; - ~TextBox() override; - - StringView GetControlType() const override final; - - protected: - void RequestChangeCaretPosition(unsigned position) override final; - - private: - // return true if left - bool GetCaretSelectionSide() const; - void ShiftLeftSelectionRange(int count); - void ShiftRightSelectionRange(int count); - - private: - unsigned caret_position_ = 0; - std::optional caret_timer_{}; - Microsoft::WRL::ComPtr caret_brush_; - bool is_caret_show_ = false; - }; -} diff --git a/src/ui/controls/text_control.cpp b/src/ui/controls/text_control.cpp deleted file mode 100644 index fcfcb90c..00000000 --- a/src/ui/controls/text_control.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "text_control.hpp" - -#include - -#include "ui/window.hpp" -#include "graph/graph.hpp" -#include "exception.hpp" -#include "ui/ui_manager.hpp" - -namespace cru::ui::controls -{ - namespace - { - unsigned TextLayoutHitTest(IDWriteTextLayout* text_layout, const Point& point) - { - BOOL is_trailing, is_inside; - DWRITE_HIT_TEST_METRICS metrics{}; - text_layout->HitTestPoint(point.x, point.y, &is_trailing, &is_inside, &metrics); - return is_trailing == 0 ? metrics.textPosition : metrics.textPosition + 1; - } - - - } - - TextControl::TextControl(const Microsoft::WRL::ComPtr& init_text_format, - const Microsoft::WRL::ComPtr& init_brush) - { - text_format_ = init_text_format; - - RecreateTextLayout(); - - brush_ = init_brush; - - selection_brush_ = UiManager::GetInstance()->GetPredefineResources()->text_control_selection_brush; - - SetClipContent(true); - - draw_content_event.AddHandler([this](events::DrawEventArgs& args) - { - }); - - mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (is_selectable_ && args.GetMouseButton() == MouseButton::Left && GetRect(RectRange::Padding).IsPointInside(args.GetPoint(this, RectRange::Margin))) - { - selected_range_ = std::nullopt; - const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this)); - RequestChangeCaretPosition(hit_test_result); - mouse_down_position_ = hit_test_result; - is_selecting_ = true; - GetWindow()->CaptureMouseFor(this); - InvalidateDraw(); - } - }); - - mouse_move_event.bubble.AddHandler([this](events::MouseEventArgs& args) - { - if (is_selecting_) - { - const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this)); - RequestChangeCaretPosition(hit_test_result); - selected_range_ = TextRange::FromTwoSides(hit_test_result, mouse_down_position_); - InvalidateDraw(); - } - UpdateCursor(args.GetPoint(this, RectRange::Margin)); - }); - - - mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - { - if (is_selecting_) - { - is_selecting_ = false; - GetWindow()->ReleaseCurrentMouseCapture(); - } - } - }); - - lose_focus_event.direct.AddHandler([this](events::FocusChangeEventArgs& args) - { - if (is_selecting_) - { - is_selecting_ = false; - GetWindow()->ReleaseCurrentMouseCapture(); - } - if (!args.IsWindow()) // If the focus lose is triggered window-wide, then save the selection state. Otherwise, clear selection. - { - selected_range_ = std::nullopt; - InvalidateDraw(); - } - }); - } - - - void TextControl::SetText(const String& text) - { - if (text_ != text) - { - const auto old_text = text_; - text_ = text; - OnTextChangedCore(old_text, text); - } - } - - void TextControl::SetBrush(const Microsoft::WRL::ComPtr& brush) - { - brush_ = brush; - InvalidateDraw(); - } - - void TextControl::SetTextFormat(const Microsoft::WRL::ComPtr& text_format) - { - text_format_ = text_format; - RecreateTextLayout(); - InvalidateDraw(); - } - - void TextControl::SetSelectable(const bool is_selectable) - { - if (is_selectable_ != is_selectable) - { - if (!is_selectable) - { - if (is_selecting_) - { - is_selecting_ = false; - GetWindow()->ReleaseCurrentMouseCapture(); - } - selected_range_ = std::nullopt; - InvalidateDraw(); - } - is_selectable_ = is_selectable; - UpdateCursor(std::nullopt); - } - } - - void TextControl::SetSelectedRange(std::optional text_range) - { - if (is_selectable_) - { - selected_range_ = text_range; - InvalidateDraw(); - } - } - - - - void TextControl::RequestChangeCaretPosition(unsigned position) - { - - } - - void TextControl::OnRectChange(const Rect& old_rect, const Rect& new_rect) - { - const auto content = GetRect(RectRange::Content); - ThrowIfFailed(text_layout_->SetMaxWidth(content.width)); - ThrowIfFailed(text_layout_->SetMaxHeight(content.height)); - } - - void TextControl::OnTextChangedCore(const String& old_text, const String& new_text) - { - RecreateTextLayout(); - InvalidateLayout(); - InvalidateDraw(); - } - - void TextControl::UpdateCursor(const std::optional& point) - { - if (!is_selectable_) - { - SetCursor(nullptr); - return; - } - - const auto window = GetWindow(); - if (window == nullptr) - { - SetCursor(nullptr); - return; - } - - if (is_selecting_) - { - SetCursor(cursors::i_beam); - return; - } - - const auto p = point.value_or(WindowToControl(window->GetMousePosition())); - if (GetRect(RectRange::Padding).IsPointInside(p)) - SetCursor(cursors::i_beam); - else - SetCursor(nullptr); - } -} diff --git a/src/ui/controls/text_control.hpp b/src/ui/controls/text_control.hpp deleted file mode 100644 index 83d4753f..00000000 --- a/src/ui/controls/text_control.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "ui/control.hpp" - -namespace cru::ui::controls -{ - class TextControl : public NoChildControl - { - protected: - TextControl( - const Microsoft::WRL::ComPtr& init_text_format, - const Microsoft::WRL::ComPtr& init_brush - ); - public: - TextControl(const TextControl& other) = delete; - TextControl(TextControl&& other) = delete; - TextControl& operator=(const TextControl& other) = delete; - TextControl& operator=(TextControl&& other) = delete; - ~TextControl() override = default; - - String GetText() const - { - return text_; - } - - void SetText(const String& text); - - Microsoft::WRL::ComPtr GetBrush() const - { - return brush_; - } - - void SetBrush(const Microsoft::WRL::ComPtr& brush); - - Microsoft::WRL::ComPtr GetTextFormat() const - { - return text_format_; - } - - void SetTextFormat(const Microsoft::WRL::ComPtr& text_format); - - bool IsSelectable() const - { - return is_selectable_; - } - - std::optional GetSelectedRange() const - { - return selected_range_; - } - - void SetSelectedRange(std::optional text_range); - - void ClearSelection() - { - SetSelectedRange(std::nullopt); - } - - protected: - void SetSelectable(bool is_selectable); - - - virtual void RequestChangeCaretPosition(unsigned position); - - void OnRectChange(const Rect& old_rect, const Rect& new_rect) override; - - private: - void OnTextChangedCore(const String& old_text, const String& new_text); - - void RecreateTextLayout(); - - // param point is the mouse point relative to this control. - void UpdateCursor(const std::optional& point); - - private: - String text_; - - Microsoft::WRL::ComPtr brush_; - Microsoft::WRL::ComPtr selection_brush_; - Microsoft::WRL::ComPtr text_format_; - Microsoft::WRL::ComPtr text_layout_; - - bool is_selectable_ = false; - std::optional selected_range_ = std::nullopt; - - bool is_selecting_ = false; - unsigned mouse_down_position_ = 0; - }; -} diff --git a/src/ui/controls/toggle_button.cpp b/src/ui/controls/toggle_button.cpp deleted file mode 100644 index db72d7bb..00000000 --- a/src/ui/controls/toggle_button.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "toggle_button.hpp" - -#include "graph/graph.hpp" -#include "ui/animations/animation.hpp" -#include "ui/ui_manager.hpp" -#include "ui/d2d_util.hpp" - -namespace cru::ui::controls -{ - using animations::AnimationBuilder; - - // ui length parameters of toggle button. - constexpr float half_height = 15; - constexpr float half_width = half_height * 2; - constexpr float stroke_width = 3; - constexpr float inner_circle_radius = half_height - stroke_width; - constexpr float inner_circle_x = half_width - half_height; - - ToggleButton::ToggleButton() : current_circle_position_(-inner_circle_x) - { - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(D2D1::RoundedRect(D2D1::RectF(-half_width, -half_height, half_width, half_height), half_height, half_height), &frame_path_); - - on_brush_ = UiManager::GetInstance()->GetPredefineResources()->toggle_button_on_brush; - off_brush_ = UiManager::GetInstance()->GetPredefineResources()->toggle_button_off_brush; - - draw_content_event.AddHandler([this](events::DrawEventArgs& args) - { - const auto device_context = args.GetDeviceContext(); - const auto size = GetSize(); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2), [this](ID2D1DeviceContext* device_context) - { - if (state_) - { - device_context->DrawGeometry(frame_path_.Get(), on_brush_.Get(), stroke_width); - device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), on_brush_.Get()); - } - else - { - device_context->DrawGeometry(frame_path_.Get(), off_brush_.Get(), stroke_width); - device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), off_brush_.Get()); - } - }); - }); - - mouse_click_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetMouseButton() == MouseButton::Left) - Toggle(); - }); - } - - - StringView ToggleButton::GetControlType() const - { - return control_type; - } - - bool ToggleButton::IsPointInside(const Point& point) - { - const auto size = GetSize(); - const auto transform = D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2); - BOOL contains; - frame_path_->FillContainsPoint(Convert(point), transform, &contains); - if (!contains) - frame_path_->StrokeContainsPoint(Convert(point), stroke_width, nullptr, transform, &contains); - return contains != 0; - } - - void ToggleButton::SetState(const bool state) - { - if (state != state_) - { - state_ = state; - float destination_x; - - if (state) - destination_x = inner_circle_x; - else - destination_x = -inner_circle_x; - - const auto previous_position = current_circle_position_; - const auto delta = destination_x - current_circle_position_; - - constexpr auto total_time = FloatSecond(0.2); - - const auto time = total_time * (std::abs(delta) / (inner_circle_x * 2)); - - // ReSharper disable once CppExpressionWithoutSideEffects - AnimationBuilder(Format(L"ToggleButton {}", reinterpret_cast(this)), time) - .AddStepHandler([=](auto, const double percentage) - { - current_circle_position_ = static_cast(previous_position + delta * percentage); - InvalidateDraw(); - }) - .Start(); - - events::ToggleEventArgs args(this, this, state); - toggle_event.Raise(args); - InvalidateDraw(); - } - } - - void ToggleButton::Toggle() - { - SetState(!GetState()); - } - - Size ToggleButton::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo&) - { - const Size result_size( - half_width * 2 + stroke_width, - half_height * 2 + stroke_width - ); - - return result_size; - } -} diff --git a/src/ui/controls/toggle_button.hpp b/src/ui/controls/toggle_button.hpp deleted file mode 100644 index dee655d4..00000000 --- a/src/ui/controls/toggle_button.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "ui/control.hpp" - -namespace cru::ui::controls -{ - class ToggleButton : public NoChildControl - { - public: - static constexpr auto control_type = L"ToggleButton"; - - static ToggleButton* Create() - { - return new ToggleButton(); - } - - protected: - ToggleButton(); - - public: - ToggleButton(const ToggleButton& other) = delete; - ToggleButton(ToggleButton&& other) = delete; - ToggleButton& operator=(const ToggleButton& other) = delete; - ToggleButton& operator=(ToggleButton&& other) = delete; - ~ToggleButton() override = default; - - StringView GetControlType() const override final; - - bool IsPointInside(const Point& point) override; - - bool GetState() const - { - return state_; - } - - void SetState(bool state); - - void Toggle(); - - Event toggle_event; - - protected: - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo&) override; - - private: - bool state_ = false; - - float current_circle_position_; - - Microsoft::WRL::ComPtr frame_path_; - Microsoft::WRL::ComPtr on_brush_; - Microsoft::WRL::ComPtr off_brush_; - }; -} diff --git a/src/ui/render/text_render_object.hpp b/src/ui/render/text_render_object.hpp index b868796d..d1d91034 100644 --- a/src/ui/render/text_render_object.hpp +++ b/src/ui/render/text_render_object.hpp @@ -18,7 +18,10 @@ class TextRenderObject : public RenderObject { ~TextRenderObject() override = default; String GetText() const { return text_; } - void SetText(String new_text) { text_ = std::move(new_text); } + void SetText(String new_text) { + text_ = std::move(new_text); + RecreateTextLayout(); + } Microsoft::WRL::ComPtr GetBrush() const { return brush_; } void SetBrush(Microsoft::WRL::ComPtr new_brush) { @@ -31,6 +34,7 @@ class TextRenderObject : public RenderObject { void SetTextFormat( Microsoft::WRL::ComPtr new_text_format) { text_format_ = std::move(new_text_format); + RecreateTextLayout(); } std::optional GetSelectionRange() const { diff --git a/src/ui/ui_manager.cpp b/src/ui/ui_manager.cpp index 689a04a2..bcda4133 100644 --- a/src/ui/ui_manager.cpp +++ b/src/ui/ui_manager.cpp @@ -1,107 +1,66 @@ #include "ui_manager.hpp" #include "application.hpp" -#include "border_property.hpp" -#include "graph/graph.hpp" #include "exception.hpp" +#include "graph/graph.hpp" +namespace cru::ui { +namespace { +void GetSystemCaretInfo(CaretInfo* caret_info) { + caret_info->caret_blink_duration = + std::chrono::milliseconds(::GetCaretBlinkTime()); + DWORD caret_width; + if (!::SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caret_width, 0)) + throw Win32Error(::GetLastError(), "Failed to get system caret width."); + caret_info->half_caret_width = caret_width / 2.0f; +} -namespace cru::ui -{ - namespace - { - void GetSystemCaretInfo(CaretInfo* caret_info) - { - caret_info->caret_blink_duration = std::chrono::milliseconds(::GetCaretBlinkTime()); - DWORD caret_width; - if (!::SystemParametersInfoW(SPI_GETCARETWIDTH, 0 , &caret_width, 0)) - throw Win32Error(::GetLastError(), "Failed to get system caret width."); - caret_info->half_caret_width = caret_width / 2.0f; - } - - Microsoft::WRL::ComPtr CreateSolidBrush(graph::GraphManager* graph_manager, const D2D1_COLOR_F& color) - { - const auto device_context = graph_manager->GetD2D1DeviceContext(); - Microsoft::WRL::ComPtr solid_color_brush; - device_context->CreateSolidColorBrush(color, &solid_color_brush); - return solid_color_brush; - } - - Microsoft::WRL::ComPtr CreateDefaultTextFormat(graph::GraphManager* graph_manager) - { - const auto dwrite_factory = graph_manager->GetDWriteFactory(); - - Microsoft::WRL::ComPtr text_format; - - ThrowIfFailed(dwrite_factory->CreateTextFormat( - L"等线", nullptr, - DWRITE_FONT_WEIGHT_NORMAL, - DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, - 24.0, L"zh-cn", - &text_format - )); - - ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); - ThrowIfFailed(text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); - - return text_format; - } - } - - - //!!! never use default constructor of border at here, because it will recursively call this method! - PredefineResources::PredefineResources(graph::GraphManager* graph_manager) : - border_property_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - - button_normal_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::RoyalBlue)), 2, 6, 6}, - button_press_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Blue)), 2, 6, 6}, - - text_control_selection_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightSkyBlue))}, - - text_box_border {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_box_text_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_box_text_format {CreateDefaultTextFormat(graph_manager)}, - text_box_caret_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - - text_block_text_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_block_text_format {CreateDefaultTextFormat(graph_manager)}, +Microsoft::WRL::ComPtr CreateSolidBrush( + graph::GraphManager* graph_manager, const D2D1_COLOR_F& color) { + const auto device_context = graph_manager->GetD2D1DeviceContext(); + Microsoft::WRL::ComPtr solid_color_brush; + device_context->CreateSolidColorBrush(color, &solid_color_brush); + return solid_color_brush; +} - toggle_button_on_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DeepSkyBlue))}, - toggle_button_off_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightGray))}, +Microsoft::WRL::ComPtr CreateDefaultTextFormat( + graph::GraphManager* graph_manager) { + const auto dwrite_factory = graph_manager->GetDWriteFactory(); - list_item_normal_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::White, 0))}, - list_item_normal_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::White, 0))}, - list_item_hover_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue))}, - list_item_hover_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))}, - list_item_select_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::MediumBlue))}, - list_item_select_fill_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.3f))}, + Microsoft::WRL::ComPtr text_format; - scroll_bar_background_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Gainsboro, 0.3f))}, - scroll_bar_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DimGray))}, - scroll_bar_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::DimGray))} + ThrowIfFailed(dwrite_factory->CreateTextFormat( + L"等线", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, 24.0, L"zh-cn", &text_format)); -#ifdef CRU_DEBUG_LAYOUT - , - debug_layout_out_border_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Crimson))}, - debug_layout_margin_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f))}, - debug_layout_padding_brush {CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f))} -#endif - { - - } + ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); + ThrowIfFailed( + text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); - UiManager* UiManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton([](auto) - { - return new UiManager{}; - }); - } + return text_format; +} +} // namespace + +PredefineResources::PredefineResources(graph::GraphManager* graph_manager) + : text_block_selection_brush{CreateSolidBrush( + graph_manager, D2D1::ColorF(D2D1::ColorF::LightSkyBlue))}, + text_block_text_brush{ + CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, + text_block_text_format{CreateDefaultTextFormat(graph_manager)}, + debug_layout_out_border_brush{ + CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Crimson))}, + debug_layout_margin_brush{CreateSolidBrush( + graph_manager, D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f))}, + debug_layout_padding_brush{CreateSolidBrush( + graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f))} {} + +UiManager* UiManager::GetInstance() { + return Application::GetInstance()->ResolveSingleton( + [](auto) { return new UiManager{}; }); +} - UiManager::UiManager() - : predefine_resources_(graph::GraphManager::GetInstance()) - { - GetSystemCaretInfo(&caret_info_); - } +UiManager::UiManager() + : predefine_resources_(graph::GraphManager::GetInstance()) { + GetSystemCaretInfo(&caret_info_); } +} // namespace cru::ui diff --git a/src/ui/ui_manager.hpp b/src/ui/ui_manager.hpp index f0e1e8ce..3fd2adc9 100644 --- a/src/ui/ui_manager.hpp +++ b/src/ui/ui_manager.hpp @@ -1,108 +1,63 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" #include "system_headers.hpp" #include "base.hpp" -#include "border_property.hpp" -namespace cru::graph -{ - class GraphManager; +namespace cru::graph { +class GraphManager; } -namespace cru::ui -{ - struct CaretInfo - { - std::chrono::milliseconds caret_blink_duration; - float half_caret_width; - }; - - - class PredefineResources : public Object - { - public: - explicit PredefineResources(graph::GraphManager* graph_manager); - PredefineResources(const PredefineResources& other) = delete; - PredefineResources(PredefineResources&& other) = delete; - PredefineResources& operator=(const PredefineResources& other) = delete; - PredefineResources& operator=(PredefineResources&& other) = delete; - ~PredefineResources() override = default; - - //region BorderProperty - Microsoft::WRL::ComPtr border_property_brush; - - //region Button - BorderProperty button_normal_border; - BorderProperty button_press_border; - - //region TextControl - Microsoft::WRL::ComPtr text_control_selection_brush; - - //region TextBox - BorderProperty text_box_border; - Microsoft::WRL::ComPtr text_box_text_brush; - Microsoft::WRL::ComPtr text_box_text_format; - Microsoft::WRL::ComPtr text_box_caret_brush; - - //region TextBlock - Microsoft::WRL::ComPtr text_block_text_brush; - Microsoft::WRL::ComPtr text_block_text_format; - - //region ToggleButton - Microsoft::WRL::ComPtr toggle_button_on_brush; - Microsoft::WRL::ComPtr toggle_button_off_brush; - - //region ListItem - Microsoft::WRL::ComPtr list_item_normal_border_brush; - Microsoft::WRL::ComPtr list_item_normal_fill_brush; - Microsoft::WRL::ComPtr list_item_hover_border_brush; - Microsoft::WRL::ComPtr list_item_hover_fill_brush; - Microsoft::WRL::ComPtr list_item_select_border_brush; - Microsoft::WRL::ComPtr list_item_select_fill_brush; - - //region ScrollControl - Microsoft::WRL::ComPtr scroll_bar_background_brush; - Microsoft::WRL::ComPtr scroll_bar_border_brush; - Microsoft::WRL::ComPtr scroll_bar_brush; - -#ifdef CRU_DEBUG_LAYOUT - //region debug - Microsoft::WRL::ComPtr debug_layout_out_border_brush; - Microsoft::WRL::ComPtr debug_layout_margin_brush; - Microsoft::WRL::ComPtr debug_layout_padding_brush; -#endif - }; - - class UiManager : public Object - { - public: - static UiManager* GetInstance(); - private: - UiManager(); - public: - UiManager(const UiManager& other) = delete; - UiManager(UiManager&& other) = delete; - UiManager& operator=(const UiManager& other) = delete; - UiManager& operator=(UiManager&& other) = delete; - ~UiManager() override = default; - - CaretInfo GetCaretInfo() const - { - return caret_info_; - } - - const PredefineResources* GetPredefineResources() const - { - return &predefine_resources_; - } - - private: - CaretInfo caret_info_; - - PredefineResources predefine_resources_; - }; -} +namespace cru::ui { +struct CaretInfo { + std::chrono::milliseconds caret_blink_duration; + float half_caret_width; +}; + +class PredefineResources : public Object { + public: + explicit PredefineResources(graph::GraphManager* graph_manager); + PredefineResources(const PredefineResources& other) = delete; + PredefineResources(PredefineResources&& other) = delete; + PredefineResources& operator=(const PredefineResources& other) = delete; + PredefineResources& operator=(PredefineResources&& other) = delete; + ~PredefineResources() override = default; + + // region TextBlock + Microsoft::WRL::ComPtr text_block_selection_brush; + Microsoft::WRL::ComPtr text_block_text_brush; + Microsoft::WRL::ComPtr text_block_text_format; + + // region debug + Microsoft::WRL::ComPtr debug_layout_out_border_brush; + Microsoft::WRL::ComPtr debug_layout_margin_brush; + Microsoft::WRL::ComPtr debug_layout_padding_brush; +}; + +class UiManager : public Object { + public: + static UiManager* GetInstance(); + + private: + UiManager(); + + public: + UiManager(const UiManager& other) = delete; + UiManager(UiManager&& other) = delete; + UiManager& operator=(const UiManager& other) = delete; + UiManager& operator=(UiManager&& other) = delete; + ~UiManager() override = default; + + CaretInfo GetCaretInfo() const { return caret_info_; } + + const PredefineResources* GetPredefineResources() const { + return &predefine_resources_; + } + + private: + CaretInfo caret_info_; + + PredefineResources predefine_resources_; +}; +} // namespace cru::ui -- cgit v1.2.3 From 962dc18ee4827b464764ec3708be3d00a9143971 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 21 Mar 2019 22:22:02 +0800 Subject: ... --- CruUI.vcxproj | 6 ++ CruUI.vcxproj.filters | 33 +++++----- src/base.hpp | 8 --- src/cru_debug.cpp | 2 + src/cru_debug.hpp | 4 ++ src/ui/content_control.cpp | 32 ++++++++++ src/ui/content_control.hpp | 31 +++++++++ src/ui/control.cpp | 110 +------------------------------- src/ui/control.hpp | 136 +--------------------------------------- src/ui/controls/flex_layout.hpp | 4 +- src/ui/controls/text_block.hpp | 2 +- src/ui/layout_control.cpp | 52 +++++++++++++++ src/ui/layout_control.hpp | 33 ++++++++++ src/ui/no_child_control.cpp | 5 ++ src/ui/no_child_control.hpp | 26 ++++++++ src/ui/window.cpp | 86 +++++++++++++++++++++++++ src/ui/window.hpp | 9 +-- src/util/format.hpp | 42 +++++++------ 18 files changed, 327 insertions(+), 294 deletions(-) create mode 100644 src/ui/content_control.cpp create mode 100644 src/ui/content_control.hpp create mode 100644 src/ui/layout_control.cpp create mode 100644 src/ui/layout_control.hpp create mode 100644 src/ui/no_child_control.cpp create mode 100644 src/ui/no_child_control.hpp (limited to 'src/ui/control.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 2e1359f9..d307222b 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -117,6 +117,9 @@ + + + @@ -131,6 +134,9 @@ + + + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 3bd5d30a..55b19e92 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -81,6 +81,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -170,6 +179,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -199,24 +217,9 @@ Source Files - - Source Files - - - Source Files - - - Source Files - Source Files - - Source Files - - - Source Files - diff --git a/src/base.hpp b/src/base.hpp index a62a745b..2c511c4b 100644 --- a/src/base.hpp +++ b/src/base.hpp @@ -7,9 +7,6 @@ #include namespace cru { -template -struct type_tag {}; - // typedefs using String = std::wstring; using MultiByteString = std::string; @@ -38,9 +35,4 @@ struct Interface { [[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 b9226132..331d2bce 100644 --- a/src/cru_debug.cpp +++ b/src/cru_debug.cpp @@ -3,7 +3,9 @@ #include "system_headers.hpp" namespace cru::debug { +#ifdef CRU_DEBUG void DebugMessage(const StringView& message) { ::OutputDebugStringW(message.data()); } +#endif } // namespace cru::debug diff --git a/src/cru_debug.hpp b/src/cru_debug.hpp index 9c22a24f..58431d56 100644 --- a/src/cru_debug.hpp +++ b/src/cru_debug.hpp @@ -7,7 +7,11 @@ #include "util/format.hpp" namespace cru::debug { +#ifdef CRU_DEBUG void DebugMessage(const StringView& message); +#else +inline void DebugMessage(const StringView& message) {} +#endif #ifdef CRU_DEBUG inline void DebugTime(const std::function& action, diff --git a/src/ui/content_control.cpp b/src/ui/content_control.cpp new file mode 100644 index 00000000..960867b2 --- /dev/null +++ b/src/ui/content_control.cpp @@ -0,0 +1,32 @@ +#include "content_control.hpp" + +#include "window.hpp" + +namespace cru::ui { +ContentControl::ContentControl() + : child_vector_{nullptr}, child_(child_vector_[0]) {} + +ContentControl::~ContentControl() { delete child_; } + +void ContentControl::SetChild(Control* child) { + if (dynamic_cast(child)) + throw std::invalid_argument("Can't add a window as child."); + + if (child == child_) return; + + const auto window = GetWindow(); + const auto old_child = child_; + child_ = child; + if (old_child) { + old_child->_SetParent(nullptr); + old_child->_SetDescendantWindow(nullptr); + } + if (child) { + child->_SetParent(this); + child->_SetDescendantWindow(window); + } + OnChildChanged(old_child, child); +} + +void ContentControl::OnChildChanged(Control* old_child, Control* new_child) {} +} // namespace cru::ui diff --git a/src/ui/content_control.hpp b/src/ui/content_control.hpp new file mode 100644 index 00000000..88e7f60f --- /dev/null +++ b/src/ui/content_control.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "pre.hpp" + +#include "control.hpp" + +namespace cru::ui { +class ContentControl : public Control { + protected: + ContentControl(); + + public: + ContentControl(const ContentControl& other) = delete; + ContentControl(ContentControl&& other) = delete; + ContentControl& operator=(const ContentControl& other) = delete; + ContentControl& operator=(ContentControl&& other) = delete; + ~ContentControl() override; + + const std::vector& GetChildren() const override final { + return child_vector_; + } + Control* GetChild() const { return child_; } + void SetChild(Control* child); + + protected: + virtual void OnChildChanged(Control* old_child, Control* new_child); + + private: + std::vector child_vector_; + Control*& child_; +}; +} // namespace cru::ui diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 98986d3c..e19754dc 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -35,14 +35,14 @@ void Control::_SetDescendantWindow(Window* window) { void Control::TraverseDescendants( const std::function& predicate) { - TraverseDescendantsInternal(this, predicate); + _TraverseDescendants(this, predicate); } -void Control::TraverseDescendantsInternal( +void Control::_TraverseDescendants( Control* control, const std::function& predicate) { predicate(control); for (auto c : control->GetChildren()) - TraverseDescendantsInternal(c, predicate); + _TraverseDescendants(c, predicate); } bool Control::RequestFocus() { auto window = GetWindow(); @@ -76,108 +76,4 @@ void Control::OnDetachToWindow(Window* window) {} void Control::OnMouseClickBegin(MouseButton button) {} void Control::OnMouseClickEnd(MouseButton button) {} - -const std::vector NoChildControl::empty_control_vector{}; - -ContentControl::ContentControl() - : child_vector_{nullptr}, child_(child_vector_[0]) {} - -ContentControl::~ContentControl() { delete child_; } - -void ContentControl::SetChild(Control* child) { - if (child == child_) return; - - const auto window = GetWindow(); - const auto old_child = child_; - child_ = child; - if (old_child) { - old_child->_SetParent(nullptr); - old_child->_SetDescendantWindow(nullptr); - } - if (child) { - child->_SetParent(this); - child->_SetDescendantWindow(window); - } - OnChildChanged(old_child, child); -} - -void ContentControl::OnChildChanged(Control* old_child, Control* new_child) {} - -void ControlAddChildCheck(Control* control) { - if (control->GetParent() != nullptr) - throw std::invalid_argument("The control already has a parent."); - - if (dynamic_cast(control)) - throw std::invalid_argument("Can't add a window as child."); -} - -Layout::~Layout() { - for (const auto child : children_) delete child; -} - -void Layout::AddChild(Control* control, const int position) { - ControlAddChildCheck(control); - - if (position < 0 || static_cast(position) > - this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - children_.insert(this->children_.cbegin() + position, control); - - control->_SetParent(this); - control->_SetDescendantWindow(GetWindow()); - - OnAddChild(control, position); -} - -void Layout::RemoveChild(const int position) { - if (position < 0 || static_castchildren_.size())>(position) >= - this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - const auto i = children_.cbegin() + position; - const auto child = *i; - - children_.erase(i); - - child->_SetParent(nullptr); - child->_SetDescendantWindow(nullptr); - - OnRemoveChild(child, position); -} - -void Layout::OnAddChild(Control* child, int position) {} - -void Layout::OnRemoveChild(Control* child, int position) {} - -std::list GetAncestorList(Control* control) { - std::list l; - while (control != nullptr) { - l.push_front(control); - control = control->GetParent(); - } - return l; -} - -Control* FindLowestCommonAncestor(Control* left, Control* right) { - if (left == nullptr || right == nullptr) return nullptr; - - auto&& left_list = GetAncestorList(left); - auto&& right_list = GetAncestorList(right); - - // the root is different - if (left_list.front() != right_list.front()) return nullptr; - - // find the last same control or the last control (one is ancestor of the - // other) - auto left_i = left_list.cbegin(); - auto right_i = right_list.cbegin(); - while (true) { - if (left_i == left_list.cend()) return *(--left_i); - if (right_i == right_list.cend()) return *(--right_i); - if (*left_i != *right_i) return *(--left_i); - ++left_i; - ++right_i; - } -} } // namespace cru::ui diff --git a/src/ui/control.hpp b/src/ui/control.hpp index 9ff407b0..8454e981 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -48,7 +48,7 @@ class Control : public Object { void _SetDescendantWindow(Window* window); private: - static void TraverseDescendantsInternal( + static void _TraverseDescendants( Control* control, const std::function& predicate); public: @@ -110,138 +110,4 @@ class Control : public Object { Cursor::Ptr cursor_{}; }; - -class NoChildControl : public Control { - private: - static const std::vector empty_control_vector; - - protected: - NoChildControl() = default; - - public: - NoChildControl(const NoChildControl& other) = delete; - NoChildControl(NoChildControl&& other) = delete; - NoChildControl& operator=(const NoChildControl& other) = delete; - NoChildControl& operator=(NoChildControl&& other) = delete; - ~NoChildControl() override = default; - - protected: - const std::vector& GetChildren() const override final { - return empty_control_vector; - } -}; - -class ContentControl : public Control { - protected: - ContentControl(); - - public: - ContentControl(const ContentControl& other) = delete; - ContentControl(ContentControl&& other) = delete; - ContentControl& operator=(const ContentControl& other) = delete; - ContentControl& operator=(ContentControl&& other) = delete; - ~ContentControl() override; - - const std::vector& GetChildren() const override final { - return child_vector_; - } - Control* GetChild() const { return child_; } - void SetChild(Control* child); - - protected: - virtual void OnChildChanged(Control* old_child, Control* new_child); - - private: - std::vector child_vector_; - Control*& child_; -}; - -class Layout : public Control { - protected: - Layout() = default; - - public: - Layout(const Layout& other) = delete; - Layout(Layout&& other) = delete; - Layout& operator=(const Layout& other) = delete; - Layout& operator=(Layout&& other) = delete; - ~Layout() override; - - const std::vector& GetChildren() const override final { - return children_; - } - - void AddChild(Control* control, int position); - - void RemoveChild(int position); - - protected: - virtual void OnAddChild(Control* child, int position); - virtual void OnRemoveChild(Control* child, int position); - - private: - std::vector children_; -}; - -//*************** region: event dispatcher helper *************** - -// Dispatch the event. -// -// This will raise routed event of the control and its parent and parent's -// parent ... (until "last_receiver" if it's not nullptr) with appropriate args. -// -// First tunnel from top to bottom possibly stopped by "handled" flag in -// EventArgs. Second bubble from bottom to top possibly stopped by "handled" -// flag in EventArgs. Last direct to each control. -// -// Args is of type "EventArgs". The first init argument is "sender", which is -// automatically bound to each receiving control. The second init argument is -// "original_sender", which is unchanged. And "args" will be perfectly forwarded -// as the rest arguments. -template -void DispatchEvent(Control* const original_sender, - events::RoutedEvent Control::*event_ptr, - Control* const last_receiver, Args&&... args) { - std::list receive_list; - - auto parent = original_sender; - while (parent != last_receiver) { - receive_list.push_back(parent); - parent = parent->GetParent(); - } - - auto handled = false; - - // tunnel - for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) { - EventArgs event_args(*i, original_sender, std::forward(args)...); - (*i->*event_ptr).tunnel.Raise(event_args); - if (event_args.IsHandled()) { - handled = true; - break; - } - } - - // bubble - if (!handled) { - for (auto i : receive_list) { - EventArgs event_args(i, original_sender, std::forward(args)...); - (i->*event_ptr).bubble.Raise(event_args); - if (event_args.IsHandled()) break; - } - } - - // direct - for (auto i : receive_list) { - EventArgs event_args(i, original_sender, std::forward(args)...); - (i->*event_ptr).direct.Raise(event_args); - } -} - -//*************** region: tree helper *************** - -// Find the lowest common ancestor. -// Return nullptr if "left" and "right" are not in the same tree. -Control* FindLowestCommonAncestor(Control* left, Control* right); - } // namespace cru::ui diff --git a/src/ui/controls/flex_layout.hpp b/src/ui/controls/flex_layout.hpp index 682ed8dc..2ab3e259 100644 --- a/src/ui/controls/flex_layout.hpp +++ b/src/ui/controls/flex_layout.hpp @@ -1,7 +1,7 @@ #pragma once #include "pre.hpp" -#include "ui/control.hpp" +#include "ui/layout_control.hpp" namespace cru::ui::render { class FlexLayoutRenderObject; @@ -9,7 +9,7 @@ class FlexLayoutRenderObject; namespace cru::ui::controls { -class FlexLayout : public Layout { +class FlexLayout : public LayoutControl { public: static constexpr auto control_type = L"FlexLayout"; diff --git a/src/ui/controls/text_block.hpp b/src/ui/controls/text_block.hpp index c345c5ab..4c443020 100644 --- a/src/ui/controls/text_block.hpp +++ b/src/ui/controls/text_block.hpp @@ -1,7 +1,7 @@ #pragma once #include "pre.hpp" -#include "ui/control.hpp" +#include "ui/no_child_control.hpp" namespace cru::ui::render { class TextRenderObject; diff --git a/src/ui/layout_control.cpp b/src/ui/layout_control.cpp new file mode 100644 index 00000000..d2b430dd --- /dev/null +++ b/src/ui/layout_control.cpp @@ -0,0 +1,52 @@ +#include "layout_control.hpp" + +#include "window.hpp" + +namespace cru::ui { +void ControlAddChildCheck(Control* control) { + if (control->GetParent() != nullptr) + throw std::invalid_argument("The control already has a parent."); + + if (dynamic_cast(control)) + throw std::invalid_argument("Can't add a window as child."); +} + +LayoutControl::~LayoutControl() { + for (const auto child : children_) delete child; +} + +void LayoutControl::AddChild(Control* control, const int position) { + ControlAddChildCheck(control); + + if (position < 0 || static_cast(position) > + this->children_.size()) + throw std::invalid_argument("The position is out of range."); + + children_.insert(this->children_.cbegin() + position, control); + + control->_SetParent(this); + control->_SetDescendantWindow(GetWindow()); + + OnAddChild(control, position); +} + +void LayoutControl::RemoveChild(const int position) { + if (position < 0 || static_castchildren_.size())>(position) >= + this->children_.size()) + throw std::invalid_argument("The position is out of range."); + + const auto i = children_.cbegin() + position; + const auto child = *i; + + children_.erase(i); + + child->_SetParent(nullptr); + child->_SetDescendantWindow(nullptr); + + OnRemoveChild(child, position); +} + +void LayoutControl::OnAddChild(Control* child, int position) {} + +void LayoutControl::OnRemoveChild(Control* child, int position) {} +} // namespace cru::ui diff --git a/src/ui/layout_control.hpp b/src/ui/layout_control.hpp new file mode 100644 index 00000000..53f53186 --- /dev/null +++ b/src/ui/layout_control.hpp @@ -0,0 +1,33 @@ +#pragma once +#include "pre.hpp" + +#include "control.hpp" + +namespace cru::ui { +class LayoutControl : public Control { + protected: + LayoutControl() = default; + + public: + LayoutControl(const LayoutControl& other) = delete; + LayoutControl(LayoutControl&& other) = delete; + LayoutControl& operator=(const LayoutControl& other) = delete; + LayoutControl& operator=(LayoutControl&& other) = delete; + ~LayoutControl() override; + + const std::vector& GetChildren() const override final { + return children_; + } + + void AddChild(Control* control, int position); + + void RemoveChild(int position); + + protected: + virtual void OnAddChild(Control* child, int position); + virtual void OnRemoveChild(Control* child, int position); + + private: + std::vector children_; +}; +} // namespace cru::ui diff --git a/src/ui/no_child_control.cpp b/src/ui/no_child_control.cpp new file mode 100644 index 00000000..e6bbe813 --- /dev/null +++ b/src/ui/no_child_control.cpp @@ -0,0 +1,5 @@ +#include "no_child_control.hpp" + +namespace cru::ui { +const std::vector NoChildControl::empty_control_vector{}; +} diff --git a/src/ui/no_child_control.hpp b/src/ui/no_child_control.hpp new file mode 100644 index 00000000..26b5546f --- /dev/null +++ b/src/ui/no_child_control.hpp @@ -0,0 +1,26 @@ +#pragma once +#include "pre.hpp" + +#include "control.hpp" + +namespace cru::ui { +class NoChildControl : public Control { + private: + static const std::vector empty_control_vector; + + protected: + NoChildControl() = default; + + public: + NoChildControl(const NoChildControl& other) = delete; + NoChildControl(NoChildControl&& other) = delete; + NoChildControl& operator=(const NoChildControl& other) = delete; + NoChildControl& operator=(NoChildControl&& other) = delete; + ~NoChildControl() override = default; + + protected: + const std::vector& GetChildren() const override final { + return empty_control_vector; + } +}; +} // namespace cru::ui diff --git a/src/ui/window.cpp b/src/ui/window.cpp index f92811a7..b976ca6a 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -7,6 +7,92 @@ #include "render/window_render_object.hpp" namespace cru::ui { +namespace { +// Dispatch the event. +// +// This will raise routed event of the control and its parent and parent's +// parent ... (until "last_receiver" if it's not nullptr) with appropriate args. +// +// First tunnel from top to bottom possibly stopped by "handled" flag in +// EventArgs. Second bubble from bottom to top possibly stopped by "handled" +// flag in EventArgs. Last direct to each control. +// +// Args is of type "EventArgs". The first init argument is "sender", which is +// automatically bound to each receiving control. The second init argument is +// "original_sender", which is unchanged. And "args" will be perfectly forwarded +// as the rest arguments. +template +void DispatchEvent(Control* const original_sender, + events::RoutedEvent Control::*event_ptr, + Control* const last_receiver, Args&&... args) { + std::list receive_list; + + auto parent = original_sender; + while (parent != last_receiver) { + receive_list.push_back(parent); + parent = parent->GetParent(); + } + + auto handled = false; + + // tunnel + for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) { + EventArgs event_args(*i, original_sender, std::forward(args)...); + (*i->*event_ptr).tunnel.Raise(event_args); + if (event_args.IsHandled()) { + handled = true; + break; + } + } + + // bubble + if (!handled) { + for (auto i : receive_list) { + EventArgs event_args(i, original_sender, std::forward(args)...); + (i->*event_ptr).bubble.Raise(event_args); + if (event_args.IsHandled()) break; + } + } + + // direct + for (auto i : receive_list) { + EventArgs event_args(i, original_sender, std::forward(args)...); + (i->*event_ptr).direct.Raise(event_args); + } +} + +std::list GetAncestorList(Control* control) { + std::list l; + while (control != nullptr) { + l.push_front(control); + control = control->GetParent(); + } + return l; +} + +Control* FindLowestCommonAncestor(Control* left, Control* right) { + if (left == nullptr || right == nullptr) return nullptr; + + auto&& left_list = GetAncestorList(left); + auto&& right_list = GetAncestorList(right); + + // the root is different + if (left_list.front() != right_list.front()) return nullptr; + + // find the last same control or the last control (one is ancestor of the + // other) + auto left_i = left_list.cbegin(); + auto right_i = right_list.cbegin(); + while (true) { + if (left_i == left_list.cend()) return *(--left_i); + if (right_i == right_list.cend()) return *(--right_i); + if (*left_i != *right_i) return *(--left_i); + ++left_i; + ++right_i; + } +} +} // namespace + 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 7a28c257..3e0422b1 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -5,7 +5,7 @@ #include #include "system_headers.hpp" -#include "control.hpp" +#include "content_control.hpp" #include "events/ui_event.hpp" #include "window_class.hpp" @@ -230,13 +230,6 @@ class Window final : public ContentControl { bool window_focus_ = false; Control* focus_control_ = this; // "focus_control_" can't be nullptr - Control* mouse_capture_control_ = nullptr; - - bool is_layout_invalid_ = false; - -#ifdef CRU_DEBUG_LAYOUT - bool debug_layout_ = false; -#endif }; } // namespace cru::ui diff --git a/src/util/format.hpp b/src/util/format.hpp index 874c5b43..7c1cee05 100644 --- a/src/util/format.hpp +++ b/src/util/format.hpp @@ -5,26 +5,29 @@ namespace cru::util { namespace details { -constexpr StringView PlaceHolder(type_tag) { return StringView(L"{}"); } +template +struct TypeTag {}; -constexpr MultiByteStringView PlaceHolder(type_tag) { +constexpr StringView PlaceHolder(TypeTag) { return StringView(L"{}"); } + +constexpr MultiByteStringView PlaceHolder(TypeTag) { return MultiByteStringView("{}"); } template void FormatInternal(TString& string) { - const auto find_result = string.find(PlaceHolder(type_tag{})); + const auto find_result = string.find(PlaceHolder(TypeTag{})); if (find_result != TString::npos) throw std::invalid_argument("There is more placeholders than args."); } template void FormatInternal(TString& string, const T& arg, const TRest&... args) { - const auto find_result = string.find(PlaceHolder(type_tag{})); + const auto find_result = string.find(PlaceHolder(TypeTag{})); if (find_result == TString::npos) throw std::invalid_argument("There is less placeholders than args."); - string.replace(find_result, 2, FormatToString(arg, type_tag{})); + string.replace(find_result, 2, FormatToString(arg, TypeTag{})); FormatInternal(string, args...); } } // namespace details @@ -43,13 +46,13 @@ MultiByteString Format(const MultiByteStringView& format, const T&... args) { return result; } -#define CRU_FORMAT_NUMBER(type) \ - inline String FormatToString(const type number, type_tag) { \ - return std::to_wstring(number); \ - } \ - inline MultiByteString FormatToString(const type number, \ - type_tag) { \ - return std::to_string(number); \ +#define CRU_FORMAT_NUMBER(type) \ + inline String FormatToString(const type number, details::TypeTag) { \ + return std::to_wstring(number); \ + } \ + inline MultiByteString FormatToString(const type number, \ + details::TypeTag) { \ + return std::to_string(number); \ } CRU_FORMAT_NUMBER(int) @@ -65,30 +68,33 @@ CRU_FORMAT_NUMBER(double) #undef CRU_FORMAT_NUMBER -inline StringView FormatToString(const String& string, type_tag) { +inline StringView FormatToString(const String& string, + details::TypeTag) { return string; } inline MultiByteString FormatToString(const MultiByteString& string, - type_tag) { + details::TypeTag) { return string; } -inline StringView FormatToString(const StringView& string, type_tag) { +inline StringView FormatToString(const StringView& string, + details::TypeTag) { return string; } inline MultiByteStringView FormatToString(const MultiByteStringView& string, - type_tag) { + details::TypeTag) { return string; } -inline StringView FormatToString(const wchar_t* string, type_tag) { +inline StringView FormatToString(const wchar_t* string, + details::TypeTag) { return StringView(string); } inline MultiByteStringView FormatToString(const char* string, - type_tag) { + details::TypeTag) { return MultiByteString(string); } } // namespace cru::util -- cgit v1.2.3 From afdac77a66143375b5bebd4ff128b0fba87c5d21 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 21 Mar 2019 22:44:31 +0800 Subject: Change all error handling. --- src/base.hpp | 1 + src/ui/content_control.cpp | 4 +--- src/ui/control.cpp | 3 --- src/ui/layout_control.cpp | 21 +++++---------------- src/ui/render/render_object.cpp | 21 ++++++--------------- src/ui/render/text_render_object.cpp | 2 -- src/ui/render/window_render_object.cpp | 2 -- src/ui/window.cpp | 25 ++++++++++--------------- src/ui/window_class.cpp | 5 ++++- 9 files changed, 27 insertions(+), 57 deletions(-) (limited to 'src/ui/control.cpp') diff --git a/src/base.hpp b/src/base.hpp index 2c511c4b..e3dfc1ee 100644 --- a/src/base.hpp +++ b/src/base.hpp @@ -1,6 +1,7 @@ #pragma once #include "pre.hpp" +#include #include #include #include diff --git a/src/ui/content_control.cpp b/src/ui/content_control.cpp index 960867b2..d5abca1c 100644 --- a/src/ui/content_control.cpp +++ b/src/ui/content_control.cpp @@ -9,9 +9,7 @@ ContentControl::ContentControl() ContentControl::~ContentControl() { delete child_; } void ContentControl::SetChild(Control* child) { - if (dynamic_cast(child)) - throw std::invalid_argument("Can't add a window as child."); - + assert(!dynamic_cast(child)); // Can't add a window as child. if (child == child_) return; const auto window = GetWindow(); diff --git a/src/ui/control.cpp b/src/ui/control.cpp index e19754dc..5c629fd6 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -1,11 +1,8 @@ #include "control.hpp" -#include - #include "window.hpp" namespace cru::ui { - void Control::_SetParent(Control* parent) { const auto old_parent = GetParent(); parent_ = parent; diff --git a/src/ui/layout_control.cpp b/src/ui/layout_control.cpp index d2b430dd..c0c4a7fe 100644 --- a/src/ui/layout_control.cpp +++ b/src/ui/layout_control.cpp @@ -3,24 +3,14 @@ #include "window.hpp" namespace cru::ui { -void ControlAddChildCheck(Control* control) { - if (control->GetParent() != nullptr) - throw std::invalid_argument("The control already has a parent."); - - if (dynamic_cast(control)) - throw std::invalid_argument("Can't add a window as child."); -} - LayoutControl::~LayoutControl() { for (const auto child : children_) delete child; } void LayoutControl::AddChild(Control* control, const int position) { - ControlAddChildCheck(control); - - if (position < 0 || static_cast(position) > - this->children_.size()) - throw std::invalid_argument("The position is out of range."); + assert(control->GetParent() == nullptr); // The control already has a parent. + assert(!dynamic_cast(control)); // Can't add a window as child. + assert(position >= 0 || position <= this->children_.size()); // The position is out of range. children_.insert(this->children_.cbegin() + position, control); @@ -31,9 +21,8 @@ void LayoutControl::AddChild(Control* control, const int position) { } void LayoutControl::RemoveChild(const int position) { - if (position < 0 || static_castchildren_.size())>(position) >= - this->children_.size()) - throw std::invalid_argument("The position is out of range."); + assert(position >= 0 && + position < this->children_.size()); // The position is out of range. const auto i = children_.cbegin() + position; const auto child = *i; diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 0be44c1e..6380c2fe 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -4,15 +4,10 @@ namespace cru::ui::render { 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::size_type>(position) > - children_.size()) - throw std::invalid_argument("Position index is out of bound."); + assert(render_object->GetParent() == + nullptr); // Render object already has a parent. + assert(position >= 0); // Position index is less than 0. + assert(position <= children_.size()); // Position index is out of bound. children_.insert(children_.cbegin() + position, render_object); render_object->SetParent(this); @@ -20,12 +15,8 @@ void RenderObject::AddChild(RenderObject* render_object, const int position) { } void RenderObject::RemoveChild(const int position) { - if (position < 0) - throw std::invalid_argument("Position index is less than 0."); - - if (static_cast::size_type>(position) >= - children_.size()) - throw std::invalid_argument("Position index is out of bound."); + assert(position >= 0); // Position index is less than 0. + assert(position < children_.size()); // Position index is out of bound. const auto i = children_.cbegin() + position; const auto removed_child = *i; diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp index e8032f78..b90dae71 100644 --- a/src/ui/render/text_render_object.cpp +++ b/src/ui/render/text_render_object.cpp @@ -1,7 +1,5 @@ #include "text_render_object.hpp" -#include - #include "exception.hpp" #include "graph/graph.hpp" diff --git a/src/ui/render/window_render_object.cpp b/src/ui/render/window_render_object.cpp index 52452bd4..f198c2fa 100644 --- a/src/ui/render/window_render_object.cpp +++ b/src/ui/render/window_render_object.cpp @@ -1,7 +1,5 @@ #include "window_render_object.hpp" -#include - #include "graph/graph.hpp" #include "ui/window.hpp" diff --git a/src/ui/window.cpp b/src/ui/window.cpp index b976ca6a..ca3356ff 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -117,19 +117,14 @@ WindowManager::WindowManager() { } void WindowManager::RegisterWindow(HWND hwnd, Window* window) { - const auto find_result = window_map_.find(hwnd); - if (find_result != window_map_.end()) - throw std::runtime_error("The hwnd is already in the map."); - + assert(window_map_.count(hwnd) == 0); // The hwnd is already in the map. window_map_.emplace(hwnd, window); } void WindowManager::UnregisterWindow(HWND hwnd) { const auto find_result = window_map_.find(hwnd); - if (find_result == window_map_.end()) - throw std::runtime_error("The hwnd is not in the map."); + assert(find_result != window_map_.end()); // The hwnd is not in the map. window_map_.erase(find_result); - if (window_map_.empty()) Application::GetInstance()->Quit(0); } @@ -188,14 +183,15 @@ Window::Window(tag_overlapped_constructor) { CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr); - if (hwnd_ == nullptr) throw std::runtime_error("Failed to create window."); + if (hwnd_ == nullptr) + throw Win32Error(::GetLastError(), "Failed to create window."); AfterCreateHwnd(window_manager); } Window::Window(tag_popup_constructor, Window* parent, const bool caption) { - if (parent != nullptr && !parent->IsWindowValid()) - throw std::runtime_error("Parent window is not valid."); + assert(parent == nullptr || + parent->IsWindowValid()); // Parent window is not valid. BeforeCreateHwnd(); @@ -210,7 +206,8 @@ Window::Window(tag_popup_constructor, Window* parent, const bool caption) { parent == nullptr ? nullptr : parent->GetWindowHandle(), nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr); - if (hwnd_ == nullptr) throw std::runtime_error("Failed to create window."); + if (hwnd_ == nullptr) + throw Win32Error(::GetLastError(), "Failed to create window."); AfterCreateHwnd(window_manager); } @@ -474,10 +471,8 @@ Point Window::GetMousePosition() { } bool Window::RequestFocusFor(Control* control) { - if (control == nullptr) - throw std::invalid_argument( - "The control to request focus can't be null. You can set it as the " - "window."); + assert(control != nullptr); // The control to request focus can't be null. + // You can set it as the window. if (!IsWindowValid()) return false; diff --git a/src/ui/window_class.cpp b/src/ui/window_class.cpp index 456d9492..5e8b3454 100644 --- a/src/ui/window_class.cpp +++ b/src/ui/window_class.cpp @@ -1,5 +1,7 @@ #include "window_class.hpp" +#include "exception.hpp" + namespace cru::ui { WindowClass::WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance) @@ -20,6 +22,7 @@ WindowClass::WindowClass(const String& name, WNDPROC window_proc, window_class.hIconSm = NULL; atom_ = ::RegisterClassExW(&window_class); - if (atom_ == 0) throw std::runtime_error("Failed to create window class."); + if (atom_ == 0) + throw Win32Error(::GetLastError(), "Failed to create window class."); } } // namespace cru::ui -- cgit v1.2.3 From e8be3841457853daefc26d0ca00256ad8c44f593 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 23 Mar 2019 23:52:07 +0800 Subject: ... --- CruUI.vcxproj | 15 +++++++++--- CruUI.vcxproj.filters | 21 +++++++++-------- src/application.cpp | 5 ++-- src/application.hpp | 2 +- src/cru_debug.cpp | 2 +- src/exception.hpp | 2 +- src/graph/graph.hpp | 8 +++++-- src/system_headers.hpp | 20 ---------------- src/timer.hpp | 2 +- src/ui/control.cpp | 9 -------- src/ui/control.hpp | 14 ----------- src/ui/controls/button.cpp | 0 src/ui/controls/button.hpp | 8 +++++++ src/ui/controls/text_block.cpp | 2 ++ src/ui/cursor.cpp | 24 ------------------- src/ui/cursor.hpp | 37 ----------------------------- src/ui/d2d_util.hpp | 2 +- src/ui/events/ui_event.cpp | 6 +---- src/ui/events/ui_event.hpp | 45 +++++------------------------------- src/ui/events/window_event.cpp | 3 +++ src/ui/events/window_event.hpp | 42 +++++++++++++++++++++++++++++++++ src/ui/input_util.cpp | 2 +- src/ui/render/text_render_object.cpp | 2 ++ src/ui/ui_manager.cpp | 2 ++ src/ui/ui_manager.hpp | 3 ++- src/ui/window.cpp | 23 ++---------------- src/ui/window.hpp | 6 ++--- src/ui/window_class.hpp | 2 +- src/util/string_util.cpp | 5 ++-- 29 files changed, 114 insertions(+), 200 deletions(-) delete mode 100644 src/system_headers.hpp create mode 100644 src/ui/controls/button.cpp create mode 100644 src/ui/controls/button.hpp delete mode 100644 src/ui/cursor.cpp delete mode 100644 src/ui/cursor.hpp create mode 100644 src/ui/events/window_event.cpp create mode 100644 src/ui/events/window_event.hpp (limited to 'src/ui/control.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 374a882c..4143bb43 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -83,6 +83,7 @@ MachineX86 true Windows + D3D11.lib;D2d1.lib;DWrite.lib;%(AdditionalDependencies) @@ -100,6 +101,7 @@ Windows true true + D3D11.lib;D2d1.lib;DWrite.lib;%(AdditionalDependencies) @@ -108,6 +110,9 @@ stdcpplatest CRU_X64;%(PreprocessorDefinitions) + + D3D11.lib;D2d1.lib;DWrite.lib;%(AdditionalDependencies) + @@ -115,9 +120,14 @@ $(ProjectDir)\src;%(AdditionalIncludeDirectories) CRU_X64;%(PreprocessorDefinitions) + + D3D11.lib;D2d1.lib;DWrite.lib;%(AdditionalDependencies) + + + @@ -135,6 +145,8 @@ + + @@ -144,7 +156,6 @@ - @@ -161,14 +172,12 @@ - - diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 5554927b..01575ce2 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -42,9 +42,6 @@ Source Files - - Source Files - Source Files @@ -90,6 +87,12 @@ Source Files + + Source Files + + + Source Files + @@ -104,9 +107,6 @@ Header Files - - Header Files - Header Files @@ -128,9 +128,6 @@ Header Files - - Header Files - Header Files @@ -188,6 +185,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/src/application.cpp b/src/application.cpp index e580b56b..aafca6fe 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,8 +1,9 @@ #include "application.hpp" +#include + #include "exception.hpp" #include "timer.hpp" -#include "ui/cursor.hpp" #include "ui/window_class.hpp" namespace cru { @@ -79,8 +80,6 @@ Application::Application(HINSTANCE h_instance) : h_instance_(h_instance) { throw std::runtime_error("Must run on Windows 8 or later."); god_window_ = std::make_unique(this); - - ui::cursors::LoadSystemCursors(); } Application::~Application() { diff --git a/src/application.hpp b/src/application.hpp index acf264c3..f5f69ea4 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -1,12 +1,12 @@ #pragma once #include "pre.hpp" +#include #include #include #include #include #include -#include "system_headers.hpp" #include "base.hpp" diff --git a/src/cru_debug.cpp b/src/cru_debug.cpp index 331d2bce..81945227 100644 --- a/src/cru_debug.cpp +++ b/src/cru_debug.cpp @@ -1,6 +1,6 @@ #include "cru_debug.hpp" -#include "system_headers.hpp" +#include namespace cru::debug { #ifdef CRU_DEBUG diff --git a/src/exception.hpp b/src/exception.hpp index db2572f1..ade51d54 100644 --- a/src/exception.hpp +++ b/src/exception.hpp @@ -1,8 +1,8 @@ #pragma once #include "pre.hpp" +#include #include -#include "system_headers.hpp" #include "base.hpp" diff --git a/src/graph/graph.hpp b/src/graph/graph.hpp index bad5b6d0..af14cc50 100644 --- a/src/graph/graph.hpp +++ b/src/graph/graph.hpp @@ -1,9 +1,13 @@ #pragma once #include "pre.hpp" +#include +#include +#include +#include +#include #include #include -#include "system_headers.hpp" #include "base.hpp" @@ -101,7 +105,7 @@ class GraphManager final : public Object { Microsoft::WRL::ComPtr GetSystemFontCollection() const { - return dwrite_system_font_collection_.Get(); + return dwrite_system_font_collection_; } private: diff --git a/src/system_headers.hpp b/src/system_headers.hpp deleted file mode 100644 index 5517d71a..00000000 --- a/src/system_headers.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "pre.hpp" - -//include system headers -#include -#include - -#pragma comment(lib, "D3D11.lib") -#include - -#pragma comment(lib, "D2d1.lib") -#include - -#pragma comment(lib, "DWrite.lib") -#include - -#include -#include - -#include diff --git a/src/timer.hpp b/src/timer.hpp index 685e83b9..7199adc2 100644 --- a/src/timer.hpp +++ b/src/timer.hpp @@ -1,11 +1,11 @@ #pragma once #include "pre.hpp" +#include #include #include #include #include -#include "system_headers.hpp" #include "base.hpp" diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 5c629fd6..318d591a 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -55,15 +55,6 @@ bool Control::HasFocus() { return window->GetFocusControl() == this; } -void Control::SetCursor(const Cursor::Ptr& cursor) { - if (cursor != cursor_) { - cursor_ = cursor; - const auto window = GetWindow(); - if (window && window->GetMouseHoverControl() == this) - window->UpdateCursor(); - } -} - void Control::OnParentChanged(Control* old_parent, Control* new_parent) {} void Control::OnAttachToWindow(Window* window) {} diff --git a/src/ui/control.hpp b/src/ui/control.hpp index 8454e981..a44399bf 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -1,12 +1,8 @@ #pragma once #include "pre.hpp" -#include "system_headers.hpp" - #include "base.hpp" -#include "cursor.hpp" #include "events/ui_event.hpp" -#include "input_util.hpp" #include "ui_base.hpp" namespace cru::ui { @@ -60,14 +56,6 @@ class Control : public Object { bool HasFocus(); - //*************** 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_; } - - void SetCursor(const Cursor::Ptr& cursor); - //*************** region: events *************** public: // Raised when mouse enter the control. @@ -107,7 +95,5 @@ class Control : public Object { private: Window* window_ = nullptr; Control* parent_ = nullptr; - - Cursor::Ptr cursor_{}; }; } // namespace cru::ui diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/ui/controls/button.hpp b/src/ui/controls/button.hpp new file mode 100644 index 00000000..010c3f5b --- /dev/null +++ b/src/ui/controls/button.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "pre.hpp" + +#include "ui/control.hpp" + +namespace cru::ui::controls { + +} diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp index c891b832..85116910 100644 --- a/src/ui/controls/text_block.cpp +++ b/src/ui/controls/text_block.cpp @@ -1,5 +1,7 @@ #include "text_block.hpp" +#include + #include "ui/render/text_render_object.hpp" #include "ui/ui_manager.hpp" diff --git a/src/ui/cursor.cpp b/src/ui/cursor.cpp deleted file mode 100644 index d8c362ed..00000000 --- a/src/ui/cursor.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "cursor.hpp" - -#include "exception.hpp" - -namespace cru::ui { -Cursor::Cursor(HCURSOR handle, const bool auto_release) - : handle_(handle), auto_release_(auto_release) {} - -Cursor::~Cursor() { - if (auto_release_) ::DestroyCursor(handle_); -} - -namespace cursors { -Cursor::Ptr arrow{}; -Cursor::Ptr hand{}; -Cursor::Ptr i_beam{}; - -void LoadSystemCursors() { - arrow = std::make_shared(::LoadCursorW(nullptr, IDC_ARROW), false); - hand = std::make_shared(::LoadCursorW(nullptr, IDC_HAND), false); - i_beam = std::make_shared(::LoadCursorW(nullptr, IDC_IBEAM), false); -} -} // namespace cursors -} // namespace cru::ui diff --git a/src/ui/cursor.hpp b/src/ui/cursor.hpp deleted file mode 100644 index aec3fc40..00000000 --- a/src/ui/cursor.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include -#include "system_headers.hpp" - -#include "base.hpp" - -namespace cru::ui { -class Cursor : public Object { - public: - using Ptr = std::shared_ptr; - - Cursor(HCURSOR handle, bool auto_release); - Cursor(const Cursor& other) = delete; - Cursor(Cursor&& other) = delete; - Cursor& operator=(const Cursor& other) = delete; - Cursor& operator=(Cursor&& other) = delete; - ~Cursor() override; - - HCURSOR GetHandle() const { return handle_; } - - private: - HCURSOR handle_; - bool auto_release_; -}; - -namespace cursors { -extern Cursor::Ptr arrow; -extern Cursor::Ptr hand; -extern Cursor::Ptr i_beam; - -void LoadSystemCursors(); -} // namespace cursors -} // namespace cru::ui diff --git a/src/ui/d2d_util.hpp b/src/ui/d2d_util.hpp index 96a017dc..2ec8ba98 100644 --- a/src/ui/d2d_util.hpp +++ b/src/ui/d2d_util.hpp @@ -1,7 +1,7 @@ #pragma once #include "pre.hpp" -#include "system_headers.hpp" +#include #include "ui_base.hpp" diff --git a/src/ui/events/ui_event.cpp b/src/ui/events/ui_event.cpp index ee3a68dc..78d56a83 100644 --- a/src/ui/events/ui_event.cpp +++ b/src/ui/events/ui_event.cpp @@ -1,7 +1,3 @@ #include "ui_event.hpp" -#include "ui/control.hpp" - -namespace cru::ui::events -{ -} +namespace cru::ui::events {} diff --git a/src/ui/events/ui_event.hpp b/src/ui/events/ui_event.hpp index 572cf8d6..7fe4e6eb 100644 --- a/src/ui/events/ui_event.hpp +++ b/src/ui/events/ui_event.hpp @@ -2,13 +2,14 @@ #include "pre.hpp" #include -#include "system_headers.hpp" #include "base.hpp" #include "cru_event.hpp" #include "ui/ui_base.hpp" #include "ui/input_util.hpp" +struct ID2D1RenderTarget; + namespace cru::ui { class Control; } @@ -112,18 +113,18 @@ class MouseWheelEventArgs : public MouseEventArgs { class DrawEventArgs : public UiEventArgs { public: DrawEventArgs(Object* sender, Object* original_sender, - ID2D1DeviceContext* device_context) - : UiEventArgs(sender, original_sender), device_context_(device_context) {} + ID2D1RenderTarget* render_target) + : UiEventArgs(sender, original_sender), render_target_(render_target) {} DrawEventArgs(const DrawEventArgs& other) = default; DrawEventArgs(DrawEventArgs&& other) = default; DrawEventArgs& operator=(const DrawEventArgs& other) = default; DrawEventArgs& operator=(DrawEventArgs&& other) = default; ~DrawEventArgs() = default; - ID2D1DeviceContext* GetDeviceContext() const { return device_context_; } + ID2D1RenderTarget* GetRenderTarget() const { return render_target_; } private: - ID2D1DeviceContext* device_context_; + ID2D1RenderTarget* render_target_; }; class FocusChangeEventArgs : public UiEventArgs { @@ -160,40 +161,6 @@ class ToggleEventArgs : public UiEventArgs { bool new_state_; }; -struct WindowNativeMessage { - HWND hwnd; - int msg; - WPARAM w_param; - LPARAM l_param; -}; - -class WindowNativeMessageEventArgs : public UiEventArgs { - public: - WindowNativeMessageEventArgs(Object* sender, Object* original_sender, - const WindowNativeMessage& message) - : UiEventArgs(sender, original_sender), - message_(message), - result_(std::nullopt) {} - WindowNativeMessageEventArgs(const WindowNativeMessageEventArgs& other) = - default; - WindowNativeMessageEventArgs(WindowNativeMessageEventArgs&& other) = default; - WindowNativeMessageEventArgs& operator=( - const WindowNativeMessageEventArgs& other) = default; - WindowNativeMessageEventArgs& operator=( - WindowNativeMessageEventArgs&& other) = default; - ~WindowNativeMessageEventArgs() override = default; - - WindowNativeMessage GetWindowMessage() const { return message_; } - - std::optional GetResult() const { return result_; } - - void SetResult(const std::optional result) { result_ = result; } - - private: - WindowNativeMessage message_; - std::optional result_; -}; - class KeyEventArgs : public UiEventArgs { public: KeyEventArgs(Object* sender, Object* original_sender, int virtual_code) diff --git a/src/ui/events/window_event.cpp b/src/ui/events/window_event.cpp new file mode 100644 index 00000000..a217136c --- /dev/null +++ b/src/ui/events/window_event.cpp @@ -0,0 +1,3 @@ +#include "window_event.hpp" + +namespace cru::ui::events {} diff --git a/src/ui/events/window_event.hpp b/src/ui/events/window_event.hpp new file mode 100644 index 00000000..21c644af --- /dev/null +++ b/src/ui/events/window_event.hpp @@ -0,0 +1,42 @@ +#pragma once +#include "pre.hpp" + +#include + +#include "ui_event.hpp" + +namespace cru::ui::events { +struct WindowNativeMessage { + HWND hwnd; + int msg; + WPARAM w_param; + LPARAM l_param; +}; + +class WindowNativeMessageEventArgs : public UiEventArgs { + public: + WindowNativeMessageEventArgs(Object* sender, Object* original_sender, + const WindowNativeMessage& message) + : UiEventArgs(sender, original_sender), + message_(message), + result_(std::nullopt) {} + WindowNativeMessageEventArgs(const WindowNativeMessageEventArgs& other) = + default; + WindowNativeMessageEventArgs(WindowNativeMessageEventArgs&& other) = default; + WindowNativeMessageEventArgs& operator=( + const WindowNativeMessageEventArgs& other) = default; + WindowNativeMessageEventArgs& operator=( + WindowNativeMessageEventArgs&& other) = default; + ~WindowNativeMessageEventArgs() override = default; + + WindowNativeMessage GetWindowMessage() const { return message_; } + + std::optional GetResult() const { return result_; } + + void SetResult(const std::optional result) { result_ = result; } + + private: + WindowNativeMessage message_; + std::optional result_; +}; +} diff --git a/src/ui/input_util.cpp b/src/ui/input_util.cpp index 3fe34f10..193cba4a 100644 --- a/src/ui/input_util.cpp +++ b/src/ui/input_util.cpp @@ -1,6 +1,6 @@ #include "input_util.hpp" -#include "system_headers.hpp" +#include namespace cru::ui { bool IsKeyDown(const int virtual_code) { diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp index d57335ad..e8967d48 100644 --- a/src/ui/render/text_render_object.cpp +++ b/src/ui/render/text_render_object.cpp @@ -1,5 +1,7 @@ #include "text_render_object.hpp" +#include +#include #include #include "exception.hpp" diff --git a/src/ui/ui_manager.cpp b/src/ui/ui_manager.cpp index bcda4133..26b1fe62 100644 --- a/src/ui/ui_manager.cpp +++ b/src/ui/ui_manager.cpp @@ -1,5 +1,7 @@ #include "ui_manager.hpp" +#include + #include "application.hpp" #include "exception.hpp" #include "graph/graph.hpp" diff --git a/src/ui/ui_manager.hpp b/src/ui/ui_manager.hpp index 3fd2adc9..c2331dd4 100644 --- a/src/ui/ui_manager.hpp +++ b/src/ui/ui_manager.hpp @@ -1,7 +1,8 @@ #pragma once #include "pre.hpp" -#include "system_headers.hpp" +#include +#include #include "base.hpp" diff --git a/src/ui/window.cpp b/src/ui/window.cpp index ca3356ff..7b00ca05 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -1,7 +1,8 @@ #include "window.hpp" +#include + #include "application.hpp" -#include "cursor.hpp" #include "exception.hpp" #include "graph/graph.hpp" #include "render/window_render_object.hpp" @@ -153,17 +154,6 @@ inline POINT DipToPi(const Point& dip_point) { return result; } -namespace { -Cursor::Ptr GetCursorInherit(Control* control) { - while (control != nullptr) { - const auto cursor = control->GetCursor(); - if (cursor != nullptr) return cursor; - control = control->GetParent(); - } - return cursors::arrow; -} -} // namespace - Window* Window::CreateOverlapped() { return new Window(tag_overlapped_constructor{}); } @@ -220,8 +210,6 @@ void Window::AfterCreateHwnd(WindowManager* window_manager) { render_target_ = graph::GraphManager::GetInstance()->CreateWindowRenderTarget(hwnd_); - SetCursor(cursors::arrow); - render_object_ = new render::WindowRenderObject(this); } @@ -522,12 +510,6 @@ Control* Window::ReleaseCurrentMouseCapture() { } } -void Window::UpdateCursor() { - if (IsWindowValid() && mouse_hover_control_ != nullptr) { - SetCursorInternal(GetCursorInherit(mouse_hover_control_)->GetHandle()); - } -} - #ifdef CRU_DEBUG_LAYOUT void Window::SetDebugLayout(const bool value) { if (debug_layout_ != value) { @@ -719,7 +701,6 @@ void Window::DispatchMouseHoverControlChangeEvent(Control* old_control, DispatchEvent(new_control, &Control::mouse_enter_event, lowest_common_ancestor, point); // dispatch mouse enter event. - UpdateCursor(); } } } diff --git a/src/ui/window.hpp b/src/ui/window.hpp index 3e0422b1..1c48bf43 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -1,12 +1,13 @@ #pragma once #include "pre.hpp" +#include #include #include -#include "system_headers.hpp" #include "content_control.hpp" #include "events/ui_event.hpp" +#include "events/window_event.hpp" #include "window_class.hpp" namespace cru::graph { @@ -164,9 +165,6 @@ class Window final : public ContentControl { Control* CaptureMouseFor(Control* control); Control* ReleaseCurrentMouseCapture(); - //*************** region: cursor *************** - void UpdateCursor(); - public: //*************** region: events *************** Event activated_event; diff --git a/src/ui/window_class.hpp b/src/ui/window_class.hpp index 66babd94..72a7c431 100644 --- a/src/ui/window_class.hpp +++ b/src/ui/window_class.hpp @@ -1,7 +1,7 @@ #pragma once #include "pre.hpp" -#include "system_headers.hpp" +#include #include "base.hpp" diff --git a/src/util/string_util.cpp b/src/util/string_util.cpp index 3c765259..c9391fc6 100644 --- a/src/util/string_util.cpp +++ b/src/util/string_util.cpp @@ -1,6 +1,7 @@ #include "string_util.hpp" -#include "system_headers.hpp" +#include + #include "exception.hpp" namespace cru::util { @@ -18,4 +19,4 @@ MultiByteString ToUtf8String(const StringView& string) { "Failed to convert wide string to UTF-8."); return result; } -} +} // namespace cru::util -- cgit v1.2.3