From a410e2048db6f5ef6fb50e401a59b4b98b979050 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 4 Apr 2019 17:12:25 +0800 Subject: ... --- drafts/input_util.cpp | 20 ++ include/cru/common/ui_base.hpp | 16 ++ include/cru/platform/painter.hpp | 5 + include/cru/platform/text_layout.hpp | 10 +- include/cru/platform/win/win_painter.hpp | 5 + include/cru/platform/win/win_pre_config.hpp | 1 + include/cru/platform/win/win_text_layout.hpp | 2 + include/cru/ui/content_control.hpp | 29 +++ include/cru/ui/control.hpp | 137 +++++++++++++ include/cru/ui/layout_control.hpp | 31 +++ include/cru/ui/no_child_control.hpp | 24 +++ include/cru/ui/render/text_render_object.hpp | 60 +++--- include/cru/ui/ui_manager.hpp | 51 +++++ include/cru/ui/window.hpp | 97 +++++++++ src/CMakeLists.txt | 1 - src/platform_win/win_font.cpp | 3 + src/platform_win/win_painter.cpp | 31 +++ src/platform_win/win_text_layout.cpp | 13 ++ src/ui/content_control.cpp | 4 +- src/ui/content_control.hpp | 31 --- src/ui/control.cpp | 8 +- src/ui/control.hpp | 134 ------------- src/ui/input_util.cpp | 20 -- src/ui/input_util.hpp | 10 - src/ui/layout_control.cpp | 7 +- src/ui/layout_control.hpp | 33 ---- src/ui/no_child_control.cpp | 2 +- src/ui/no_child_control.hpp | 26 --- src/ui/render/text_render_object.cpp | 151 +++++--------- src/ui/ui_manager.cpp | 77 ++------ src/ui/ui_manager.hpp | 61 ------ src/ui/window.cpp | 283 +-------------------------- src/ui/window.hpp | 162 --------------- src/util/math_util.hpp | 51 ----- 34 files changed, 587 insertions(+), 1009 deletions(-) create mode 100644 drafts/input_util.cpp create mode 100644 include/cru/ui/content_control.hpp create mode 100644 include/cru/ui/control.hpp create mode 100644 include/cru/ui/layout_control.hpp create mode 100644 include/cru/ui/no_child_control.hpp create mode 100644 include/cru/ui/ui_manager.hpp create mode 100644 include/cru/ui/window.hpp delete mode 100644 src/ui/content_control.hpp delete mode 100644 src/ui/control.hpp delete mode 100644 src/ui/input_util.cpp delete mode 100644 src/ui/input_util.hpp delete mode 100644 src/ui/layout_control.hpp delete mode 100644 src/ui/no_child_control.hpp delete mode 100644 src/ui/ui_manager.hpp delete mode 100644 src/ui/window.hpp delete mode 100644 src/util/math_util.hpp diff --git a/drafts/input_util.cpp b/drafts/input_util.cpp new file mode 100644 index 00000000..193cba4a --- /dev/null +++ b/drafts/input_util.cpp @@ -0,0 +1,20 @@ +#include "input_util.hpp" + +#include + +namespace cru::ui { +bool IsKeyDown(const int virtual_code) { + const auto result = ::GetKeyState(virtual_code); + return (static_cast(result) & 0x8000) != 0; +} + +bool IsKeyToggled(const int virtual_code) { + const auto result = ::GetKeyState(virtual_code); + return (static_cast(result) & 1) != 0; +} + +bool IsAnyMouseButtonDown() { + return IsKeyDown(VK_LBUTTON) || IsKeyDown(VK_RBUTTON) || + IsKeyDown(VK_MBUTTON); +} +} // namespace cru::ui diff --git a/include/cru/common/ui_base.hpp b/include/cru/common/ui_base.hpp index 0da18f22..017e3bd1 100644 --- a/include/cru/common/ui_base.hpp +++ b/include/cru/common/ui_base.hpp @@ -233,9 +233,25 @@ struct TextRange final { }; struct Color { + constexpr Color() : Color(0, 0, 0, 255) {} + constexpr Color(std::uint8_t red, std::uint8_t green, std::uint8_t blue, + std::uint8_t alpha = 255) + : red(red), green(green), blue(blue), alpha(alpha) {} + + constexpr static Color FromHex(std::uint32_t hex) { + return Color(hex & (0b11111111 << 16), hex & (0b11111111 << 8), + hex & (0b11111111), hex & (0b11111111 << 24)); + } + std::uint8_t red; std::uint8_t green; std::uint8_t blue; std::uint8_t alpha; }; + +namespace colors { +constexpr Color black{0, 0, 0}; +constexpr Color white{255, 255, 255}; +constexpr Color skyblue = Color::FromHex(0x87ceeb); +} // namespace colors } // namespace cru::ui diff --git a/include/cru/platform/painter.hpp b/include/cru/platform/painter.hpp index 2e979184..7310bc5c 100644 --- a/include/cru/platform/painter.hpp +++ b/include/cru/platform/painter.hpp @@ -7,13 +7,18 @@ namespace cru::platform { struct Brush; struct Geometry; +struct TextLayout; struct Painter : virtual Interface { virtual Matrix GetTransform() = 0; virtual void SetTransform(const Matrix& matrix) = 0; + virtual void StrokeRectangle(const ui::Rect& rectangle, Brush* brush, + float width) = 0; + virtual void FillRectangle(const ui::Rect& rectangle, Brush* brush) = 0; virtual void StrokeGeometry(Geometry* geometry, Brush* brush, float width) = 0; virtual void FillGeometry(Geometry* geometry, Brush* brush) = 0; + virtual void DrawText(const ui::Point& offset, TextLayout* text_layout, Brush* brush); virtual void EndDraw() = 0; virtual bool IsDisposed() = 0; }; diff --git a/include/cru/platform/text_layout.hpp b/include/cru/platform/text_layout.hpp index 7bcf01fe..e43adfde 100644 --- a/include/cru/platform/text_layout.hpp +++ b/include/cru/platform/text_layout.hpp @@ -3,16 +3,22 @@ #include "cru/common/ui_base.hpp" -#include +#include #include +#include namespace cru::platform { +struct FontDescriptor; + struct TextLayout : virtual Interface { virtual std::wstring GetText() = 0; virtual void SetText(std::wstring new_text) = 0; + virtual std::shared_ptr GetFont() = 0; + virtual void SetFont(std::shared_ptr font) = 0; virtual void SetMaxWidth(float max_width) = 0; virtual void SetMaxHeight(float max_height) = 0; virtual ui::Rect GetTextBounds() = 0; - virtual std::vector TextRangeRect(const ui::TextRange& text_range) = 0; + virtual std::vector TextRangeRect( + const ui::TextRange& text_range) = 0; }; } // namespace cru::platform diff --git a/include/cru/platform/win/win_painter.hpp b/include/cru/platform/win/win_painter.hpp index 1e449d85..dfb981d5 100644 --- a/include/cru/platform/win/win_painter.hpp +++ b/include/cru/platform/win/win_painter.hpp @@ -17,8 +17,13 @@ class WinPainter : public Object, public virtual Painter { Matrix GetTransform() override; void SetTransform(const Matrix& matrix) override; + void StrokeRectangle(const ui::Rect& rectangle, Brush* brush, + float width) override; + void FillRectangle(const ui::Rect& rectangle, Brush* brush) override; void StrokeGeometry(Geometry* geometry, Brush* brush, float width) override; void FillGeometry(Geometry* geometry, Brush* brush) override; + void DrawText(const ui::Point& offset, TextLayout* text_layout, + Brush* brush) override; void EndDraw() override; bool IsDisposed() override { return is_disposed; } diff --git a/include/cru/platform/win/win_pre_config.hpp b/include/cru/platform/win/win_pre_config.hpp index d6ba4ec7..6962eb7b 100644 --- a/include/cru/platform/win/win_pre_config.hpp +++ b/include/cru/platform/win/win_pre_config.hpp @@ -4,6 +4,7 @@ #define WIN32_LEAN_AND_MEAN #include #undef CreateWindow +#undef DrawText #include #include diff --git a/include/cru/platform/win/win_text_layout.hpp b/include/cru/platform/win/win_text_layout.hpp index 277bbcae..9c93aa8c 100644 --- a/include/cru/platform/win/win_text_layout.hpp +++ b/include/cru/platform/win/win_text_layout.hpp @@ -21,6 +21,8 @@ class WinTextLayout : public Object, public virtual TextLayout { std::wstring GetText() override; void SetText(std::wstring new_text) override; + std::shared_ptr GetFont(); + void SetFont(std::shared_ptr font); void SetMaxWidth(float max_width) override; void SetMaxHeight(float max_height) override; ui::Rect GetTextBounds() override; diff --git a/include/cru/ui/content_control.hpp b/include/cru/ui/content_control.hpp new file mode 100644 index 00000000..f88ec157 --- /dev/null +++ b/include/cru/ui/content_control.hpp @@ -0,0 +1,29 @@ +#pragma once +#include "control.hpp" + +namespace cru::ui { +class ContentControl : public Control { + protected: + ContentControl(); + + public: + ContentControl(const ContentControl& other) = delete; + ContentControl(ContentControl&& other) = delete; + ContentControl& operator=(const ContentControl& other) = delete; + ContentControl& operator=(ContentControl&& other) = delete; + ~ContentControl() override; + + const std::vector& GetChildren() const override final { + return child_vector_; + } + Control* GetChild() const { return child_; } + void SetChild(Control* child); + + protected: + virtual void OnChildChanged(Control* old_child, Control* new_child); + + private: + std::vector child_vector_; + Control*& child_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/control.hpp b/include/cru/ui/control.hpp new file mode 100644 index 00000000..684af2f0 --- /dev/null +++ b/include/cru/ui/control.hpp @@ -0,0 +1,137 @@ +#pragma once +#include "cru/common/base.hpp" + +#include "cru/platform/basic_types.hpp" +#include "event/ui_event.hpp" + +#include +#include + +namespace cru::ui { +class Window; +namespace render { +class RenderObject; +} // namespace render + +class Control : public Object { + friend class Window; + + protected: + Control() = default; + + public: + Control(const Control& other) = delete; + Control(Control&& other) = delete; + Control& operator=(const Control& other) = delete; + Control& operator=(Control&& other) = delete; + ~Control() override = default; + + public: + virtual std::wstring_view GetControlType() const = 0; + + //*************** region: tree *************** + public: + // Get the window if attached, otherwise, return nullptr. + Window* GetWindow() const { return window_; } + + Control* GetParent() const { return parent_; } + + virtual const std::vector& GetChildren() const = 0; + + // Traverse the tree rooted the control including itself. + void TraverseDescendants(const std::function& predicate); + + void _SetParent(Control* parent); + void _SetDescendantWindow(Window* window); + + private: + static void _TraverseDescendants( + Control* control, const std::function& predicate); + + public: + virtual render::RenderObject* GetRenderObject() const = 0; + + //*************** region: focus *************** + public: + bool RequestFocus(); + + bool HasFocus(); + + //*************** region: events *************** + public: + // Raised when mouse enter the control. + event::RoutedEvent* MouseEnterEvent() { + return &mouse_enter_event_; + } + // Raised when mouse is leave the control. + event::RoutedEvent* MouseLeaveEvent() { + return &mouse_leave_event_; + } + // Raised when mouse is move in the control. + event::RoutedEvent* MouseMoveEvent() { + return &mouse_move_event_; + } + // Raised when a mouse button is pressed in the control. + event::RoutedEvent* MouseDownEvent() { + return &mouse_down_event_; + } + // Raised when a mouse button is released in the control. + event::RoutedEvent* MouseUpEvent() { + return &mouse_up_event_; + } + // Raised when a mouse button is pressed in the control and released in the + // control with mouse not leaving it between two operations. + event::RoutedEvent* MouseClickEvent() { + return &mouse_click_event_; + } + event::RoutedEvent* MouseWheelEvent() { + return &mouse_wheel_event_; + } + event::RoutedEvent* KeyDownEvent() { + return &key_down_event_; + } + event::RoutedEvent* KeyUpEvent() { + return &key_up_event_; + } + // event::RoutedEvent* CharEvent() { + // return &char_event_; + // } + event::RoutedEvent* GainFocusEvent() { + return &gain_focus_event_; + } + event::RoutedEvent* LoseFocusEvent() { + return &lose_focus_event_; + } + + private: + event::RoutedEvent mouse_enter_event_; + event::RoutedEvent mouse_leave_event_; + event::RoutedEvent mouse_move_event_; + event::RoutedEvent mouse_down_event_; + event::RoutedEvent mouse_up_event_; + event::RoutedEvent mouse_click_event_; + event::RoutedEvent mouse_wheel_event_; + + event::RoutedEvent key_down_event_; + event::RoutedEvent key_up_event_; + // event::RoutedEvent char_event_; + + event::RoutedEvent gain_focus_event_; + event::RoutedEvent lose_focus_event_; + + //*************** region: tree *************** + protected: + virtual void OnParentChanged(Control* old_parent, Control* new_parent); + virtual void OnAttachToWindow(Window* window); + virtual void OnDetachToWindow(Window* window); + + //*************** region: additional mouse event *************** + protected: + virtual void OnMouseClickBegin(platform::MouseButton button); + virtual void OnMouseClickEnd(platform::MouseButton button); + + private: + Window* window_ = nullptr; + Control* parent_ = nullptr; +}; +} // namespace cru::ui diff --git a/include/cru/ui/layout_control.hpp b/include/cru/ui/layout_control.hpp new file mode 100644 index 00000000..187f0e0d --- /dev/null +++ b/include/cru/ui/layout_control.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "control.hpp" + +namespace cru::ui { +class LayoutControl : public Control { + protected: + LayoutControl() = default; + + public: + LayoutControl(const LayoutControl& other) = delete; + LayoutControl(LayoutControl&& other) = delete; + LayoutControl& operator=(const LayoutControl& other) = delete; + LayoutControl& operator=(LayoutControl&& other) = delete; + ~LayoutControl() override; + + const std::vector& GetChildren() const override final { + return children_; + } + + void AddChild(Control* control, int position); + + void RemoveChild(int position); + + protected: + virtual void OnAddChild(Control* child, int position); + virtual void OnRemoveChild(Control* child, int position); + + private: + std::vector children_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/no_child_control.hpp b/include/cru/ui/no_child_control.hpp new file mode 100644 index 00000000..62a9fa8d --- /dev/null +++ b/include/cru/ui/no_child_control.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "control.hpp" + +namespace cru::ui { +class NoChildControl : public Control { + private: + static const std::vector empty_control_vector; + + protected: + NoChildControl() = default; + + public: + NoChildControl(const NoChildControl& other) = delete; + NoChildControl(NoChildControl&& other) = delete; + NoChildControl& operator=(const NoChildControl& other) = delete; + NoChildControl& operator=(NoChildControl&& other) = delete; + ~NoChildControl() override = default; + + protected: + const std::vector& GetChildren() const override final { + return empty_control_vector; + } +}; +} // namespace cru::ui diff --git a/include/cru/ui/render/text_render_object.hpp b/include/cru/ui/render/text_render_object.hpp index 7827f994..329af18a 100644 --- a/include/cru/ui/render/text_render_object.hpp +++ b/include/cru/ui/render/text_render_object.hpp @@ -1,35 +1,38 @@ #pragma once -#include "pre.hpp" - #include "render_object.hpp" +#include +#include + // forward declarations -struct ID2D1Brush; -struct IDWriteTextFormat; -struct IDWriteTextLayout; +namespace cru::platform { +struct Brush; +struct FontDescriptor; +struct TextLayout; +} // namespace cru::platform namespace cru::ui::render { class TextRenderObject : public RenderObject { public: - TextRenderObject(ID2D1Brush* brush, IDWriteTextFormat* format, - ID2D1Brush* selection_brush); + TextRenderObject(std::shared_ptr brush, + std::shared_ptr font, + std::shared_ptr selection_brush); TextRenderObject(const TextRenderObject& other) = delete; TextRenderObject(TextRenderObject&& other) = delete; TextRenderObject& operator=(const TextRenderObject& other) = delete; TextRenderObject& operator=(TextRenderObject&& other) = delete; ~TextRenderObject() override; - String GetText() const { return text_; } - void SetText(String new_text) { - text_ = std::move(new_text); - RecreateTextLayout(); - } + std::wstring GetText() const; + void SetText(std::wstring new_text); - ID2D1Brush* GetBrush() const { return brush_; } - void SetBrush(ID2D1Brush* new_brush); + std::shared_ptr GetBrush() const { return brush_; } + void SetBrush(std::shared_ptr new_brush) { + new_brush.swap(brush_); + } - IDWriteTextFormat* GetTextFormat() const { return text_format_; } - void SetTextFormat(IDWriteTextFormat* new_text_format); + std::shared_ptr GetFont() const; + void SetFont(std::shared_ptr font); std::optional GetSelectionRange() const { return selection_range_; @@ -38,12 +41,14 @@ class TextRenderObject : public RenderObject { selection_range_ = std::move(new_range); } - ID2D1Brush* GetSelectionBrush() const { return selection_brush_; } - void SetSelectionBrush(ID2D1Brush* new_brush); - - void Refresh() { RecreateTextLayout(); } + std::shared_ptr GetSelectionBrush() const { + return selection_brush_; + } + void SetSelectionBrush(std::shared_ptr new_brush) { + new_brush.swap(selection_brush_); + } - void Draw(ID2D1RenderTarget* render_target) override; + void Draw(platform::Painter* painter) override; RenderObject* HitTest(const Point& point) override; @@ -54,16 +59,11 @@ class TextRenderObject : public RenderObject { void OnLayoutContent(const Rect& content_rect) override; private: - void RecreateTextLayout(); - - private: - String text_; - - ID2D1Brush* brush_ = nullptr; - IDWriteTextFormat* text_format_ = nullptr; - IDWriteTextLayout* text_layout_ = nullptr; + std::shared_ptr brush_; + std::shared_ptr font_; + std::shared_ptr text_layout_; std::optional selection_range_ = std::nullopt; - ID2D1Brush* selection_brush_ = nullptr; + std::shared_ptr selection_brush_; }; } // namespace cru::ui::render diff --git a/include/cru/ui/ui_manager.hpp b/include/cru/ui/ui_manager.hpp new file mode 100644 index 00000000..6f4a3bc0 --- /dev/null +++ b/include/cru/ui/ui_manager.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "cru/common/base.hpp" + +#include + +namespace cru::platform { +struct Brush; +struct FontDescriptor; +} // namespace cru::platform + +namespace cru::ui { +class PredefineResources : public Object { + public: + PredefineResources(); + PredefineResources(const PredefineResources& other) = delete; + PredefineResources(PredefineResources&& other) = delete; + PredefineResources& operator=(const PredefineResources& other) = delete; + PredefineResources& operator=(PredefineResources&& other) = delete; + ~PredefineResources() override = default; + + // region Button + std::shared_ptr button_normal_border_brush; + + // region TextBlock + std::shared_ptr text_block_selection_brush; + std::shared_ptr text_block_text_brush; + std::shared_ptr text_block_font; +}; + +class UiManager : public Object { + public: + static UiManager* GetInstance(); + + private: + UiManager(); + + public: + UiManager(const UiManager& other) = delete; + UiManager(UiManager&& other) = delete; + UiManager& operator=(const UiManager& other) = delete; + UiManager& operator=(UiManager&& other) = delete; + ~UiManager() override = default; + + const PredefineResources* GetPredefineResources() const { + return predefine_resources_.get(); + } + + private: + std::unique_ptr predefine_resources_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/window.hpp b/include/cru/ui/window.hpp new file mode 100644 index 00000000..043aae35 --- /dev/null +++ b/include/cru/ui/window.hpp @@ -0,0 +1,97 @@ +#pragma once +#include "content_control.hpp" + +#include "event/ui_event.hpp" + +#include + +namespace cru::platform { +struct NativeWindow; +} + +namespace cru::ui { +namespace render { +class WindowRenderObject; +} + +class Window final : public ContentControl { + public: + static constexpr auto control_type = L"Window"; + + public: + static Window* CreateOverlapped(); + + private: + struct tag_overlapped_constructor {}; + + explicit Window(tag_overlapped_constructor); + + public: + Window(const Window& other) = delete; + Window(Window&& other) = delete; + Window& operator=(const Window& other) = delete; + Window& operator=(Window&& other) = delete; + ~Window() override; + + public: + std::wstring_view GetControlType() const override final; + + render::RenderObject* GetRenderObject() const override; + + platform::NativeWindow* GetNativeWindow() const; + + Control* GetMouseHoverControl() const { return mouse_hover_control_; } + + //*************** region: focus *************** + + // Request focus for specified control. + bool RequestFocusFor(Control* control); + + // Get the control that has focus. + Control* GetFocusControl(); + + protected: + void OnChildChanged(Control* old_child, Control* new_child) override; + + private: + Control* HitTest(const Point& point); + + //*************** region: native messages *************** + + void OnNativeDestroy(); + void OnNativePaint(); + void OnNativeResize(float new_width, float new_height); + + void OnSetFocusInternal(); + void OnKillFocusInternal(); + + void OnMouseMoveInternal(POINT point); + void OnMouseLeaveInternal(); + void OnMouseDownInternal(MouseButton button, POINT point); + void OnMouseUpInternal(MouseButton button, POINT point); + + void OnMouseWheelInternal(short delta, POINT point); + void OnKeyDownInternal(int virtual_code); + void OnKeyUpInternal(int virtual_code); + void OnCharInternal(wchar_t c); + + void OnActivatedInternal(); + void OnDeactivatedInternal(); + + //*************** region: event dispatcher helper *************** + + void DispatchMouseHoverControlChangeEvent(Control* old_control, + Control* new_control, + const Point& point); + + private: + platform::NativeWindow* native_window_; + std::vector revoke_tokens_; + + std::shared_ptr render_object_; + + Control* mouse_hover_control_ = nullptr; + + Control* focus_control_ = this; // "focus_control_" can't be nullptr +}; +} // namespace cru::ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8041d8cc..4155e800 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,6 @@ add_library(cru_ui STATIC ui/content_control.cpp ui/control.cpp - ui/input_util.cpp ui/layout_control.cpp ui/no_child_control.cpp ui/ui_manager.cpp diff --git a/src/platform_win/win_font.cpp b/src/platform_win/win_font.cpp index e0a309cc..bca70b9f 100644 --- a/src/platform_win/win_font.cpp +++ b/src/platform_win/win_font.cpp @@ -18,5 +18,8 @@ WinFontDescriptor::WinFontDescriptor(GraphManager* graph_manager, font_family.data(), nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, font_size, buffer.data(), &text_format_)); + + ThrowIfFailed(text_format_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); + ThrowIfFailed(text_format_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); } } // namespace cru::platform::win diff --git a/src/platform_win/win_painter.cpp b/src/platform_win/win_painter.cpp index 748d5766..b648f97d 100644 --- a/src/platform_win/win_painter.cpp +++ b/src/platform_win/win_painter.cpp @@ -6,6 +6,7 @@ #include "cru/platform/win/win_brush.hpp" #include "cru/platform/win/win_geometry.hpp" #include "cru/platform/win/win_native_window.hpp" +#include "cru/platform/win/win_text_layout.hpp" #include "cru/platform/win/window_render_target.hpp" #include @@ -28,15 +29,33 @@ WinPainter::~WinPainter() { } Matrix WinPainter::GetTransform() { + assert(!IsDisposed()); D2D1_MATRIX_3X2_F m; render_target_->GetTransform(&m); return util::Convert(m); } void WinPainter::SetTransform(const Matrix& matrix) { + assert(!IsDisposed()); render_target_->SetTransform(util::Convert(matrix)); } +void WinPainter::StrokeRectangle(const ui::Rect& rectangle, Brush* brush, + float width) { + assert(!IsDisposed()); + const auto b = dynamic_cast(brush); + assert(b); + render_target_->DrawRectangle(util::Convert(rectangle), b->GetD2DBrush(), + width); +} + +void WinPainter::FillRectangle(const ui::Rect& rectangle, Brush* brush) { + assert(!IsDisposed()); + const auto b = dynamic_cast(brush); + assert(b); + render_target_->FillRectangle(util::Convert(rectangle), b->GetD2DBrush()); +} + void WinPainter::StrokeGeometry(Geometry* geometry, Brush* brush, float width) { assert(!IsDisposed()); const auto g = dynamic_cast(geometry); @@ -57,6 +76,18 @@ void WinPainter::FillGeometry(Geometry* geometry, Brush* brush) { render_target_->FillGeometry(g->GetNative(), b->GetD2DBrush()); } +void WinPainter::DrawText(const ui::Point& offset, TextLayout* text_layout, + Brush* brush) { + assert(!IsDisposed()); + const auto t = dynamic_cast(text_layout); + assert(t); + const auto b = dynamic_cast(brush); + assert(b); + + render_target_->DrawTextLayout(util::Convert(offset), + t->GetDWriteTextLayout(), b->GetD2DBrush()); +} + void WinPainter::EndDraw() { if (!IsDisposed()) { ThrowIfFailed(render_target_->EndDraw()); diff --git a/src/platform_win/win_text_layout.cpp b/src/platform_win/win_text_layout.cpp index 9915b56c..7ae87a80 100644 --- a/src/platform_win/win_text_layout.cpp +++ b/src/platform_win/win_text_layout.cpp @@ -30,6 +30,19 @@ void WinTextLayout::SetText(std::wstring new_text) { max_width_, max_height_, &text_layout_)); } +std::shared_ptr WinTextLayout::GetFont() { + return font_descriptor_; +} + +void WinTextLayout::SetFont(std::shared_ptr font) { + auto f = std::dynamic_pointer_cast(font); + assert(f); + f.swap(font_descriptor_); + ThrowIfFailed(graph_manager_->GetDWriteFactory()->CreateTextLayout( + text_.c_str(), text_.size(), font_descriptor_->GetDWriteTextFormat(), + max_width_, max_height_, &text_layout_)); +} + void WinTextLayout::SetMaxWidth(float max_width) { max_width_ = max_width; ThrowIfFailed(text_layout_->SetMaxWidth(max_width_)); diff --git a/src/ui/content_control.cpp b/src/ui/content_control.cpp index d5abca1c..3a23164c 100644 --- a/src/ui/content_control.cpp +++ b/src/ui/content_control.cpp @@ -1,7 +1,9 @@ -#include "content_control.hpp" +#include "cru/ui/content_control.hpp" #include "window.hpp" +#include + namespace cru::ui { ContentControl::ContentControl() : child_vector_{nullptr}, child_(child_vector_[0]) {} diff --git a/src/ui/content_control.hpp b/src/ui/content_control.hpp deleted file mode 100644 index 88e7f60f..00000000 --- a/src/ui/content_control.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include "pre.hpp" - -#include "control.hpp" - -namespace cru::ui { -class ContentControl : public Control { - protected: - ContentControl(); - - public: - ContentControl(const ContentControl& other) = delete; - ContentControl(ContentControl&& other) = delete; - ContentControl& operator=(const ContentControl& other) = delete; - ContentControl& operator=(ContentControl&& other) = delete; - ~ContentControl() override; - - const std::vector& GetChildren() const override final { - return child_vector_; - } - Control* GetChild() const { return child_; } - void SetChild(Control* child); - - protected: - virtual void OnChildChanged(Control* old_child, Control* new_child); - - private: - std::vector child_vector_; - Control*& child_; -}; -} // namespace cru::ui diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 318d591a..c5d02d40 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -1,7 +1,9 @@ -#include "control.hpp" +#include "cru/ui/control.hpp" #include "window.hpp" +#include + namespace cru::ui { void Control::_SetParent(Control* parent) { const auto old_parent = GetParent(); @@ -61,7 +63,7 @@ void Control::OnAttachToWindow(Window* window) {} void Control::OnDetachToWindow(Window* window) {} -void Control::OnMouseClickBegin(MouseButton button) {} +void Control::OnMouseClickBegin(platform::MouseButton button) {} -void Control::OnMouseClickEnd(MouseButton button) {} +void Control::OnMouseClickEnd(platform::MouseButton button) {} } // namespace cru::ui diff --git a/src/ui/control.hpp b/src/ui/control.hpp deleted file mode 100644 index b69734d6..00000000 --- a/src/ui/control.hpp +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once -#include "cru/common/base.hpp" - -#include "cru/ui/event/ui_event.hpp" - -#include "" - -#include - -namespace cru::ui { -class Window; - -class Control : public Object { - friend class Window; - - protected: - Control() = default; - - public: - Control(const Control& other) = delete; - Control(Control&& other) = delete; - Control& operator=(const Control& other) = delete; - Control& operator=(Control&& other) = delete; - ~Control() override = default; - - public: - virtual std::wstring_view GetControlType() const = 0; - - //*************** region: tree *************** - public: - // Get the window if attached, otherwise, return nullptr. - Window* GetWindow() const { return window_; } - - Control* GetParent() const { return parent_; } - - virtual const std::vector& GetChildren() const = 0; - - // Traverse the tree rooted the control including itself. - void TraverseDescendants(const std::function& predicate); - - void _SetParent(Control* parent); - void _SetDescendantWindow(Window* window); - - private: - static void _TraverseDescendants( - Control* control, const std::function& predicate); - - public: - virtual render::RenderObject* GetRenderObject() const = 0; - - //*************** region: focus *************** - public: - bool RequestFocus(); - - bool HasFocus(); - - //*************** region: events *************** - public: - // Raised when mouse enter the control. - events::RoutedEvent* MouseEnterEvent() { - return &mouse_enter_event_; - } - // Raised when mouse is leave the control. - events::RoutedEvent* MouseLeaveEvent() { - return &mouse_leave_event_; - } - // Raised when mouse is move in the control. - events::RoutedEvent* MouseMoveEvent() { - return &mouse_move_event_; - } - // Raised when a mouse button is pressed in the control. - events::RoutedEvent* MouseDownEvent() { - return &mouse_down_event_; - } - // Raised when a mouse button is released in the control. - events::RoutedEvent* MouseUpEvent() { - return &mouse_up_event_; - } - // Raised when a mouse button is pressed in the control and released in the - // control with mouse not leaving it between two operations. - events::RoutedEvent* MouseClickEvent() { - return &mouse_click_event_; - } - events::RoutedEvent* MouseWheelEvent() { - return &mouse_wheel_event_; - } - events::RoutedEvent* KeyDownEvent() { - return &key_down_event_; - } - events::RoutedEvent* KeyUpEvent() { - return &key_up_event_; - } - events::RoutedEvent* CharEvent() { - return &char_event_; - } - events::RoutedEvent* GainFocusEvent() { - return &gain_focus_event_; - } - events::RoutedEvent* LoseFocusEvent() { - return &lose_focus_event_; - } - - private: - events::RoutedEvent mouse_enter_event_; - events::RoutedEvent mouse_leave_event_; - events::RoutedEvent mouse_move_event_; - events::RoutedEvent mouse_down_event_; - events::RoutedEvent mouse_up_event_; - events::RoutedEvent mouse_click_event_; - events::RoutedEvent mouse_wheel_event_; - - events::RoutedEvent key_down_event_; - events::RoutedEvent key_up_event_; - events::RoutedEvent char_event_; - - events::RoutedEvent gain_focus_event_; - events::RoutedEvent lose_focus_event_; - - //*************** region: tree *************** - protected: - virtual void OnParentChanged(Control* old_parent, Control* new_parent); - virtual void OnAttachToWindow(Window* window); - virtual void OnDetachToWindow(Window* window); - - //*************** region: additional mouse event *************** - protected: - virtual void OnMouseClickBegin(MouseButton button); - virtual void OnMouseClickEnd(MouseButton button); - - private: - Window* window_ = nullptr; - Control* parent_ = nullptr; -}; -} // namespace cru::ui diff --git a/src/ui/input_util.cpp b/src/ui/input_util.cpp deleted file mode 100644 index 193cba4a..00000000 --- a/src/ui/input_util.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "input_util.hpp" - -#include - -namespace cru::ui { -bool IsKeyDown(const int virtual_code) { - const auto result = ::GetKeyState(virtual_code); - return (static_cast(result) & 0x8000) != 0; -} - -bool IsKeyToggled(const int virtual_code) { - const auto result = ::GetKeyState(virtual_code); - return (static_cast(result) & 1) != 0; -} - -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 deleted file mode 100644 index 2d01f725..00000000 --- a/src/ui/input_util.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include "pre.hpp" - -namespace cru::ui { -enum class MouseButton { Left, Right, Middle }; - -bool IsKeyDown(int virtual_code); -bool IsKeyToggled(int virtual_code); -bool IsAnyMouseButtonDown(); -} // namespace cru::ui diff --git a/src/ui/layout_control.cpp b/src/ui/layout_control.cpp index c0c4a7fe..9d789670 100644 --- a/src/ui/layout_control.cpp +++ b/src/ui/layout_control.cpp @@ -1,7 +1,9 @@ -#include "layout_control.hpp" +#include "cru/ui/layout_control.hpp" #include "window.hpp" +#include + namespace cru::ui { LayoutControl::~LayoutControl() { for (const auto child : children_) delete child; @@ -10,7 +12,8 @@ LayoutControl::~LayoutControl() { void LayoutControl::AddChild(Control* control, const int position) { assert(control->GetParent() == nullptr); // The control already has a parent. assert(!dynamic_cast(control)); // Can't add a window as child. - assert(position >= 0 || position <= this->children_.size()); // The position is out of range. + assert(position >= 0 || + position <= this->children_.size()); // The position is out of range. children_.insert(this->children_.cbegin() + position, control); diff --git a/src/ui/layout_control.hpp b/src/ui/layout_control.hpp deleted file mode 100644 index 53f53186..00000000 --- a/src/ui/layout_control.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include "pre.hpp" - -#include "control.hpp" - -namespace cru::ui { -class LayoutControl : public Control { - protected: - LayoutControl() = default; - - public: - LayoutControl(const LayoutControl& other) = delete; - LayoutControl(LayoutControl&& other) = delete; - LayoutControl& operator=(const LayoutControl& other) = delete; - LayoutControl& operator=(LayoutControl&& other) = delete; - ~LayoutControl() override; - - const std::vector& GetChildren() const override final { - return children_; - } - - void AddChild(Control* control, int position); - - void RemoveChild(int position); - - protected: - virtual void OnAddChild(Control* child, int position); - virtual void OnRemoveChild(Control* child, int position); - - private: - std::vector children_; -}; -} // namespace cru::ui diff --git a/src/ui/no_child_control.cpp b/src/ui/no_child_control.cpp index e6bbe813..81299411 100644 --- a/src/ui/no_child_control.cpp +++ b/src/ui/no_child_control.cpp @@ -1,4 +1,4 @@ -#include "no_child_control.hpp" +#include "cru/ui/no_child_control.hpp" namespace cru::ui { const std::vector NoChildControl::empty_control_vector{}; diff --git a/src/ui/no_child_control.hpp b/src/ui/no_child_control.hpp deleted file mode 100644 index 26b5546f..00000000 --- a/src/ui/no_child_control.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "pre.hpp" - -#include "control.hpp" - -namespace cru::ui { -class NoChildControl : public Control { - private: - static const std::vector empty_control_vector; - - protected: - NoChildControl() = default; - - public: - NoChildControl(const NoChildControl& other) = delete; - NoChildControl(NoChildControl&& other) = delete; - NoChildControl& operator=(const NoChildControl& other) = delete; - NoChildControl& operator=(NoChildControl&& other) = delete; - ~NoChildControl() override = default; - - protected: - const std::vector& GetChildren() const override final { - return empty_control_vector; - } -}; -} // namespace cru::ui diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp index 69563ad7..bdf48c9a 100644 --- a/src/ui/render/text_render_object.cpp +++ b/src/ui/render/text_render_object.cpp @@ -1,103 +1,61 @@ -#include "text_render_object.hpp" +#include "cru/ui/render/text_render_object.hpp" -#include -#include -#include +#include "cru/platform/graph_factory.hpp" +#include "cru/platform/painter_util.hpp" +#include "cru/platform/text_layout.hpp" +#include "cru/platform/ui_applicaition.hpp" -#include "exception.hpp" -#include "graph/graph_manager.hpp" -#include "graph/graph_util.hpp" -#include "util/com_util.hpp" +#include +#include namespace cru::ui::render { -TextRenderObject::TextRenderObject(ID2D1Brush* brush, IDWriteTextFormat* format, - ID2D1Brush* selection_brush) { +TextRenderObject::TextRenderObject( + std::shared_ptr brush, + std::shared_ptr font, + std::shared_ptr selection_brush) { assert(brush); - assert(format); + assert(font); assert(selection_brush); - brush->AddRef(); - format->AddRef(); - selection_brush->AddRef(); - this->brush_ = brush; - this->text_format_ = format; - this->selection_brush_ = selection_brush; - try { - RecreateTextLayout(); - } catch (...) { - brush->Release(); - format->Release(); - selection_brush->Release(); - throw; - } -} -TextRenderObject::~TextRenderObject() { - util::SafeRelease(brush_); - util::SafeRelease(text_format_); - util::SafeRelease(text_layout_); - util::SafeRelease(selection_brush_); -} + brush.swap(brush_); + font.swap(font_); + selection_brush.swap(selection_brush_); -void TextRenderObject::SetBrush(ID2D1Brush* new_brush) { - assert(new_brush); - util::SafeRelease(brush_); - new_brush->AddRef(); - brush_ = new_brush; -} + const auto graph_factory = + platform::UiApplication::GetInstance()->GetGraphFactory(); -void TextRenderObject::SetTextFormat(IDWriteTextFormat* new_text_format) { - assert(new_text_format); - util::SafeRelease(text_format_); - new_text_format->AddRef(); - text_format_ = new_text_format; - RecreateTextLayout(); + text_layout_.reset(graph_factory->CreateTextLayout(font_, L"")); } -void TextRenderObject::SetSelectionBrush(ID2D1Brush* new_brush) { - assert(new_brush); - util::SafeRelease(selection_brush_); - new_brush->AddRef(); - selection_brush_ = new_brush; +std::wstring TextRenderObject::GetText() const { + return text_layout_->GetText(); } -namespace { -void DrawSelectionRect(ID2D1RenderTarget* render_target, - IDWriteTextLayout* layout, ID2D1Brush* brush, - const std::optional range) { - if (range.has_value()) { - DWRITE_TEXT_METRICS text_metrics{}; - ThrowIfFailed(layout->GetMetrics(&text_metrics)); - const auto metrics_count = - text_metrics.lineCount * text_metrics.maxBidiReorderingDepth; - - std::vector hit_test_metrics(metrics_count); - UINT32 actual_count; - layout->HitTestTextRange(range.value().position, range.value().count, 0, 0, - hit_test_metrics.data(), metrics_count, - &actual_count); +void TextRenderObject::SetText(std::wstring new_text) { + text_layout_->SetText(std::move(new_text)); +} - hit_test_metrics.erase(hit_test_metrics.cbegin() + actual_count, - hit_test_metrics.cend()); +std::shared_ptr TextRenderObject::GetFont() const { + return text_layout_->GetFont(); +} - for (const auto& metrics : hit_test_metrics) - render_target->FillRoundedRectangle( - D2D1::RoundedRect(D2D1::RectF(metrics.left, metrics.top, - metrics.left + metrics.width, - metrics.top + metrics.height), - 3, 3), - brush); - } +void TextRenderObject::SetFont(std::shared_ptr font) { + text_layout_->SetFont(std::move(font)); } -} // namespace -void TextRenderObject::Draw(ID2D1RenderTarget* render_target) { - graph::WithTransform( - render_target, - D2D1::Matrix3x2F::Translation(GetMargin().left + GetPadding().left, +void TextRenderObject::Draw(platform::Painter* painter) { + platform::util::WithTransform( + painter, + platform::Matrix::Translation(GetMargin().left + GetPadding().left, GetMargin().top + GetPadding().top), - [this](auto rt) { - DrawSelectionRect(rt, text_layout_, selection_brush_, selection_range_); - rt->DrawTextLayout(D2D1::Point2F(), text_layout_, brush_); + [this](platform::Painter* p) { + if (this->selection_range_.has_value()) { + const auto&& rects = + text_layout_->TextRangeRect(this->selection_range_.value()); + for (const auto& rect : rects) + p->FillRectangle(rect, this->GetSelectionBrush().get()); + } + p->DrawText(Point{}, text_layout_.get(), brush_.get()); }); } @@ -115,34 +73,15 @@ RenderObject* TextRenderObject::HitTest(const Point& point) { void TextRenderObject::OnSizeChanged(const Size& old_size, const Size& new_size) { const auto&& size = GetContentRect().GetSize(); - ThrowIfFailed(text_layout_->SetMaxWidth(size.width)); - ThrowIfFailed(text_layout_->SetMaxHeight(size.height)); + text_layout_->SetMaxWidth(size.width); + text_layout_->SetMaxHeight(size.height); } Size TextRenderObject::OnMeasureContent(const Size& available_size) { - ThrowIfFailed(text_layout_->SetMaxWidth(available_size.width)); - ThrowIfFailed(text_layout_->SetMaxHeight(available_size.height)); - - DWRITE_TEXT_METRICS metrics; - ThrowIfFailed(text_layout_->GetMetrics(&metrics)); - - return Size(metrics.width, metrics.height); + text_layout_->SetMaxWidth(available_size.width); + text_layout_->SetMaxHeight(available_size.height); + return text_layout_->GetTextBounds().GetSize(); } void TextRenderObject::OnLayoutContent(const Rect& content_rect) {} - -void TextRenderObject::RecreateTextLayout() { - assert(text_format_ != nullptr); - - util::SafeRelease(text_layout_); - - const auto dwrite_factory = - graph::GraphManager::GetInstance()->GetDWriteFactory(); - - const auto&& size = GetContentRect().GetSize(); - - ThrowIfFailed(dwrite_factory->CreateTextLayout( - text_.c_str(), static_cast(text_.size()), text_format_, - size.width, size.height, &text_layout_)); -} } // namespace cru::ui::render diff --git a/src/ui/ui_manager.cpp b/src/ui/ui_manager.cpp index 4d14575b..9c3c00d2 100644 --- a/src/ui/ui_manager.cpp +++ b/src/ui/ui_manager.cpp @@ -1,74 +1,27 @@ -#include "ui_manager.hpp" +#include "cru/ui/ui_manager.hpp" -#include -#include -#include - -#include "application.hpp" -#include "exception.hpp" -#include "graph/graph_manager.hpp" -#include "graph/graph_util.hpp" -#include "util/com_util.hpp" +#include "cru/platform/graph_factory.hpp" +#include "cru/platform/ui_applicaition.hpp" namespace cru::ui { -namespace { -void GetSystemCaretInfo(CaretInfo* caret_info) { - caret_info->caret_blink_duration = - std::chrono::milliseconds(::GetCaretBlinkTime()); - DWORD caret_width; - if (!::SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caret_width, 0)) - throw Win32Error(::GetLastError(), "Failed to get system caret width."); - caret_info->half_caret_width = caret_width / 2.0f; -} - -IDWriteTextFormat* CreateDefaultTextFormat() { - const auto dwrite_factory = - graph::GraphManager::GetInstance()->GetDWriteFactory(); - IDWriteTextFormat* text_format; - - ThrowIfFailed(dwrite_factory->CreateTextFormat( - L"等线", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, 24.0, L"zh-cn", &text_format)); - - ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); - ThrowIfFailed( - text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); - - return text_format; -} -} // namespace - PredefineResources::PredefineResources() { - try { - button_normal_border_brush = - graph::CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black)); + const auto graph_factory = + platform::UiApplication::GetInstance()->GetGraphFactory(); - text_block_selection_brush = - graph::CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::LightSkyBlue)); - text_block_text_brush = - graph::CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black)); - text_block_text_format = CreateDefaultTextFormat(); - } catch (...) { - util::SafeRelease(button_normal_border_brush); - util::SafeRelease(text_block_selection_brush); - util::SafeRelease(text_block_text_brush); - util::SafeRelease(text_block_text_format); - } -} + button_normal_border_brush.reset( + graph_factory->CreateSolidColorBrush(colors::black)); -PredefineResources::~PredefineResources() { - util::SafeRelease(button_normal_border_brush); - util::SafeRelease(text_block_selection_brush); - util::SafeRelease(text_block_text_brush); - util::SafeRelease(text_block_text_format); + text_block_selection_brush.reset( + graph_factory->CreateSolidColorBrush(colors::skyblue)); + text_block_text_brush.reset( + graph_factory->CreateSolidColorBrush(colors::black)); + text_block_font.reset(graph_factory->CreateFontDescriptor(L"等线", 24.0f)); } UiManager* UiManager::GetInstance() { - return Application::GetInstance()->ResolveSingleton( - [](auto) { return new UiManager{}; }); + static UiManager instance; + return &instance; } -UiManager::UiManager() : predefine_resources_() { - GetSystemCaretInfo(&caret_info_); -} +UiManager::UiManager() : predefine_resources_(new PredefineResources()) {} } // namespace cru::ui diff --git a/src/ui/ui_manager.hpp b/src/ui/ui_manager.hpp deleted file mode 100644 index 107b536c..00000000 --- a/src/ui/ui_manager.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once -#include "pre.hpp" - -#include "base.hpp" - -struct ID2D1Brush; -struct IDWriteTextFormat; -namespace cru::graph { -class GraphManager; -} - -namespace cru::ui { -struct CaretInfo { - std::chrono::milliseconds caret_blink_duration; - float half_caret_width; -}; - -class PredefineResources : public Object { - public: - PredefineResources(); - PredefineResources(const PredefineResources& other) = delete; - PredefineResources(PredefineResources&& other) = delete; - PredefineResources& operator=(const PredefineResources& other) = delete; - PredefineResources& operator=(PredefineResources&& other) = delete; - ~PredefineResources() override; - - // region Button - ID2D1Brush* button_normal_border_brush = nullptr; - - // region TextBlock - ID2D1Brush* text_block_selection_brush = nullptr; - ID2D1Brush* text_block_text_brush = nullptr; - IDWriteTextFormat* text_block_text_format = nullptr; -}; - -class UiManager : public Object { - public: - static UiManager* GetInstance(); - - private: - UiManager(); - - public: - UiManager(const UiManager& other) = delete; - UiManager(UiManager&& other) = delete; - UiManager& operator=(const UiManager& other) = delete; - UiManager& operator=(UiManager&& other) = delete; - ~UiManager() override = default; - - CaretInfo GetCaretInfo() const { return caret_info_; } - - const PredefineResources* GetPredefineResources() const { - return &predefine_resources_; - } - - private: - CaretInfo caret_info_; - - PredefineResources predefine_resources_; -}; -} // namespace cru::ui diff --git a/src/ui/window.cpp b/src/ui/window.cpp index d138424f..1065a3fb 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -1,14 +1,10 @@ -#include "window.hpp" +#include "cru/ui/window.hpp" -#include -#include +#include "cru/ui/render/window_render_object.hpp" +#include "cru/platform/ui_applicaition.hpp" +#include "cru/platform/native_window.hpp" -#include "application.hpp" -#include "exception.hpp" -#include "graph/graph_manager.hpp" -#include "graph/graph_util.hpp" -#include "graph/window_render_target.hpp" -#include "render/window_render_object.hpp" +#include namespace cru::ui { namespace { @@ -27,7 +23,7 @@ namespace { // as the rest arguments. template void DispatchEvent(Control* const original_sender, - events::RoutedEvent* (Control::*event_ptr)(), + event::RoutedEvent* (Control::*event_ptr)(), Control* const last_receiver, Args&&... args) { std::list receive_list; @@ -97,232 +93,31 @@ Control* FindLowestCommonAncestor(Control* left, Control* right) { } } // namespace - -inline Point PiToDip(const POINT& pi_point) { - return Point(graph::PixelToDipX(pi_point.x), graph::PixelToDipY(pi_point.y)); -} - -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; -} - Window* Window::CreateOverlapped() { return new Window(tag_overlapped_constructor{}); } -Window* Window::CreatePopup(Window* parent, const bool caption) { - return new Window(tag_popup_constructor{}, parent, caption); -} Window::Window(tag_overlapped_constructor) { - BeforeCreateHwnd(); - - const auto window_manager = WindowManager::GetInstance(); - - 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 Win32Error(::GetLastError(), "Failed to create window."); - - AfterCreateHwnd(window_manager); -} - -Window::Window(tag_popup_constructor, Window* parent, const bool caption) { - assert(parent == nullptr || - parent->IsWindowValid()); // Parent window is not valid. - - BeforeCreateHwnd(); - - parent_window_ = parent; - - 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); - - if (hwnd_ == nullptr) - throw Win32Error(::GetLastError(), "Failed to create window."); - - AfterCreateHwnd(window_manager); -} - -void Window::BeforeCreateHwnd() { window_ = this; } - -void Window::AfterCreateHwnd(WindowManager* window_manager) { - window_manager->RegisterWindow(hwnd_, this); - - render_target_.reset( - new graph::WindowRenderTarget(graph::GraphManager::GetInstance(), hwnd_)); - + native_window_ = platform::UiApplication::GetInstance()->CreateWindow(nullptr); render_object_.reset(new render::WindowRenderObject(this)); } Window::~Window() { - TraverseDescendants( [this](Control* control) { control->OnDetachToWindow(this); }); } -StringView Window::GetControlType() const { return control_type; } +std::wstring_view Window::GetControlType() const { return control_type; } render::RenderObject* Window::GetRenderObject() const { return render_object_.get(); } -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(w_param)); - result = 0; - return true; - case WM_KEYUP: - OnKeyUpInternal(static_cast(w_param)); - result = 0; - return true; - case WM_CHAR: - OnCharInternal(static_cast(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; - } -} - -Point Window::GetMousePosition() { - if (!IsWindowValid()) return Point::Zero(); - POINT point; - ::GetCursorPos(&point); - ::ScreenToClient(hwnd_, &point); - return PiToDip(point); -} - bool Window::RequestFocusFor(Control* control) { assert(control != nullptr); // The control to request focus can't be null. // You can set it as the window. - if (!IsWindowValid()) return false; - - if (!window_focus_) { - focus_control_ = control; - ::SetFocus(hwnd_); - return true; // event dispatch will be done in window message handling - // function "OnSetFocusInternal". - } - if (focus_control_ == control) return true; DispatchEvent(focus_control_, &Control::LoseFocusEvent, nullptr, false); @@ -336,41 +131,6 @@ bool Window::RequestFocusFor(Control* control) { 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; - } -} - -#ifdef CRU_DEBUG_LAYOUT -void Window::SetDebugLayout(const bool value) { - if (debug_layout_ != value) { - debug_layout_ = value; - InvalidateDraw(); - } -} -#endif - 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); @@ -380,32 +140,9 @@ Control* Window::HitTest(const Point& point) { return render_object_->HitTest(point)->GetAttachedControl(); } -RECT Window::GetClientRectPixel() { - RECT rect{}; - GetClientRect(hwnd_, &rect); - return rect; -} - -bool Window::IsMessageInQueue(UINT message) { - MSG msg; - return ::PeekMessageW(&msg, hwnd_, message, message, PM_NOREMOVE) != 0; -} - -void Window::SetCursorInternal(HCURSOR cursor) { - if (IsWindowValid()) { - ::SetClassLongPtrW(GetWindowHandle(), GCLP_HCURSOR, - reinterpret_cast(cursor)); - if (mouse_hover_control_ != nullptr) ::SetCursor(cursor); - } -} - -void Window::OnDestroyInternal() { - WindowManager::GetInstance()->UnregisterWindow(hwnd_); - hwnd_ = nullptr; - if (delete_this_on_destroy_) InvokeLater([this] { delete this; }); -} +void Window::OnNativeDestroy() { delete this; } -void Window::OnPaintInternal() { +void Window::OnNativePaint() { render_target_->SetAsTarget(); auto device_context = diff --git a/src/ui/window.hpp b/src/ui/window.hpp deleted file mode 100644 index f38743dc..00000000 --- a/src/ui/window.hpp +++ /dev/null @@ -1,162 +0,0 @@ -#pragma once -#include "cru/common/pre_config.hpp" - -#include -#include - -#include "content_control.hpp" -#include "events/ui_event.hpp" -#include "window_class.hpp" - - -namespace cru::ui { - - -class Window final : public ContentControl { - public: - static constexpr auto control_type = L"Window"; - - public: - static Window* CreateOverlapped(); - static Window* CreatePopup(Window* parent, bool caption = false); - - private: - struct tag_overlapped_constructor {}; - struct tag_popup_constructor {}; - - explicit Window(tag_overlapped_constructor); - Window(tag_popup_constructor, Window* parent, bool caption); - - void BeforeCreateHwnd(); - void AfterCreateHwnd(WindowManager* window_manager); - - public: - Window(const Window& other) = delete; - Window(Window&& other) = delete; - Window& operator=(const Window& other) = delete; - Window& operator=(Window&& other) = delete; - ~Window() override; - - public: - StringView GetControlType() const override final; - - render::RenderObject* GetRenderObject() const override; - - - - - //*************** region: window operations *************** - - // Get the client size. - Size GetClientSize(); - - // Set the client size and repaint. - void SetClientSize(const Size& size); - - // Get the rect of the window containing frame. - // The lefttop of the rect is relative to screen lefttop. - Rect GetWindowRect(); - - // Set the rect of the window containing frame. - // The lefttop of the rect is relative to screen lefttop. - void SetWindowRect(const Rect& rect); - - // Set the lefttop of the window relative to screen. - void SetWindowPosition(const Point& position); - - Point PointToScreen(const Point& point); - - Point PointFromScreen(const Point& point); - - // Handle the raw window message. - // Return true if the message is handled and get the result through "result" - // argument. Return false if the message is not handled. - bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, - LRESULT& result); - - //*************** region: mouse *************** - - Point GetMousePosition(); - - Control* GetMouseHoverControl() const { return mouse_hover_control_; } - - //*************** region: focus *************** - - // Request focus for specified control. - bool RequestFocusFor(Control* control); - - // Get the control that has focus. - Control* GetFocusControl(); - - //*************** region: mouse capture *************** - - Control* CaptureMouseFor(Control* control); - Control* ReleaseCurrentMouseCapture(); - - //*************** region: events *************** - public: - Event* ActivatedEvent() { return &activated_event_; } - Event* DeactivatedEvent() { return &deactivated_event_; } - Event* NativeMessageEvent() { - return &native_message_event_; - } - - private: - Event activated_event_; - Event deactivated_event_; - Event 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. - RECT GetClientRectPixel(); - - bool IsMessageInQueue(UINT message); - - void SetCursorInternal(HCURSOR cursor); - - //*************** region: native messages *************** - - void OnDestroyInternal(); - void OnPaintInternal(); - void OnResizeInternal(int new_width, int new_height); - - void OnSetFocusInternal(); - void OnKillFocusInternal(); - - void OnMouseMoveInternal(POINT point); - void OnMouseLeaveInternal(); - void OnMouseDownInternal(MouseButton button, POINT point); - void OnMouseUpInternal(MouseButton button, POINT point); - - void OnMouseWheelInternal(short delta, POINT point); - void OnKeyDownInternal(int virtual_code); - void OnKeyUpInternal(int virtual_code); - void OnCharInternal(wchar_t c); - - void OnActivatedInternal(); - void OnDeactivatedInternal(); - - //*************** region: event dispatcher helper *************** - - void DispatchMouseHoverControlChangeEvent(Control* old_control, - Control* new_control, - const Point& point); - - private: - std::shared_ptr render_target_{}; - std::shared_ptr render_object_{}; - - Control* mouse_hover_control_ = nullptr; - - bool window_focus_ = false; - Control* focus_control_ = this; // "focus_control_" can't be nullptr - Control* mouse_capture_control_ = nullptr; -}; -} // namespace cru::ui diff --git a/src/util/math_util.hpp b/src/util/math_util.hpp deleted file mode 100644 index 01348641..00000000 --- a/src/util/math_util.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include "pre.hpp" - -#include -#include - -namespace cru::util { -template >> -float Coerce(const T n, const std::optional min, - const std::optional max) { - if (min.has_value() && n < min.value()) return min.value(); - if (max.has_value() && n > max.value()) return max.value(); - return n; -} - -template >> -float Coerce(const T n, const T min, const T max) { - if (n < min) return min; - if (n > max) return max; - return n; -} - -template >> -float Coerce(const T n, const std::nullopt_t, const std::optional max) { - if (max.has_value() && n > max.value()) return max.value(); - return n; -} - -template >> -float Coerce(const T n, const std::optional min, const std::nullopt_t) { - if (min.has_value() && n < min.value()) return min.value(); - return n; -} - -template >> -float Coerce(const T n, const std::nullopt_t, const T max) { - if (n > max) return max; - return n; -} - -template >> -float Coerce(const T n, const T min, const std::nullopt_t) { - if (n < min) return min; - return n; -} - -template >> -T AtLeast0(const T value) { - return value < static_cast(0) ? static_cast(0) : value; -} -} // namespace cru::util -- cgit v1.2.3