aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp239
-rw-r--r--src/pre.hpp4
-rw-r--r--src/ui/control.hpp2
-rw-r--r--src/ui/controls/flex_layout.cpp4
-rw-r--r--src/ui/controls/flex_layout.hpp10
-rw-r--r--src/ui/controls/text_block.cpp4
-rw-r--r--src/ui/controls/text_block.hpp10
-rw-r--r--src/ui/d2d_util.hpp158
-rw-r--r--src/ui/events/ui_event.cpp6
-rw-r--r--src/ui/events/ui_event.hpp507
-rw-r--r--src/ui/input_util.cpp29
-rw-r--r--src/ui/input_util.hpp20
-rw-r--r--src/ui/render/flex_layout_render_object.cpp2
-rw-r--r--src/ui/render/render_object.cpp5
-rw-r--r--src/ui/render/render_object.hpp8
-rw-r--r--src/ui/render/text_render_object.cpp6
-rw-r--r--src/ui/render/text_render_object.hpp2
-rw-r--r--src/ui/render/window_render_object.cpp48
-rw-r--r--src/ui/render/window_render_object.hpp40
-rw-r--r--src/ui/window.cpp1194
-rw-r--r--src/ui/window.hpp33
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;