diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.cpp | 239 | ||||
-rw-r--r-- | src/pre.hpp | 4 | ||||
-rw-r--r-- | src/ui/control.hpp | 2 | ||||
-rw-r--r-- | src/ui/controls/flex_layout.cpp | 4 | ||||
-rw-r--r-- | src/ui/controls/flex_layout.hpp | 10 | ||||
-rw-r--r-- | src/ui/controls/text_block.cpp | 4 | ||||
-rw-r--r-- | src/ui/controls/text_block.hpp | 10 | ||||
-rw-r--r-- | src/ui/d2d_util.hpp | 158 | ||||
-rw-r--r-- | src/ui/events/ui_event.cpp | 6 | ||||
-rw-r--r-- | src/ui/events/ui_event.hpp | 507 | ||||
-rw-r--r-- | src/ui/input_util.cpp | 29 | ||||
-rw-r--r-- | src/ui/input_util.hpp | 20 | ||||
-rw-r--r-- | src/ui/render/flex_layout_render_object.cpp | 2 | ||||
-rw-r--r-- | src/ui/render/render_object.cpp | 5 | ||||
-rw-r--r-- | src/ui/render/render_object.hpp | 8 | ||||
-rw-r--r-- | src/ui/render/text_render_object.cpp | 6 | ||||
-rw-r--r-- | src/ui/render/text_render_object.hpp | 2 | ||||
-rw-r--r-- | src/ui/render/window_render_object.cpp | 48 | ||||
-rw-r--r-- | src/ui/render/window_render_object.hpp | 40 | ||||
-rw-r--r-- | src/ui/window.cpp | 1194 | ||||
-rw-r--r-- | src/ui/window.hpp | 33 |
21 files changed, 1010 insertions, 1321 deletions
diff --git a/src/main.cpp b/src/main.cpp index 1773ce84..906528c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,242 +1,43 @@ #include "pre.hpp" #include "application.hpp" -#include "ui/window.hpp" -#include "ui/controls/linear_layout.hpp" +#include "ui/controls/flex_layout.hpp" #include "ui/controls/text_block.hpp" -#include "ui/controls/toggle_button.hpp" -#include "ui/controls/button.hpp" -#include "ui/controls/text_box.hpp" -#include "ui/controls/list_item.hpp" -#include "ui/controls/popup_menu.hpp" -#include "ui/controls/frame_layout.hpp" -#include "ui/controls/scroll_control.hpp" -#include "graph/graph.hpp" +#include "ui/window.hpp" +using cru::Application; using cru::String; using cru::StringView; -using cru::Application; using cru::ui::Rect; -using cru::ui::Window; -using cru::ui::Alignment; -using cru::ui::LayoutSideParams; using cru::ui::Thickness; -using cru::ui::ControlList; -using cru::ui::CreateWithLayout; -using cru::ui::controls::LinearLayout; +using cru::ui::Window; +using cru::ui::controls::FlexLayout; using cru::ui::controls::TextBlock; -using cru::ui::controls::ToggleButton; -using cru::ui::controls::Button; -using cru::ui::controls::TextBox; -using cru::ui::controls::ListItem; -using cru::ui::controls::FrameLayout; -using cru::ui::controls::ScrollControl; - -int APIENTRY wWinMain( - HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPWSTR lpCmdLine, - int nCmdShow) { +int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPWSTR lpCmdLine, int nCmdShow) { #ifdef CRU_DEBUG - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); -#endif - - Application application(hInstance); - - const auto window = Window::CreateOverlapped(); - /* - window.native_message_event.AddHandler([](cru::ui::events::WindowNativeMessageEventArgs& args) - { - if (args.GetWindowMessage().msg == WM_PAINT) - { - OutputDebugStringW(L"Paint!\n"); - //args.SetResult(0); - } - }); - */ - /* - // test1 - cru::ui::controls::TextBlock text_block; - text_block.SetText(L"Hello world!"); - text_block.SetSize(cru::ui::Size(200, 30)); - window.AddChild(&text_block); - - std::array<D2D_COLOR_F, 4> colors = - { - D2D1::ColorF(D2D1::ColorF::Blue), - D2D1::ColorF(D2D1::ColorF::Yellow), - D2D1::ColorF(D2D1::ColorF::Green), - D2D1::ColorF(D2D1::ColorF::Red) - }; - - std::random_device rd; // only used once to initialise (seed) engine - std::mt19937 rng(rd()); // random-number engine used (Mersenne-Twister in this case) - std::uniform_int_distribution<decltype(colors.size())> uni(0, colors.size() - 1); // guaranteed unbiased - - - window.draw_event.AddHandler([&](cru::ui::events::DrawEventArgs& args) { - auto device_context = args.GetDeviceContext(); - - ID2D1SolidColorBrush* brush; - device_context->CreateSolidColorBrush(colors[uni(rng)], &brush); - - device_context->FillRectangle(D2D1::RectF(100.0f, 100.0f, 300.0f, 200.0f), brush); - - brush->Release(); - }); - - cru::SetTimeout(2.0, [&window]() { - window.InvalidateDraw(); - - auto task = cru::SetInterval(0.5, [&window]() { - window.InvalidateDraw(); - }); - - cru::SetTimeout(4, [task]() { - task->Cancel(); - task->Cancel(); // test for idempotency. - }); - }); - */ - - - //test 2 - const auto layout = CreateWithLayout<LinearLayout>(LayoutSideParams::Exactly(500), LayoutSideParams::Content()); - - layout->mouse_click_event.bubble.AddHandler([layout](cru::ui::events::MouseButtonEventArgs& args) - { - if (args.GetSender() == args.GetOriginalSender()) - layout->AddChild(TextBlock::Create(L"Layout is clicked!")); - }); - - { - const auto inner_layout = CreateWithLayout<LinearLayout>(LayoutSideParams::Content(Alignment::End), LayoutSideParams::Content(), LinearLayout::Orientation::Horizontal); - - inner_layout->AddChild(TextBlock::Create(L"Toggle debug border")); - - const auto l = FrameLayout::Create(); - l->GetLayoutParams()->padding.SetLeftRight(20.0f); - const auto toggle_button = ToggleButton::Create(); -#ifdef CRU_DEBUG_LAYOUT - toggle_button->toggle_event.AddHandler([&window](cru::ui::events::ToggleEventArgs& args) - { - window->SetDebugLayout(args.GetNewState()); - }); + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif - l->AddChild(toggle_button); - inner_layout->AddChild(l); - layout->AddChild(inner_layout); - } - - { - const auto button = Button::Create(); - button->GetLayoutParams()->padding = Thickness(20, 5); - button->SetChild(TextBlock::Create(L"Show popup window parenting this.")); - button->mouse_click_event.bubble.AddHandler([window, button](auto) - { - std::vector<cru::ui::controls::MenuItemInfo> items; - items.emplace_back(L"Hello world!", []{}); - items.emplace_back(L"Item 2", []{}); - items.emplace_back(L"Close parent window.", [window]{ window->Close(); }); - - cru::ui::controls::CreatePopupMenu(window->PointToScreen(button->GetPositionAbsolute()), items, window)->Show(); - }); - layout->AddChild(button); - } - - { - const auto button = Button::Create(); - button->GetLayoutParams()->padding = Thickness(20, 5); - button->SetChild(TextBlock::Create(L"Show popup window parenting null.")); - button->SetBackgroundBrush(cru::graph::CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gold))); - button->mouse_click_event.bubble.AddHandler([](auto) - { - auto popup = Window::CreatePopup(nullptr); - popup->SetWindowRect(Rect(100, 100, 300, 300)); - popup->Show(); - }); - layout->AddChild(button); - } - - { - const auto button = Button::Create(); - button->GetLayoutParams()->padding = Thickness(20, 5); - button->SetChild(TextBlock::Create(L"Show popup window with caption.")); - button->mouse_click_event.bubble.AddHandler([](auto) - { - auto popup = Window::CreatePopup(nullptr, true); - popup->SetWindowRect(Rect(100, 100, 300, 300)); - popup->Show(); - }); - layout->AddChild(button); - } - - { - const auto text_block = CreateWithLayout<TextBlock>(LayoutSideParams::Exactly(200), LayoutSideParams::Exactly(80), L"Hello World!!!"); - - text_block->mouse_click_event.bubble.AddHandler([layout](cru::ui::events::MouseButtonEventArgs& args) - { - layout->AddChild(TextBlock::Create(L"Hello world is clicked!")); - }); - - layout->AddChild(text_block); - } - - { - const auto text_box = TextBox::Create(); - text_box->GetLayoutParams()->width.min = 50.0f; - text_box->GetLayoutParams()->width.max = 100.0f; - text_box->char_event.tunnel.AddHandler([](cru::ui::events::CharEventArgs& args) - { - if (args.GetChar() == L'1') - args.SetHandled(); - }); - layout->AddChild(text_box); - } - - { - const auto scroll_view = CreateWithLayout<ScrollControl>(LayoutSideParams::Stretch(), LayoutSideParams::Stretch()); - - scroll_view->SetVerticalScrollBarVisibility(ScrollControl::ScrollBarVisibility::Always); - - const auto text_block = TextBlock::Create( - L"Love myself I do. Not everything, but I love the good as well as the bad. I love my crazy lifestyle, and I love my hard discipline. I love my freedom of speech and the way my eyes get dark when I'm tired. I love that I have learned to trust people with my heart, even if it will get broken. I am proud of everything that I am and will become."); - text_block->SetSelectable(true); - - scroll_view->SetChild(text_block); - layout->AddChild(scroll_view); - } - layout->AddChild(CreateWithLayout<TextBlock>(LayoutSideParams::Content(Alignment::Start), LayoutSideParams::Content(), L"This is a little short sentence!!!")); - layout->AddChild(CreateWithLayout<TextBlock>(LayoutSideParams::Content(Alignment::End), LayoutSideParams::Stretch(), L"By crupest!!!")); + Application application(hInstance); + const auto window = Window::CreateOverlapped(); - window->SetChild(layout); + const auto flex_layout = FlexLayout::Create(); - /* - window.AddChild( - CreateWithLayout<Border>(LayoutSideParams::Exactly(200), LayoutSideParams::Content(), - std::initializer_list<cru::ui::Control*>{ - CreateWithLayout<TextBox>(LayoutSideParams::Stretch(), LayoutSideParams::Content()) - } - )); - */ + window->SetChild(flex_layout); - /* test 3 - const auto linear_layout = CreateWithLayout<LinearLayout>(Thickness(50, 50), Thickness(50, 50), LinearLayout::Orientation::Vertical, ControlList{ - Button::Create({ - TextBlock::Create(L"Button") - }), - CreateWithLayout<TextBox>(Thickness(30), Thickness(20)) - }); + const auto text_block1 = TextBlock::Create(); + text_block1->SetText(L"Hello World!"); + flex_layout->AddChild(text_block1, 0); - linear_layout->SetBordered(true); + const auto text_block2 = TextBlock::Create(); + text_block2->SetText(L"Hello World!"); + flex_layout->AddChild(text_block2, 1); - window.AddChild(linear_layout); - */ - window->Show(); + window->Show(); - return application.Run(); + return application.Run(); } diff --git a/src/pre.hpp b/src/pre.hpp index ba47dc6c..dfa0666c 100644 --- a/src/pre.hpp +++ b/src/pre.hpp @@ -5,10 +5,6 @@ #endif #ifdef CRU_DEBUG -#define CRU_DEBUG_LAYOUT -#endif - -#ifdef CRU_DEBUG #define _CRTDBG_MAP_ALLOC #include <crtdbg.h> #include <cstdlib> diff --git a/src/ui/control.hpp b/src/ui/control.hpp index 5f8ac02a..9ff407b0 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -207,7 +207,7 @@ void DispatchEvent(Control* const original_sender, auto parent = original_sender; while (parent != last_receiver) { receive_list.push_back(parent); - parent = parent->GetInternalParent(); + parent = parent->GetParent(); } auto handled = false; diff --git a/src/ui/controls/flex_layout.cpp b/src/ui/controls/flex_layout.cpp index ebe61a6d..289df1f1 100644 --- a/src/ui/controls/flex_layout.cpp +++ b/src/ui/controls/flex_layout.cpp @@ -9,6 +9,10 @@ FlexLayout::FlexLayout() { render_object_ = new FlexLayoutRenderObject(); } FlexLayout::~FlexLayout() { delete render_object_; } +render::RenderObject* FlexLayout::GetRenderObject() const { + return render_object_; +} + void FlexLayout::OnAddChild(Control* child, int position) { render_object_->AddChild(child->GetRenderObject(), position); } diff --git a/src/ui/controls/flex_layout.hpp b/src/ui/controls/flex_layout.hpp index 6acd25dc..682ed8dc 100644 --- a/src/ui/controls/flex_layout.hpp +++ b/src/ui/controls/flex_layout.hpp @@ -13,8 +13,12 @@ class FlexLayout : public Layout { public: static constexpr auto control_type = L"FlexLayout"; - public: + static FlexLayout* Create() { return new FlexLayout(); } + + protected: FlexLayout(); + + public: FlexLayout(const FlexLayout& other) = delete; FlexLayout(FlexLayout&& other) = delete; FlexLayout& operator=(const FlexLayout& other) = delete; @@ -23,9 +27,7 @@ class FlexLayout : public Layout { StringView GetControlType() const override final { return control_type; } - render::RenderObject* GetRenderObject() const override { - return render_object_; - } + render::RenderObject* GetRenderObject() const override; protected: void OnAddChild(Control* child, int position) override; diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp index 123dbc86..c891b832 100644 --- a/src/ui/controls/text_block.cpp +++ b/src/ui/controls/text_block.cpp @@ -17,6 +17,10 @@ TextBlock::TextBlock() { TextBlock::~TextBlock() { delete render_object_; } +render::RenderObject* TextBlock::GetRenderObject() const { + return render_object_; +} + String TextBlock::GetText() const { return render_object_->GetText(); } void TextBlock::SetText(const String& text) { render_object_->SetText(text); } diff --git a/src/ui/controls/text_block.hpp b/src/ui/controls/text_block.hpp index ce8977a5..c345c5ab 100644 --- a/src/ui/controls/text_block.hpp +++ b/src/ui/controls/text_block.hpp @@ -12,8 +12,12 @@ class TextBlock : public NoChildControl { public: static constexpr auto control_type = L"TextBlock"; - public: + static TextBlock* Create() { return new TextBlock(); } + + protected: TextBlock(); + + public: TextBlock(const TextBlock& other) = delete; TextBlock(TextBlock&& other) = delete; TextBlock& operator=(const TextBlock& other) = delete; @@ -22,9 +26,7 @@ class TextBlock : public NoChildControl { StringView GetControlType() const override final { return control_type; } - render::RenderObject* GetRenderObject() const override { - return render_object_; - } + render::RenderObject* GetRenderObject() const override; String GetText() const; void SetText(const String& text); diff --git a/src/ui/d2d_util.hpp b/src/ui/d2d_util.hpp index 94ef6223..96a017dc 100644 --- a/src/ui/d2d_util.hpp +++ b/src/ui/d2d_util.hpp @@ -5,88 +5,78 @@ #include "ui_base.hpp" -namespace cru::ui -{ - inline D2D1_POINT_2F Convert(const Point& point) - { - return D2D1::Point2F(point.x, point.y); - } - - inline D2D1_RECT_F Convert(const Rect& rect) - { - return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, rect.top + rect.height); - } - - inline D2D1_ROUNDED_RECT Convert(const RoundedRect& rounded_rect) - { - return D2D1::RoundedRect(Convert(rounded_rect.rect), rounded_rect.radius_x, rounded_rect.radius_y); - } - - inline D2D1_ELLIPSE Convert(const Ellipse& ellipse) - { - return D2D1::Ellipse(Convert(ellipse.center), ellipse.radius_x, ellipse.radius_y); - } - - inline Point Convert(const D2D1_POINT_2F& point) - { - return Point(point.x, point.y); - } - - inline Rect Convert(const D2D1_RECT_F& rect) - { - return Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); - } - - inline RoundedRect Convert(const D2D1_ROUNDED_RECT& rounded_rect) - { - return RoundedRect(Convert(rounded_rect.rect), rounded_rect.radiusX, rounded_rect.radiusY); - } - - inline Ellipse Convert(const D2D1_ELLIPSE& ellipse) - { - return Ellipse(Convert(ellipse.point), ellipse.radiusX, ellipse.radiusY); - } - - inline bool operator==(const D2D1_POINT_2F& left, const D2D1_POINT_2F& right) - { - return left.x == right.x && left.y == right.y; - } - - inline bool operator!=(const D2D1_POINT_2F& left, const D2D1_POINT_2F& right) - { - return !(left == right); - } - - inline bool operator==(const D2D1_RECT_F& left, const D2D1_RECT_F& right) - { - return left.left == right.left && - left.top == right.top && - left.right == right.right && - left.bottom == right.bottom; - } - - inline bool operator!=(const D2D1_RECT_F& left, const D2D1_RECT_F& right) - { - return !(left == right); - } - - inline bool operator==(const D2D1_ROUNDED_RECT& left, const D2D1_ROUNDED_RECT& right) - { - return left.rect == right.rect && left.radiusX == right.radiusX && left.radiusY == right.radiusY; - } - - inline bool operator!=(const D2D1_ROUNDED_RECT& left, const D2D1_ROUNDED_RECT& right) - { - return !(left == right); - } - - inline bool operator == (const D2D1_ELLIPSE& left, const D2D1_ELLIPSE& right) - { - return left.point == right.point && left.radiusX == right.radiusX && left.radiusY == right.radiusY; - } - - inline bool operator!=(const D2D1_ELLIPSE& left, const D2D1_ELLIPSE& right) - { - return !(left == right); - } +namespace cru::ui { +inline D2D1_POINT_2F Convert(const Point& point) { + return D2D1::Point2F(point.x, point.y); } + +inline D2D1_RECT_F Convert(const Rect& rect) { + return D2D1::RectF(rect.left, rect.top, rect.left + rect.width, + rect.top + rect.height); +} + +inline D2D1_ROUNDED_RECT Convert(const RoundedRect& rounded_rect) { + return D2D1::RoundedRect(Convert(rounded_rect.rect), rounded_rect.radius_x, + rounded_rect.radius_y); +} + +inline D2D1_ELLIPSE Convert(const Ellipse& ellipse) { + return D2D1::Ellipse(Convert(ellipse.center), ellipse.radius_x, + ellipse.radius_y); +} + +inline Point Convert(const D2D1_POINT_2F& point) { + return Point(point.x, point.y); +} + +inline Rect Convert(const D2D1_RECT_F& rect) { + return Rect(rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top); +} + +inline RoundedRect Convert(const D2D1_ROUNDED_RECT& rounded_rect) { + return RoundedRect(Convert(rounded_rect.rect), rounded_rect.radiusX, + rounded_rect.radiusY); +} + +inline Ellipse Convert(const D2D1_ELLIPSE& ellipse) { + return Ellipse(Convert(ellipse.point), ellipse.radiusX, ellipse.radiusY); +} + +inline bool operator==(const D2D1_POINT_2F& left, const D2D1_POINT_2F& right) { + return left.x == right.x && left.y == right.y; +} + +inline bool operator!=(const D2D1_POINT_2F& left, const D2D1_POINT_2F& right) { + return !(left == right); +} + +inline bool operator==(const D2D1_RECT_F& left, const D2D1_RECT_F& right) { + return left.left == right.left && left.top == right.top && + left.right == right.right && left.bottom == right.bottom; +} + +inline bool operator!=(const D2D1_RECT_F& left, const D2D1_RECT_F& right) { + return !(left == right); +} + +inline bool operator==(const D2D1_ROUNDED_RECT& left, + const D2D1_ROUNDED_RECT& right) { + return left.rect == right.rect && left.radiusX == right.radiusX && + left.radiusY == right.radiusY; +} + +inline bool operator!=(const D2D1_ROUNDED_RECT& left, + const D2D1_ROUNDED_RECT& right) { + return !(left == right); +} + +inline bool operator==(const D2D1_ELLIPSE& left, const D2D1_ELLIPSE& right) { + return left.point == right.point && left.radiusX == right.radiusX && + left.radiusY == right.radiusY; +} + +inline bool operator!=(const D2D1_ELLIPSE& left, const D2D1_ELLIPSE& right) { + return !(left == right); +} +} // namespace cru::ui diff --git a/src/ui/events/ui_event.cpp b/src/ui/events/ui_event.cpp index a1fc3d82..ee3a68dc 100644 --- a/src/ui/events/ui_event.cpp +++ b/src/ui/events/ui_event.cpp @@ -4,10 +4,4 @@ namespace cru::ui::events { - Point MouseEventArgs::GetPoint(Control* control, const RectRange range) const - { - if (point_.has_value()) - return control->TransformPoint(control->WindowToControl(point_.value()), RectRange::Margin, range); - return Point(); - } } diff --git a/src/ui/events/ui_event.hpp b/src/ui/events/ui_event.hpp index e0040942..572cf8d6 100644 --- a/src/ui/events/ui_event.hpp +++ b/src/ui/events/ui_event.hpp @@ -1,301 +1,228 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" #include <optional> +#include "system_headers.hpp" #include "base.hpp" #include "cru_event.hpp" #include "ui/ui_base.hpp" -#include "ui/layout_base.hpp" +#include "ui/input_util.hpp" -namespace cru::ui -{ - class Control; +namespace cru::ui { +class Control; } -namespace cru::ui::events -{ - class UiEventArgs : public BasicEventArgs - { - public: - UiEventArgs(Object* sender, Object* original_sender) - : BasicEventArgs(sender), original_sender_(original_sender), handled_(false) - { - - } - - UiEventArgs(const UiEventArgs& other) = default; - UiEventArgs(UiEventArgs&& other) = default; - UiEventArgs& operator=(const UiEventArgs& other) = default; - UiEventArgs& operator=(UiEventArgs&& other) = default; - ~UiEventArgs() override = default; - - Object* GetOriginalSender() const - { - return original_sender_; - } - - bool IsHandled() const - { - return handled_; - } - - void SetHandled(const bool handled = true) - { - handled_ = handled; - } - - private: - Object* original_sender_; - bool handled_; - }; - - template <typename TEventArgs> - class RoutedEvent - { - public: - static_assert(std::is_base_of_v<UiEventArgs, TEventArgs>, "TEventArgs must be subclass of UiEventArgs."); - - using EventArgs = TEventArgs; - - RoutedEvent() = default; - RoutedEvent(const RoutedEvent& other) = delete; - RoutedEvent(RoutedEvent&& other) = delete; - RoutedEvent& operator=(const RoutedEvent& other) = delete; - RoutedEvent& operator=(RoutedEvent&& other) = delete; - ~RoutedEvent() = default; - - Event<TEventArgs> direct; - Event<TEventArgs> bubble; - Event<TEventArgs> tunnel; - }; - - class MouseEventArgs : public UiEventArgs - { - public: - MouseEventArgs(Object* sender, Object* original_sender, const std::optional<Point>& point = std::nullopt) - : UiEventArgs(sender, original_sender), point_(point) - { - - } - MouseEventArgs(const MouseEventArgs& other) = default; - MouseEventArgs(MouseEventArgs&& other) = default; - MouseEventArgs& operator=(const MouseEventArgs& other) = default; - MouseEventArgs& operator=(MouseEventArgs&& other) = default; - ~MouseEventArgs() override = default; - - Point GetPoint(Control* control, RectRange range = RectRange::Content) const; - - private: - std::optional<Point> point_; - }; - - - class MouseButtonEventArgs : public MouseEventArgs - { - public: - MouseButtonEventArgs(Object* sender, Object* original_sender, const Point& point, const MouseButton button) - : MouseEventArgs(sender, original_sender, point), button_(button) - { - - } - MouseButtonEventArgs(const MouseButtonEventArgs& other) = default; - MouseButtonEventArgs(MouseButtonEventArgs&& other) = default; - MouseButtonEventArgs& operator=(const MouseButtonEventArgs& other) = default; - MouseButtonEventArgs& operator=(MouseButtonEventArgs&& other) = default; - ~MouseButtonEventArgs() override = default; - - MouseButton GetMouseButton() const - { - return button_; - } - - private: - MouseButton button_; - }; - - - class MouseWheelEventArgs : public MouseEventArgs - { - public: - MouseWheelEventArgs(Object* sender, Object* original_sender, const Point& point, const float delta) - : MouseEventArgs(sender, original_sender, point), delta_(delta) - { - - } - MouseWheelEventArgs(const MouseWheelEventArgs& other) = default; - MouseWheelEventArgs(MouseWheelEventArgs&& other) = default; - MouseWheelEventArgs& operator=(const MouseWheelEventArgs& other) = default; - MouseWheelEventArgs& operator=(MouseWheelEventArgs&& other) = default; - ~MouseWheelEventArgs() override = default; - - float GetDelta() const - { - return delta_; - } - - private: - float delta_; - }; - - - class DrawEventArgs : public UiEventArgs - { - public: - DrawEventArgs(Object* sender, Object* original_sender, ID2D1DeviceContext* device_context) - : UiEventArgs(sender, original_sender), device_context_(device_context) - { - - } - 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_; - } - - private: - ID2D1DeviceContext * device_context_; - }; - - - class FocusChangeEventArgs : public UiEventArgs - { - public: - FocusChangeEventArgs(Object* sender, Object* original_sender, const bool is_window = false) - : UiEventArgs(sender, original_sender), is_window_(is_window) - { - - } - FocusChangeEventArgs(const FocusChangeEventArgs& other) = default; - FocusChangeEventArgs(FocusChangeEventArgs&& other) = default; - FocusChangeEventArgs& operator=(const FocusChangeEventArgs& other) = default; - FocusChangeEventArgs& operator=(FocusChangeEventArgs&& other) = default; - ~FocusChangeEventArgs() override = default; - - // Return whether the focus change is caused by the window-wide focus change. - bool IsWindow() const - { - return is_window_; - } - - private: - bool is_window_; - }; - - class ToggleEventArgs : public UiEventArgs - { - public: - ToggleEventArgs(Object* sender, Object* original_sender, bool new_state) - : UiEventArgs(sender, original_sender), new_state_(new_state) - { - - } - ToggleEventArgs(const ToggleEventArgs& other) = default; - ToggleEventArgs(ToggleEventArgs&& other) = default; - ToggleEventArgs& operator=(const ToggleEventArgs& other) = default; - ToggleEventArgs& operator=(ToggleEventArgs&& other) = default; - ~ToggleEventArgs() override = default; - - bool GetNewState() const - { - return new_state_; - } - - private: - 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<LRESULT> GetResult() const - { - return result_; - } - - void SetResult(const std::optional<LRESULT> result) - { - result_ = result; - } - - private: - WindowNativeMessage message_; - std::optional<LRESULT> result_; - }; - - class KeyEventArgs : public UiEventArgs - { - public: - KeyEventArgs(Object* sender, Object* original_sender, int virtual_code) - : UiEventArgs(sender, original_sender), virtual_code_(virtual_code) - { - } - KeyEventArgs(const KeyEventArgs& other) = default; - KeyEventArgs(KeyEventArgs&& other) = default; - KeyEventArgs& operator=(const KeyEventArgs& other) = default; - KeyEventArgs& operator=(KeyEventArgs&& other) = default; - ~KeyEventArgs() override = default; - - int GetVirtualCode() const - { - return virtual_code_; - } - - private: - int virtual_code_; - }; - - class CharEventArgs : public UiEventArgs - { - public: - CharEventArgs(Object* sender, Object* original_sender, wchar_t c) - : UiEventArgs(sender, original_sender), c_(c) - { - } - CharEventArgs(const CharEventArgs& other) = default; - CharEventArgs(CharEventArgs&& other) = default; - CharEventArgs& operator=(const CharEventArgs& other) = default; - CharEventArgs& operator=(CharEventArgs&& other) = default; - ~CharEventArgs() override = default; - - wchar_t GetChar() const - { - return c_; - } - - private: - wchar_t c_; - }; -} +namespace cru::ui::events { +class UiEventArgs : public BasicEventArgs { + public: + UiEventArgs(Object* sender, Object* original_sender) + : BasicEventArgs(sender), + original_sender_(original_sender), + handled_(false) {} + + UiEventArgs(const UiEventArgs& other) = default; + UiEventArgs(UiEventArgs&& other) = default; + UiEventArgs& operator=(const UiEventArgs& other) = default; + UiEventArgs& operator=(UiEventArgs&& other) = default; + ~UiEventArgs() override = default; + + Object* GetOriginalSender() const { return original_sender_; } + + bool IsHandled() const { return handled_; } + + void SetHandled(const bool handled = true) { handled_ = handled; } + + private: + Object* original_sender_; + bool handled_; +}; + +template <typename TEventArgs> +class RoutedEvent { + public: + static_assert(std::is_base_of_v<UiEventArgs, TEventArgs>, + "TEventArgs must be subclass of UiEventArgs."); + + using EventArgs = TEventArgs; + + RoutedEvent() = default; + RoutedEvent(const RoutedEvent& other) = delete; + RoutedEvent(RoutedEvent&& other) = delete; + RoutedEvent& operator=(const RoutedEvent& other) = delete; + RoutedEvent& operator=(RoutedEvent&& other) = delete; + ~RoutedEvent() = default; + + Event<TEventArgs> direct; + Event<TEventArgs> bubble; + Event<TEventArgs> tunnel; +}; + +class MouseEventArgs : public UiEventArgs { + public: + MouseEventArgs(Object* sender, Object* original_sender, + const std::optional<Point>& point = std::nullopt) + : UiEventArgs(sender, original_sender), point_(point) {} + MouseEventArgs(const MouseEventArgs& other) = default; + MouseEventArgs(MouseEventArgs&& other) = default; + MouseEventArgs& operator=(const MouseEventArgs& other) = default; + MouseEventArgs& operator=(MouseEventArgs&& other) = default; + ~MouseEventArgs() override = default; + + Point GetPoint() const { return point_.value_or(Point::Zero()); } + + private: + std::optional<Point> point_; +}; + +class MouseButtonEventArgs : public MouseEventArgs { + public: + MouseButtonEventArgs(Object* sender, Object* original_sender, + const Point& point, const MouseButton button) + : MouseEventArgs(sender, original_sender, point), button_(button) {} + MouseButtonEventArgs(const MouseButtonEventArgs& other) = default; + MouseButtonEventArgs(MouseButtonEventArgs&& other) = default; + MouseButtonEventArgs& operator=(const MouseButtonEventArgs& other) = default; + MouseButtonEventArgs& operator=(MouseButtonEventArgs&& other) = default; + ~MouseButtonEventArgs() override = default; + + MouseButton GetMouseButton() const { return button_; } + + private: + MouseButton button_; +}; + +class MouseWheelEventArgs : public MouseEventArgs { + public: + MouseWheelEventArgs(Object* sender, Object* original_sender, + const Point& point, const float delta) + : MouseEventArgs(sender, original_sender, point), delta_(delta) {} + MouseWheelEventArgs(const MouseWheelEventArgs& other) = default; + MouseWheelEventArgs(MouseWheelEventArgs&& other) = default; + MouseWheelEventArgs& operator=(const MouseWheelEventArgs& other) = default; + MouseWheelEventArgs& operator=(MouseWheelEventArgs&& other) = default; + ~MouseWheelEventArgs() override = default; + + float GetDelta() const { return delta_; } + + private: + float delta_; +}; + +class DrawEventArgs : public UiEventArgs { + public: + DrawEventArgs(Object* sender, Object* original_sender, + ID2D1DeviceContext* device_context) + : UiEventArgs(sender, original_sender), device_context_(device_context) {} + 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_; } + + private: + ID2D1DeviceContext* device_context_; +}; + +class FocusChangeEventArgs : public UiEventArgs { + public: + FocusChangeEventArgs(Object* sender, Object* original_sender, + const bool is_window = false) + : UiEventArgs(sender, original_sender), is_window_(is_window) {} + FocusChangeEventArgs(const FocusChangeEventArgs& other) = default; + FocusChangeEventArgs(FocusChangeEventArgs&& other) = default; + FocusChangeEventArgs& operator=(const FocusChangeEventArgs& other) = default; + FocusChangeEventArgs& operator=(FocusChangeEventArgs&& other) = default; + ~FocusChangeEventArgs() override = default; + + // Return whether the focus change is caused by the window-wide focus change. + bool IsWindow() const { return is_window_; } + + private: + bool is_window_; +}; + +class ToggleEventArgs : public UiEventArgs { + public: + ToggleEventArgs(Object* sender, Object* original_sender, bool new_state) + : UiEventArgs(sender, original_sender), new_state_(new_state) {} + ToggleEventArgs(const ToggleEventArgs& other) = default; + ToggleEventArgs(ToggleEventArgs&& other) = default; + ToggleEventArgs& operator=(const ToggleEventArgs& other) = default; + ToggleEventArgs& operator=(ToggleEventArgs&& other) = default; + ~ToggleEventArgs() override = default; + + bool GetNewState() const { return new_state_; } + + private: + 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<LRESULT> GetResult() const { return result_; } + + void SetResult(const std::optional<LRESULT> result) { result_ = result; } + + private: + WindowNativeMessage message_; + std::optional<LRESULT> result_; +}; + +class KeyEventArgs : public UiEventArgs { + public: + KeyEventArgs(Object* sender, Object* original_sender, int virtual_code) + : UiEventArgs(sender, original_sender), virtual_code_(virtual_code) {} + KeyEventArgs(const KeyEventArgs& other) = default; + KeyEventArgs(KeyEventArgs&& other) = default; + KeyEventArgs& operator=(const KeyEventArgs& other) = default; + KeyEventArgs& operator=(KeyEventArgs&& other) = default; + ~KeyEventArgs() override = default; + + int GetVirtualCode() const { return virtual_code_; } + + private: + int virtual_code_; +}; + +class CharEventArgs : public UiEventArgs { + public: + CharEventArgs(Object* sender, Object* original_sender, wchar_t c) + : UiEventArgs(sender, original_sender), c_(c) {} + CharEventArgs(const CharEventArgs& other) = default; + CharEventArgs(CharEventArgs&& other) = default; + CharEventArgs& operator=(const CharEventArgs& other) = default; + CharEventArgs& operator=(CharEventArgs&& other) = default; + ~CharEventArgs() override = default; + + wchar_t GetChar() const { return c_; } + + private: + wchar_t c_; +}; +} // namespace cru::ui::events diff --git a/src/ui/input_util.cpp b/src/ui/input_util.cpp index 6cf2d695..3fe34f10 100644 --- a/src/ui/input_util.cpp +++ b/src/ui/input_util.cpp @@ -2,22 +2,19 @@ #include "system_headers.hpp" -namespace cru::ui -{ - bool IsKeyDown(const int virtual_code) - { - const auto result = ::GetKeyState(virtual_code); - return (static_cast<unsigned short>(result) & 0x8000) != 0; - } +namespace cru::ui { +bool IsKeyDown(const int virtual_code) { + const auto result = ::GetKeyState(virtual_code); + return (static_cast<unsigned short>(result) & 0x8000) != 0; +} - bool IsKeyToggled(const int virtual_code) - { - const auto result = ::GetKeyState(virtual_code); - return (static_cast<unsigned short>(result) & 1) != 0; - } +bool IsKeyToggled(const int virtual_code) { + const auto result = ::GetKeyState(virtual_code); + return (static_cast<unsigned short>(result) & 1) != 0; +} - bool IsAnyMouseButtonDown() - { - return IsKeyDown(VK_LBUTTON) || IsKeyDown(VK_RBUTTON) || IsKeyDown(VK_MBUTTON); - } +bool IsAnyMouseButtonDown() { + return IsKeyDown(VK_LBUTTON) || IsKeyDown(VK_RBUTTON) || + IsKeyDown(VK_MBUTTON); } +} // namespace cru::ui diff --git a/src/ui/input_util.hpp b/src/ui/input_util.hpp index 13f568b3..2d01f725 100644 --- a/src/ui/input_util.hpp +++ b/src/ui/input_util.hpp @@ -1,18 +1,10 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -namespace cru::ui -{ - enum class MouseButton - { - Left, - Right, - Middle - }; +namespace cru::ui { +enum class MouseButton { Left, Right, Middle }; - bool IsKeyDown(int virtual_code); - bool IsKeyToggled(int virtual_code); - bool IsAnyMouseButtonDown(); -} +bool IsKeyDown(int virtual_code); +bool IsKeyToggled(int virtual_code); +bool IsAnyMouseButtonDown(); +} // namespace cru::ui diff --git a/src/ui/render/flex_layout_render_object.cpp b/src/ui/render/flex_layout_render_object.cpp index 58e7e338..7891906d 100644 --- a/src/ui/render/flex_layout_render_object.cpp +++ b/src/ui/render/flex_layout_render_object.cpp @@ -162,6 +162,8 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { return start_point + (total_length - content_length) / 2.0f; case Alignment::End: return start_point + total_length - content_length; + default: + UnreachableCode(); } }; diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 0a0e693c..0be44c1e 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -51,6 +51,8 @@ void RenderObject::OnAddChild(RenderObject* new_child, int position) {} void RenderObject::OnRemoveChild(RenderObject* removed_child, int position) {} +void RenderObject::OnSizeChanged(const Size& old_size, const Size& new_size) {} + void RenderObject::SetParent(RenderObject* new_parent) { const auto old_parent = parent_; parent_ = new_parent; @@ -80,8 +82,7 @@ void RenderObject::OnMeasureCore(const Size& available_size) { const auto actual_content_size = OnMeasureContent(coerced_content_available_size); - SetPreferredSize(margin_padding_size + content_available_size + - actual_content_size); + SetPreferredSize(margin_padding_size + actual_content_size); } void RenderObject::OnLayoutCore(const Rect& rect) { diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 34f5dcee..a9950198 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -37,7 +37,11 @@ class RenderObject : public Object { Point GetOffset() const { return offset_; } void SetOffset(const Point& offset) { offset_ = offset; } Size GetSize() const { return size_; } - void SetSize(const Size& size) { size_ = size; } + void SetSize(const Size& size) { + const auto old_size = size_; + size_ = size; + OnSizeChanged(old_size, size); + } Thickness GetMargin() const { return margin_; } void SetMargin(const Thickness& margin) { margin_ = margin; } @@ -64,6 +68,8 @@ class RenderObject : public Object { virtual void OnAddChild(RenderObject* new_child, int position); virtual void OnRemoveChild(RenderObject* removed_child, int position); + virtual void OnSizeChanged(const Size& old_size, const Size& new_size); + virtual Size OnMeasureContent(const Size& available_size) = 0; virtual void OnLayoutContent(const Rect& content_rect) = 0; diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp index 43724e9f..e8032f78 100644 --- a/src/ui/render/text_render_object.cpp +++ b/src/ui/render/text_render_object.cpp @@ -62,6 +62,12 @@ RenderObject* TextRenderObject::HitTest(const Point& point) { return Rect{Point::Zero(), GetSize()}.IsPointInside(point) ? this : nullptr; } +void TextRenderObject::OnSizeChanged(const Size& old_size, + const Size& new_size) { + ThrowIfFailed(text_layout_->SetMaxWidth(new_size.width)); + ThrowIfFailed(text_layout_->SetMaxHeight(new_size.height)); +} + Size TextRenderObject::OnMeasureContent(const Size& available_size) { ThrowIfFailed(text_layout_->SetMaxWidth(available_size.width)); ThrowIfFailed(text_layout_->SetMaxHeight(available_size.height)); diff --git a/src/ui/render/text_render_object.hpp b/src/ui/render/text_render_object.hpp index d1d91034..4361b9c0 100644 --- a/src/ui/render/text_render_object.hpp +++ b/src/ui/render/text_render_object.hpp @@ -56,6 +56,8 @@ class TextRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; protected: + void OnSizeChanged(const Size& old_size, const Size& new_size) override; + Size OnMeasureContent(const Size& available_size) override; void OnLayoutContent(const Rect& content_rect) override; diff --git a/src/ui/render/window_render_object.cpp b/src/ui/render/window_render_object.cpp new file mode 100644 index 00000000..52452bd4 --- /dev/null +++ b/src/ui/render/window_render_object.cpp @@ -0,0 +1,48 @@ +#include "window_render_object.hpp" + +#include <cassert> + +#include "graph/graph.hpp" +#include "ui/window.hpp" + +namespace cru::ui::render { +void WindowRenderObject::MeasureAndLayout() { + const auto client_size = window_->GetClientSize(); + Measure(client_size); + Layout(Rect{Point::Zero(), client_size}); +} + +void WindowRenderObject::Draw(ID2D1RenderTarget* render_target) { + if (const auto child = GetChild()) { + auto offset = child->GetOffset(); + graph::WithTransform(render_target, + D2D1::Matrix3x2F::Translation(offset.x, offset.y), + [child](auto rt) { child->Draw(rt); }); + } +} + +RenderObject* WindowRenderObject::HitTest(const Point& point) { + if (const auto child = GetChild()) { + auto offset = child->GetOffset(); + Point p{point.x - offset.x, point.y - offset.y}; + const auto result = child->HitTest(point); + if (result != nullptr) { + return result; + } + } + return Rect{Point::Zero(), GetSize()}.IsPointInside(point) ? this : nullptr; +} + +void WindowRenderObject::OnAddChild(RenderObject* new_child, int position) { + assert(GetChildren().size() == 1); +} + +Size WindowRenderObject::OnMeasureContent(const Size& available_size) { + if (const auto child = GetChild()) child->Measure(available_size); + return available_size; +} + +void WindowRenderObject::OnLayoutContent(const Rect& content_rect) { + if (const auto child = GetChild()) child->Layout(content_rect); +} +} // namespace cru::ui::render diff --git a/src/ui/render/window_render_object.hpp b/src/ui/render/window_render_object.hpp new file mode 100644 index 00000000..63eb8588 --- /dev/null +++ b/src/ui/render/window_render_object.hpp @@ -0,0 +1,40 @@ +#pragma once +#include "pre.hpp" + +#include "render_object.hpp" + +namespace cru::ui { +class Window; +} + +namespace cru::ui::render { +class WindowRenderObject : public RenderObject { + public: + WindowRenderObject(Window* window) : window_(window) {} + WindowRenderObject(const WindowRenderObject& other) = delete; + WindowRenderObject(WindowRenderObject&& other) = delete; + WindowRenderObject& operator=(const WindowRenderObject& other) = delete; + WindowRenderObject& operator=(WindowRenderObject&& other) = delete; + ~WindowRenderObject() override = default; + + void MeasureAndLayout(); + + void Draw(ID2D1RenderTarget* render_target) override; + + RenderObject* HitTest(const Point& point) override; + + protected: + void OnAddChild(RenderObject* new_child, int position) override; + + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + private: + RenderObject* GetChild() const { + return GetChildren().empty() ? nullptr : GetChildren()[0]; + } + + private: + Window* window_; +}; +} // namespace cru::ui::render diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 51b3f628..f92811a7 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -1,767 +1,645 @@ #include "window.hpp" #include "application.hpp" -#include "graph/graph.hpp" -#include "exception.hpp" #include "cursor.hpp" +#include "exception.hpp" +#include "graph/graph.hpp" +#include "render/window_render_object.hpp" -namespace cru::ui -{ - LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { - auto window = WindowManager::GetInstance()->FromHandle(hWnd); - - LRESULT result; - if (window != nullptr && window->HandleWindowMessage(hWnd, Msg, wParam, lParam, result)) - return result; - - return DefWindowProc(hWnd, Msg, wParam, lParam); - } - - WindowManager* WindowManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton<WindowManager>([](auto) - { - return new WindowManager{}; - }); - } - - WindowManager::WindowManager() { - general_window_class_ = std::make_unique<WindowClass>( - L"CruUIWindowClass", - GeneralWndProc, - Application::GetInstance()->GetInstanceHandle() - ); - } - - 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."); - - 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."); - window_map_.erase(find_result); - - if (window_map_.empty()) - Application::GetInstance()->Quit(0); - } - - Window* WindowManager::FromHandle(HWND hwnd) { - const auto find_result = window_map_.find(hwnd); - if (find_result == window_map_.end()) - return nullptr; - else - return find_result->second; - } +namespace cru::ui { +LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam, + LPARAM lParam) { + auto window = WindowManager::GetInstance()->FromHandle(hWnd); - std::vector<Window*> WindowManager::GetAllWindows() const - { - std::vector<Window*> windows; - for (auto [key, value] : window_map_) - windows.push_back(value); - return windows; - } + LRESULT result; + if (window != nullptr && + window->HandleWindowMessage(hWnd, Msg, wParam, lParam, result)) + return result; - inline Point PiToDip(const POINT& pi_point) - { - return Point( - graph::PixelToDipX(pi_point.x), - graph::PixelToDipY(pi_point.y) - ); - } + return DefWindowProc(hWnd, Msg, wParam, lParam); +} - inline POINT DipToPi(const Point& dip_point) - { - POINT result; - result.x = graph::DipToPixelX(dip_point.x); - result.y = graph::DipToPixelY(dip_point.y); - return result; - } +WindowManager* WindowManager::GetInstance() { + return Application::GetInstance()->ResolveSingleton<WindowManager>( + [](auto) { return new WindowManager{}; }); +} +WindowManager::WindowManager() { + general_window_class_ = std::make_unique<WindowClass>( + L"CruUIWindowClass", GeneralWndProc, + Application::GetInstance()->GetInstanceHandle()); +} - namespace - { - Cursor::Ptr GetCursorInherit(Control* control) - { - while (control != nullptr) - { - const auto cursor = control->GetCursor(); - if (cursor != nullptr) - return cursor; - control = control->GetInternalParent(); - } - return cursors::arrow; - } - } +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."); - Window* Window::CreateOverlapped() - { - return new Window(tag_overlapped_constructor{}); - } + window_map_.emplace(hwnd, window); +} - Window* Window::CreatePopup(Window* parent, const bool caption) - { - return new Window(tag_popup_constructor{}, parent, caption); - } +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."); + window_map_.erase(find_result); + if (window_map_.empty()) Application::GetInstance()->Quit(0); +} - Window::Window(tag_overlapped_constructor) - { - BeforeCreateHwnd(); +Window* WindowManager::FromHandle(HWND hwnd) { + const auto find_result = window_map_.find(hwnd); + if (find_result == window_map_.end()) + return nullptr; + else + return find_result->second; +} - const auto window_manager = WindowManager::GetInstance(); +std::vector<Window*> WindowManager::GetAllWindows() const { + std::vector<Window*> windows; + for (auto [key, value] : window_map_) windows.push_back(value); + return windows; +} - hwnd_ = CreateWindowEx(0, - window_manager->GetGeneralWindowClass()->GetName(), - L"", WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - nullptr, nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr - ); +inline Point PiToDip(const POINT& pi_point) { + return Point(graph::PixelToDipX(pi_point.x), graph::PixelToDipY(pi_point.y)); +} - if (hwnd_ == nullptr) - throw std::runtime_error("Failed to create window."); +inline POINT DipToPi(const Point& dip_point) { + POINT result; + result.x = graph::DipToPixelX(dip_point.x); + result.y = graph::DipToPixelY(dip_point.y); + return result; +} - AfterCreateHwnd(window_manager); - } +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(tag_popup_constructor, Window* parent, const bool caption) - { - if (parent != nullptr && !parent->IsWindowValid()) - throw std::runtime_error("Parent window is not valid."); +Window* Window::CreateOverlapped() { + return new Window(tag_overlapped_constructor{}); +} - BeforeCreateHwnd(); +Window* Window::CreatePopup(Window* parent, const bool caption) { + return new Window(tag_popup_constructor{}, parent, caption); +} - parent_window_ = parent; +Window::Window(tag_overlapped_constructor) { + BeforeCreateHwnd(); - const auto window_manager = WindowManager::GetInstance(); + const auto window_manager = WindowManager::GetInstance(); - hwnd_ = CreateWindowEx(0, - window_manager->GetGeneralWindowClass()->GetName(), - L"", caption ? (WS_POPUPWINDOW | WS_CAPTION) : WS_POPUP, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - parent == nullptr ? nullptr : parent->GetWindowHandle(), - nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr - ); + hwnd_ = + CreateWindowEx(0, window_manager->GetGeneralWindowClass()->GetName(), L"", + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + 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 std::runtime_error("Failed to create window."); - AfterCreateHwnd(window_manager); - } + AfterCreateHwnd(window_manager); +} - void Window::BeforeCreateHwnd() - { - window_ = this; - } +Window::Window(tag_popup_constructor, Window* parent, const bool caption) { + if (parent != nullptr && !parent->IsWindowValid()) + throw std::runtime_error("Parent window is not valid."); - void Window::AfterCreateHwnd(WindowManager* window_manager) - { - window_manager->RegisterWindow(hwnd_, this); + BeforeCreateHwnd(); - render_target_ = graph::GraphManager::GetInstance()->CreateWindowRenderTarget(hwnd_); + parent_window_ = parent; - SetCursor(cursors::arrow); - } + const auto window_manager = WindowManager::GetInstance(); - Window::~Window() { - if (IsWindowValid()) - { - SetDeleteThisOnDestroy(false); // avoid double delete. - Close(); - } - TraverseDescendants([this](Control* control) { - control->OnDetachToWindow(this); - }); - } + hwnd_ = CreateWindowEx( + 0, window_manager->GetGeneralWindowClass()->GetName(), L"", + caption ? (WS_POPUPWINDOW | WS_CAPTION) : WS_POPUP, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + parent == nullptr ? nullptr : parent->GetWindowHandle(), nullptr, + Application::GetInstance()->GetInstanceHandle(), nullptr); - StringView Window::GetControlType() const - { - return control_type; - } + if (hwnd_ == nullptr) throw std::runtime_error("Failed to create window."); - void Window::SetDeleteThisOnDestroy(bool value) - { - delete_this_on_destroy_ = value; - } + AfterCreateHwnd(window_manager); +} - void Window::Close() { - if (IsWindowValid()) - DestroyWindow(hwnd_); - } +void Window::BeforeCreateHwnd() { window_ = this; } - void Window::InvalidateDraw() { - if (IsWindowValid()) { - InvalidateRect(hwnd_, nullptr, false); - } - } +void Window::AfterCreateHwnd(WindowManager* window_manager) { + window_manager->RegisterWindow(hwnd_, this); - void Window::Show() { - if (IsWindowValid()) { - ShowWindow(hwnd_, SW_SHOWNORMAL); - } - } + render_target_ = + graph::GraphManager::GetInstance()->CreateWindowRenderTarget(hwnd_); - void Window::Hide() { - if (IsWindowValid()) { - ShowWindow(hwnd_, SW_HIDE); - } - } + SetCursor(cursors::arrow); - Size Window::GetClientSize() { - if (!IsWindowValid()) - return Size(); + render_object_ = new render::WindowRenderObject(this); +} - const auto pixel_rect = GetClientRectPixel(); - return Size( - graph::PixelToDipX(pixel_rect.right), - graph::PixelToDipY(pixel_rect.bottom) - ); - } +Window::~Window() { + if (IsWindowValid()) { + SetDeleteThisOnDestroy(false); // avoid double delete. + Close(); + } + TraverseDescendants( + [this](Control* control) { control->OnDetachToWindow(this); }); + delete render_object_; +} - void Window::SetClientSize(const Size & size) { - if (IsWindowValid()) { - const auto window_style = static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_STYLE)); - const auto window_ex_style = static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_EXSTYLE)); - - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = graph::DipToPixelX(size.width); - rect.bottom = graph::DipToPixelY(size.height); - AdjustWindowRectEx(&rect, window_style, FALSE, window_ex_style); - - SetWindowPos( - hwnd_, nullptr, 0, 0, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOZORDER | SWP_NOMOVE - ); - } - } +StringView Window::GetControlType() const { return control_type; } - Rect Window::GetWindowRect() { - if (!IsWindowValid()) - return Rect(); +render::RenderObject* Window::GetRenderObject() const { return render_object_; } - RECT rect; - ::GetWindowRect(hwnd_, &rect); +void Window::SetDeleteThisOnDestroy(bool value) { + delete_this_on_destroy_ = value; +} - return Rect::FromVertices( - graph::PixelToDipX(rect.left), - graph::PixelToDipY(rect.top), - graph::PixelToDipX(rect.right), - graph::PixelToDipY(rect.bottom) - ); - } +void Window::Close() { + if (IsWindowValid()) DestroyWindow(hwnd_); +} - void Window::SetWindowRect(const Rect & rect) { - if (IsWindowValid()) { - SetWindowPos( - hwnd_, nullptr, - graph::DipToPixelX(rect.left), - graph::DipToPixelY(rect.top), - graph::DipToPixelX(rect.GetRight()), - graph::DipToPixelY(rect.GetBottom()), - SWP_NOZORDER - ); - } - } +void Window::InvalidateDraw() { + if (IsWindowValid()) { + InvalidateRect(hwnd_, nullptr, false); + } +} - void Window::SetWindowPosition(const Point& position) - { - if (IsWindowValid()) { - SetWindowPos( - hwnd_, nullptr, - graph::DipToPixelX(position.x), - graph::DipToPixelY(position.y), - 0, 0, - SWP_NOZORDER | SWP_NOSIZE - ); - } - } +void Window::Show() { + if (IsWindowValid()) { + ShowWindow(hwnd_, SW_SHOWNORMAL); + } +} - Point Window::PointToScreen(const Point& point) - { - if (!IsWindowValid()) - return Point::Zero(); +void Window::Hide() { + if (IsWindowValid()) { + ShowWindow(hwnd_, SW_HIDE); + } +} - auto p = DipToPi(point); - if (::ClientToScreen(GetWindowHandle(), &p) == 0) - throw Win32Error(::GetLastError(), "Failed transform point from window to screen."); - return PiToDip(p); - } +Size Window::GetClientSize() { + if (!IsWindowValid()) return Size(); - Point Window::PointFromScreen(const Point& point) - { - if (!IsWindowValid()) - return Point::Zero(); + const auto pixel_rect = GetClientRectPixel(); + return Size(graph::PixelToDipX(pixel_rect.right), + graph::PixelToDipY(pixel_rect.bottom)); +} - auto p = DipToPi(point); - if (::ScreenToClient(GetWindowHandle(), &p) == 0) - throw Win32Error(::GetLastError(), "Failed transform point from screen to window."); - return PiToDip(p); - } +void Window::SetClientSize(const Size& size) { + if (IsWindowValid()) { + const auto window_style = + static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_STYLE)); + const auto window_ex_style = + static_cast<DWORD>(GetWindowLongPtr(hwnd_, GWL_EXSTYLE)); + + RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = graph::DipToPixelX(size.width); + rect.bottom = graph::DipToPixelY(size.height); + AdjustWindowRectEx(&rect, window_style, FALSE, window_ex_style); + + SetWindowPos(hwnd_, nullptr, 0, 0, rect.right - rect.left, + rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE); + } +} - bool Window::HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT & result) { - - events::WindowNativeMessageEventArgs args(this, this, {hwnd, msg, w_param, l_param}); - native_message_event.Raise(args); - if (args.GetResult().has_value()) - { - result = args.GetResult().value(); - return true; - } - - switch (msg) { - case WM_PAINT: - OnPaintInternal(); - result = 0; - return true; - case WM_ERASEBKGND: - result = 1; - return true; - case WM_SETFOCUS: - OnSetFocusInternal(); - result = 0; - return true; - case WM_KILLFOCUS: - OnKillFocusInternal(); - result = 0; - return true; - case WM_MOUSEMOVE: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseMoveInternal(point); - result = 0; - return true; - } - case WM_LBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Left, point); - result = 0; - return true; - } - case WM_LBUTTONUP: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Left, point); - result = 0; - return true; - } - case WM_RBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Right, point); - result = 0; - return true; - } - case WM_RBUTTONUP: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Right, point); - result = 0; - return true; - } - case WM_MBUTTONDOWN: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseDownInternal(MouseButton::Middle, point); - result = 0; - return true; - } - case WM_MBUTTONUP: - { - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - OnMouseUpInternal(MouseButton::Middle, point); - result = 0; - return true; - } - case WM_MOUSEWHEEL: - POINT point; - point.x = GET_X_LPARAM(l_param); - point.y = GET_Y_LPARAM(l_param); - ScreenToClient(hwnd, &point); - OnMouseWheelInternal(GET_WHEEL_DELTA_WPARAM(w_param), point); - result = 0; - return true; - case WM_KEYDOWN: - OnKeyDownInternal(static_cast<int>(w_param)); - result = 0; - return true; - case WM_KEYUP: - OnKeyUpInternal(static_cast<int>(w_param)); - result = 0; - return true; - case WM_CHAR: - OnCharInternal(static_cast<wchar_t>(w_param)); - result = 0; - return true; - case WM_SIZE: - OnResizeInternal(LOWORD(l_param), HIWORD(l_param)); - result = 0; - return true; - case WM_ACTIVATE: - if (w_param == WA_ACTIVE || w_param == WA_CLICKACTIVE) - OnActivatedInternal(); - else if (w_param == WA_INACTIVE) - OnDeactivatedInternal(); - result = 0; - return true; - case WM_DESTROY: - OnDestroyInternal(); - result = 0; - return true; - default: - return false; - } - } +Rect Window::GetWindowRect() { + if (!IsWindowValid()) return Rect(); - Point Window::GetMousePosition() - { - if (!IsWindowValid()) - return Point::Zero(); - POINT point; - ::GetCursorPos(&point); - ::ScreenToClient(hwnd_, &point); - return PiToDip(point); - } + RECT rect; + ::GetWindowRect(hwnd_, &rect); - Point Window::GetOffset() - { - return Point(); - } + return Rect::FromVertices( + graph::PixelToDipX(rect.left), graph::PixelToDipY(rect.top), + graph::PixelToDipX(rect.right), graph::PixelToDipY(rect.bottom)); +} - Size Window::GetSize() - { - return GetClientSize(); - } +void Window::SetWindowRect(const Rect& rect) { + if (IsWindowValid()) { + SetWindowPos(hwnd_, nullptr, graph::DipToPixelX(rect.left), + graph::DipToPixelY(rect.top), + graph::DipToPixelX(rect.GetRight()), + graph::DipToPixelY(rect.GetBottom()), SWP_NOZORDER); + } +} - void Window::SetRect(const Rect& size) - { +void Window::SetWindowPosition(const Point& position) { + if (IsWindowValid()) { + SetWindowPos(hwnd_, nullptr, graph::DipToPixelX(position.x), + graph::DipToPixelY(position.y), 0, 0, + SWP_NOZORDER | SWP_NOSIZE); + } +} - } +Point Window::PointToScreen(const Point& point) { + if (!IsWindowValid()) return Point::Zero(); - bool Window::IsPointInside(const Point& point) - { - return Rect(Point::Zero(), GetClientSize()).IsPointInside(point); - } + auto p = DipToPi(point); + if (::ClientToScreen(GetWindowHandle(), &p) == 0) + throw Win32Error(::GetLastError(), + "Failed transform point from window to screen."); + return PiToDip(p); +} - void Window::WindowInvalidateLayout() - { - if (is_layout_invalid_) - return; - - is_layout_invalid_ = true; - InvokeLater([this] - { - if (is_layout_invalid_) - Relayout(); - }); - } +Point Window::PointFromScreen(const Point& point) { + if (!IsWindowValid()) return Point::Zero(); - void Window::Relayout() - { - Measure(GetSize(), AdditionalMeasureInfo{}); - OnLayoutCore(Rect(Point::Zero(), GetSize()), AdditionalLayoutInfo{}); - is_layout_invalid_ = false; - } + auto p = DipToPi(point); + if (::ScreenToClient(GetWindowHandle(), &p) == 0) + throw Win32Error(::GetLastError(), + "Failed transform point from screen to window."); + return PiToDip(p); +} - void Window::SetSizeFitContent(const Size& max_size) - { - Measure(max_size, AdditionalMeasureInfo{}); - SetClientSize(GetDesiredSize()); - OnLayoutCore(Rect(Point::Zero(), GetSize()), AdditionalLayoutInfo{}); - is_layout_invalid_ = false; - } +bool Window::HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, + LPARAM l_param, LRESULT& result) { + events::WindowNativeMessageEventArgs args(this, this, + {hwnd, msg, w_param, l_param}); + native_message_event.Raise(args); + if (args.GetResult().has_value()) { + result = args.GetResult().value(); + return true; + } + + switch (msg) { + case WM_PAINT: + OnPaintInternal(); + result = 0; + return true; + case WM_ERASEBKGND: + result = 1; + return true; + case WM_SETFOCUS: + OnSetFocusInternal(); + result = 0; + return true; + case WM_KILLFOCUS: + OnKillFocusInternal(); + result = 0; + return true; + case WM_MOUSEMOVE: { + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseMoveInternal(point); + result = 0; + return true; + } + case WM_LBUTTONDOWN: { + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseDownInternal(MouseButton::Left, point); + result = 0; + return true; + } + case WM_LBUTTONUP: { + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseUpInternal(MouseButton::Left, point); + result = 0; + return true; + } + case WM_RBUTTONDOWN: { + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseDownInternal(MouseButton::Right, point); + result = 0; + return true; + } + case WM_RBUTTONUP: { + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseUpInternal(MouseButton::Right, point); + result = 0; + return true; + } + case WM_MBUTTONDOWN: { + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseDownInternal(MouseButton::Middle, point); + result = 0; + return true; + } + case WM_MBUTTONUP: { + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + OnMouseUpInternal(MouseButton::Middle, point); + result = 0; + return true; + } + case WM_MOUSEWHEEL: + POINT point; + point.x = GET_X_LPARAM(l_param); + point.y = GET_Y_LPARAM(l_param); + ScreenToClient(hwnd, &point); + OnMouseWheelInternal(GET_WHEEL_DELTA_WPARAM(w_param), point); + result = 0; + return true; + case WM_KEYDOWN: + OnKeyDownInternal(static_cast<int>(w_param)); + result = 0; + return true; + case WM_KEYUP: + OnKeyUpInternal(static_cast<int>(w_param)); + result = 0; + return true; + case WM_CHAR: + OnCharInternal(static_cast<wchar_t>(w_param)); + result = 0; + return true; + case WM_SIZE: + OnResizeInternal(LOWORD(l_param), HIWORD(l_param)); + result = 0; + return true; + case WM_ACTIVATE: + if (w_param == WA_ACTIVE || w_param == WA_CLICKACTIVE) + OnActivatedInternal(); + else if (w_param == WA_INACTIVE) + OnDeactivatedInternal(); + result = 0; + return true; + case WM_DESTROY: + OnDestroyInternal(); + result = 0; + return true; + default: + return false; + } +} - 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."); +Point Window::GetMousePosition() { + if (!IsWindowValid()) return Point::Zero(); + POINT point; + ::GetCursorPos(&point); + ::ScreenToClient(hwnd_, &point); + return PiToDip(point); +} - if (!IsWindowValid()) - return false; +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."); - if (!window_focus_) - { - focus_control_ = control; - ::SetFocus(hwnd_); - return true; // event dispatch will be done in window message handling function "OnSetFocusInternal". - } + if (!IsWindowValid()) return false; - if (focus_control_ == control) - return true; + if (!window_focus_) { + focus_control_ = control; + ::SetFocus(hwnd_); + return true; // event dispatch will be done in window message handling + // function "OnSetFocusInternal". + } - DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, false); + if (focus_control_ == control) return true; - focus_control_ = control; + DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, false); - DispatchEvent(control, &Control::get_focus_event, nullptr, false); + focus_control_ = control; - return true; - } + DispatchEvent(control, &Control::get_focus_event, nullptr, false); - Control* Window::GetFocusControl() - { - return focus_control_; - } + return true; +} - Control* Window::CaptureMouseFor(Control* control) - { - if (control != nullptr) - { - ::SetCapture(hwnd_); - std::swap(mouse_capture_control_, control); - DispatchMouseHoverControlChangeEvent(control ? control : mouse_hover_control_, mouse_capture_control_, GetMousePosition()); - return control; - } - else - { - return ReleaseCurrentMouseCapture(); - } - } +Control* Window::GetFocusControl() { return focus_control_; } + +Control* Window::CaptureMouseFor(Control* control) { + if (control != nullptr) { + ::SetCapture(hwnd_); + std::swap(mouse_capture_control_, control); + DispatchMouseHoverControlChangeEvent( + control ? control : mouse_hover_control_, mouse_capture_control_, + GetMousePosition()); + return control; + } else { + return ReleaseCurrentMouseCapture(); + } +} - Control* Window::ReleaseCurrentMouseCapture() - { - if (mouse_capture_control_) - { - const auto previous = mouse_capture_control_; - mouse_capture_control_ = nullptr; - ::ReleaseCapture(); - DispatchMouseHoverControlChangeEvent(previous, mouse_hover_control_, GetMousePosition()); - return previous; - } - else - { - return nullptr; - } - } +Control* Window::ReleaseCurrentMouseCapture() { + if (mouse_capture_control_) { + const auto previous = mouse_capture_control_; + mouse_capture_control_ = nullptr; + ::ReleaseCapture(); + DispatchMouseHoverControlChangeEvent(previous, mouse_hover_control_, + GetMousePosition()); + return previous; + } else { + return nullptr; + } +} - void Window::UpdateCursor() - { - if (IsWindowValid() && mouse_hover_control_ != nullptr) - { - SetCursorInternal(GetCursorInherit(mouse_hover_control_)->GetHandle()); - } - } +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) - { - debug_layout_ = value; - InvalidateDraw(); - } - } +void Window::SetDebugLayout(const bool value) { + if (debug_layout_ != value) { + debug_layout_ = value; + InvalidateDraw(); + } +} #endif - RECT Window::GetClientRectPixel() { - RECT rect{ }; - GetClientRect(hwnd_, &rect); - return rect; - } +void Window::OnChildChanged(Control* old_child, Control* new_child) { + if (old_child) render_object_->RemoveChild(0); + if (new_child) render_object_->AddChild(new_child->GetRenderObject(), 0); +} - bool Window::IsMessageInQueue(UINT message) - { - MSG msg; - return ::PeekMessageW(&msg, hwnd_, message, message, PM_NOREMOVE) != 0; - } +Control* Window::HitTest(const Point& point) { + return render_object_->HitTest(point)->GetAttachedControl(); +} - void Window::SetCursorInternal(HCURSOR cursor) - { - if (IsWindowValid()) - { - ::SetClassLongPtrW(GetWindowHandle(), GCLP_HCURSOR, reinterpret_cast<LONG_PTR>(cursor)); - if (mouse_hover_control_ != nullptr) - ::SetCursor(cursor); - } - } +RECT Window::GetClientRectPixel() { + RECT rect{}; + GetClientRect(hwnd_, &rect); + return rect; +} - void Window::OnDestroyInternal() { - WindowManager::GetInstance()->UnregisterWindow(hwnd_); - hwnd_ = nullptr; - if (delete_this_on_destroy_) - InvokeLater([this]{ delete this; }); - } +bool Window::IsMessageInQueue(UINT message) { + MSG msg; + return ::PeekMessageW(&msg, hwnd_, message, message, PM_NOREMOVE) != 0; +} - void Window::OnPaintInternal() { - render_target_->SetAsTarget(); +void Window::SetCursorInternal(HCURSOR cursor) { + if (IsWindowValid()) { + ::SetClassLongPtrW(GetWindowHandle(), GCLP_HCURSOR, + reinterpret_cast<LONG_PTR>(cursor)); + if (mouse_hover_control_ != nullptr) ::SetCursor(cursor); + } +} - auto device_context = render_target_->GetD2DDeviceContext(); +void Window::OnDestroyInternal() { + WindowManager::GetInstance()->UnregisterWindow(hwnd_); + hwnd_ = nullptr; + if (delete_this_on_destroy_) InvokeLater([this] { delete this; }); +} - device_context->BeginDraw(); +void Window::OnPaintInternal() { + render_target_->SetAsTarget(); - //Clear the background. - device_context->Clear(D2D1::ColorF(D2D1::ColorF::White)); + auto device_context = render_target_->GetD2DDeviceContext(); - Draw(device_context.Get()); + device_context->BeginDraw(); - ThrowIfFailed( - device_context->EndDraw(), "Failed to draw window." - ); + // Clear the background. + device_context->Clear(D2D1::ColorF(D2D1::ColorF::White)); - render_target_->Present(); + render_object_->Draw(device_context.Get()); - ValidateRect(hwnd_, nullptr); - } + ThrowIfFailed(device_context->EndDraw(), "Failed to draw window."); - void Window::OnResizeInternal(const int new_width, const int new_height) { - render_target_->ResizeBuffer(new_width, new_height); - if (!(new_width == 0 && new_height == 0)) - WindowInvalidateLayout(); - } + render_target_->Present(); - void Window::OnSetFocusInternal() - { - window_focus_ = true; - DispatchEvent(focus_control_, &Control::get_focus_event, nullptr, true); - } + ValidateRect(hwnd_, nullptr); +} - void Window::OnKillFocusInternal() - { - window_focus_ = false; - DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, true); - } +void Window::OnResizeInternal(const int new_width, const int new_height) { + render_target_->ResizeBuffer(new_width, new_height); + if (!(new_width == 0 && new_height == 0)) render_object_->MeasureAndLayout(); +} - void Window::OnMouseMoveInternal(const POINT point) - { - const auto dip_point = PiToDip(point); - - //when mouse was previous outside the window - if (mouse_hover_control_ == nullptr) { - //invoke TrackMouseEvent to have WM_MOUSELEAVE sent. - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof tme; - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hwnd_; - - TrackMouseEvent(&tme); - } - - //Find the first control that hit test succeed. - const auto new_control_mouse_hover = HitTest(dip_point); - const auto old_control_mouse_hover = mouse_hover_control_; - mouse_hover_control_ = new_control_mouse_hover; - - if (mouse_capture_control_) // if mouse is captured - { - DispatchEvent(mouse_capture_control_, &Control::mouse_move_event, nullptr, dip_point); - } - else - { - DispatchMouseHoverControlChangeEvent(old_control_mouse_hover, new_control_mouse_hover, dip_point); - DispatchEvent(new_control_mouse_hover, &Control::mouse_move_event, nullptr, dip_point); - } - } +void Window::OnSetFocusInternal() { + window_focus_ = true; + DispatchEvent(focus_control_, &Control::get_focus_event, nullptr, true); +} - void Window::OnMouseLeaveInternal() - { - DispatchEvent(mouse_hover_control_, &Control::mouse_leave_event, nullptr); - mouse_hover_control_ = nullptr; - } +void Window::OnKillFocusInternal() { + window_focus_ = false; + DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, true); +} - void Window::OnMouseDownInternal(MouseButton button, POINT point) - { - const auto dip_point = PiToDip(point); +void Window::OnMouseMoveInternal(const POINT point) { + const auto dip_point = PiToDip(point); + + // when mouse was previous outside the window + if (mouse_hover_control_ == nullptr) { + // invoke TrackMouseEvent to have WM_MOUSELEAVE sent. + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof tme; + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hwnd_; + + TrackMouseEvent(&tme); + } + + // Find the first control that hit test succeed. + const auto new_control_mouse_hover = HitTest(dip_point); + const auto old_control_mouse_hover = mouse_hover_control_; + mouse_hover_control_ = new_control_mouse_hover; + + if (mouse_capture_control_) // if mouse is captured + { + DispatchEvent(mouse_capture_control_, &Control::mouse_move_event, nullptr, + dip_point); + } else { + DispatchMouseHoverControlChangeEvent(old_control_mouse_hover, + new_control_mouse_hover, dip_point); + DispatchEvent(new_control_mouse_hover, &Control::mouse_move_event, nullptr, + dip_point); + } +} - Control* control; +void Window::OnMouseLeaveInternal() { + DispatchEvent(mouse_hover_control_, &Control::mouse_leave_event, nullptr); + mouse_hover_control_ = nullptr; +} - if (mouse_capture_control_) - control = mouse_capture_control_; - else - control = HitTest(dip_point); +void Window::OnMouseDownInternal(MouseButton button, POINT point) { + const auto dip_point = PiToDip(point); - DispatchEvent(control, &Control::mouse_down_event, nullptr, dip_point, button); - } + Control* control; - void Window::OnMouseUpInternal(MouseButton button, POINT point) - { - const auto dip_point = PiToDip(point); + if (mouse_capture_control_) + control = mouse_capture_control_; + else + control = HitTest(dip_point); - Control* control; + DispatchEvent(control, &Control::mouse_down_event, nullptr, dip_point, + button); +} - if (mouse_capture_control_) - control = mouse_capture_control_; - else - control = HitTest(dip_point); +void Window::OnMouseUpInternal(MouseButton button, POINT point) { + const auto dip_point = PiToDip(point); - DispatchEvent(control, &Control::mouse_up_event, nullptr, dip_point, button); - } + Control* control; - void Window::OnMouseWheelInternal(short delta, POINT point) - { - const auto dip_point = PiToDip(point); + if (mouse_capture_control_) + control = mouse_capture_control_; + else + control = HitTest(dip_point); - Control* control; + DispatchEvent(control, &Control::mouse_up_event, nullptr, dip_point, button); +} - if (mouse_capture_control_) - control = mouse_capture_control_; - else - control = HitTest(dip_point); +void Window::OnMouseWheelInternal(short delta, POINT point) { + const auto dip_point = PiToDip(point); - DispatchEvent(control, &Control::mouse_wheel_event, nullptr, dip_point, static_cast<float>(delta)); - } + Control* control; - void Window::OnKeyDownInternal(int virtual_code) - { - DispatchEvent(focus_control_, &Control::key_down_event, nullptr, virtual_code); - } + if (mouse_capture_control_) + control = mouse_capture_control_; + else + control = HitTest(dip_point); - void Window::OnKeyUpInternal(int virtual_code) - { - DispatchEvent(focus_control_, &Control::key_up_event, nullptr, virtual_code); - } + DispatchEvent(control, &Control::mouse_wheel_event, nullptr, dip_point, + static_cast<float>(delta)); +} - void Window::OnCharInternal(wchar_t c) - { - DispatchEvent(focus_control_, &Control::char_event, nullptr, c); - } +void Window::OnKeyDownInternal(int virtual_code) { + DispatchEvent(focus_control_, &Control::key_down_event, nullptr, + virtual_code); +} - void Window::OnActivatedInternal() - { - events::UiEventArgs args(this, this); - activated_event.Raise(args); - } +void Window::OnKeyUpInternal(int virtual_code) { + DispatchEvent(focus_control_, &Control::key_up_event, nullptr, virtual_code); +} - void Window::OnDeactivatedInternal() - { - events::UiEventArgs args(this, this); - deactivated_event.Raise(args); - } +void Window::OnCharInternal(wchar_t c) { + DispatchEvent(focus_control_, &Control::char_event, nullptr, c); +} - void Window::DispatchMouseHoverControlChangeEvent(Control* old_control, Control* new_control, const Point& point) - { - if (new_control != old_control) //if the mouse-hover-on control changed - { - const auto lowest_common_ancestor = FindLowestCommonAncestor(old_control, new_control); - if (old_control != nullptr) // if last mouse-hover-on control exists - DispatchEvent(old_control, &Control::mouse_leave_event, lowest_common_ancestor); // dispatch mouse leave event. - if (new_control != nullptr) - { - DispatchEvent(new_control, &Control::mouse_enter_event, lowest_common_ancestor, point); // dispatch mouse enter event. - UpdateCursor(); - } - } - } +void Window::OnActivatedInternal() { + events::UiEventArgs args(this, this); + activated_event.Raise(args); +} + +void Window::OnDeactivatedInternal() { + events::UiEventArgs args(this, this); + deactivated_event.Raise(args); +} + +void Window::DispatchMouseHoverControlChangeEvent(Control* old_control, + Control* new_control, + const Point& point) { + if (new_control != old_control) // if the mouse-hover-on control changed + { + const auto lowest_common_ancestor = + FindLowestCommonAncestor(old_control, new_control); + if (old_control != nullptr) // if last mouse-hover-on control exists + DispatchEvent(old_control, &Control::mouse_leave_event, + lowest_common_ancestor); // dispatch mouse leave event. + if (new_control != nullptr) { + DispatchEvent(new_control, &Control::mouse_enter_event, + lowest_common_ancestor, + point); // dispatch mouse enter event. + UpdateCursor(); + } + } } +} // namespace cru::ui diff --git a/src/ui/window.hpp b/src/ui/window.hpp index ac26de22..7a28c257 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -1,6 +1,4 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" #include <map> @@ -9,11 +7,16 @@ #include "control.hpp" #include "events/ui_event.hpp" +#include "window_class.hpp" namespace cru::graph { class WindowRenderTarget; } +namespace cru::ui::render { +class WindowRenderObject; +} + namespace cru::ui { class WindowManager : public Object { public: @@ -85,6 +88,8 @@ class Window final : public ContentControl { public: StringView GetControlType() const override final; + render::RenderObject* GetRenderObject() const override; + void SetDeleteThisOnDestroy(bool value); //*************** region: handle *************** @@ -105,7 +110,7 @@ class Window final : public ContentControl { // Send a repaint message to the window's message queue which may make the // window repaint. - void InvalidateDraw() override final; + void InvalidateDraw(); // Show the window. void Show(); @@ -146,14 +151,6 @@ class Window final : public ContentControl { Control* GetMouseHoverControl() const { return mouse_hover_control_; } - //*************** region: layout *************** - - void WindowInvalidateLayout(); - - void Relayout(); - - void SetSizeFitContent(const Size& max_size = Size(1000, 1000)); - //*************** region: focus *************** // Request focus for specified control. @@ -170,13 +167,6 @@ class Window final : public ContentControl { //*************** region: cursor *************** void UpdateCursor(); - //*************** region: debug *************** -#ifdef CRU_DEBUG_LAYOUT - bool IsDebugLayout() const { return debug_layout_; } - - void SetDebugLayout(bool value); -#endif - public: //*************** region: events *************** Event<events::UiEventArgs> activated_event; @@ -184,7 +174,12 @@ class Window final : public ContentControl { Event<events::WindowNativeMessageEventArgs> native_message_event; + protected: + void OnChildChanged(Control* old_child, Control* new_child) override; + private: + Control* HitTest(const Point& point); + //*************** region: native operations *************** // Get the client rect in pixel. @@ -229,6 +224,8 @@ class Window final : public ContentControl { Window* parent_window_ = nullptr; std::shared_ptr<graph::WindowRenderTarget> render_target_{}; + render::WindowRenderObject* render_object_; + Control* mouse_hover_control_ = nullptr; bool window_focus_ = false; |