From 3ec1e536bd0ced5abcf82e84d1eee42960912b37 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 8 Dec 2018 22:51:48 +0800 Subject: Add single render objects. --- src/ui/render/render_object.cpp | 121 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/ui/render/render_object.cpp (limited to 'src/ui/render/render_object.cpp') diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp new file mode 100644 index 00000000..d84d1d8b --- /dev/null +++ b/src/ui/render/render_object.cpp @@ -0,0 +1,121 @@ +#include "render_object.hpp" +#include + +namespace cru::ui::render +{ + void RenderObject::SetRenderHost(IRenderHost* new_render_host) + { + if (new_render_host == render_host_) + return; + + const auto old = render_host_; + render_host_ = new_render_host; + OnRenderHostChanged(old, new_render_host); + } + + void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, IRenderHost* new_render_host) + { + + } + + void RenderObject::InvalidateRenderHost() + { + if (render_host_ != nullptr) + render_host_->InvalidateRender(); + } + + SingleChildRenderObject::~SingleChildRenderObject() + { + delete child_; + } + + void SingleChildRenderObject::SetChild(RenderObject* new_child) + { + const auto old = child_; + if (old) + old->SetRenderHost(nullptr); + child_ = new_child; + if (new_child) + new_child->SetRenderHost(GetRenderHost()); + OnChildChange(old, new_child); + InvalidateRenderHost(); + } + + void SingleChildRenderObject::OnRenderHostChanged(IRenderHost* old_render_host, IRenderHost* new_render_host) + { + if (child_) + child_->SetRenderHost(new_render_host); + } + + void SingleChildRenderObject::OnChildChange(RenderObject* old_child, RenderObject* new_object) + { + + } + + ClipRenderObject::ClipRenderObject(Microsoft::WRL::ComPtr clip_geometry) + : clip_geometry_(std::move(clip_geometry)) + { + + } + + void ClipRenderObject::SetClipGeometry(Microsoft::WRL::ComPtr new_clip_geometry) + { + clip_geometry_ = std::move(new_clip_geometry); + InvalidateRenderHost(); + } + + void ClipRenderObject::Draw(ID2D1RenderTarget* render_target) + { + if (clip_geometry_ != nullptr) + render_target->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), clip_geometry_.Get()), nullptr); + const auto child = GetChild(); + if (child != nullptr) + child->Draw(render_target); + if (clip_geometry_ != nullptr) + render_target->PopLayer(); + } + + void MatrixRenderObject::ApplyAppendMatrix(ID2D1RenderTarget* render_target, const D2D1_MATRIX_3X2_F& matrix) + { + D2D1::Matrix3x2F old_matrix; + render_target->GetTransform(&old_matrix); + render_target->SetTransform(old_matrix * matrix); + } + + void MatrixRenderObject::ApplySetMatrix(ID2D1RenderTarget* render_target, const D2D1_MATRIX_3X2_F& matrix) + { + render_target->SetTransform(matrix); + } + + MatrixRenderObject::MatrixRenderObject(const D2D1_MATRIX_3X2_F& matrix, MatrixApplier applier) + : matrix_(matrix), applier_(std::move(applier)) + { + + } + + void MatrixRenderObject::SetMatrix(const D2D1_MATRIX_3X2_F& new_matrix) + { + matrix_ = new_matrix; + InvalidateRenderHost(); + } + + void MatrixRenderObject::SetMatrixApplier(MatrixApplier applier) + { + applier_ = std::move(applier); + InvalidateRenderHost(); + } + + void MatrixRenderObject::Draw(ID2D1RenderTarget* render_target) + { + D2D1_MATRIX_3X2_F old_matrix; + render_target->GetTransform(&old_matrix); + applier_(render_target, matrix_); + const auto child = GetChild(); + if (child) + child->Draw(render_target); + render_target->SetTransform(&old_matrix); + } + + const MatrixRenderObject::MatrixApplier MatrixRenderObject::append_applier(ApplyAppendMatrix); + const MatrixRenderObject::MatrixApplier MatrixRenderObject::set_applier(ApplySetMatrix); +} -- cgit v1.2.3 From 4219992207b524e23a426e753589001b6f7a24d0 Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 10 Dec 2018 00:25:35 +0800 Subject: ... --- src/ui/render/render_object.cpp | 12 ++++++ src/ui/render/render_object.hpp | 83 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 4 deletions(-) (limited to 'src/ui/render/render_object.cpp') diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index d84d1d8b..7b430f06 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -116,6 +116,18 @@ namespace cru::ui::render render_target->SetTransform(&old_matrix); } + void OffsetRenderObject::SetOffsetX(const float new_offset_x) + { + offset_x_ = new_offset_x; + SetMatrix(D2D1::Matrix3x2F::Translation(offset_x_, offset_y_)); + } + + void OffsetRenderObject::SetOffsetY(const float new_offset_y) + { + offset_y_ = new_offset_y; + SetMatrix(D2D1::Matrix3x2F::Translation(offset_x_, offset_y_)); + } + const MatrixRenderObject::MatrixApplier MatrixRenderObject::append_applier(ApplyAppendMatrix); const MatrixRenderObject::MatrixApplier MatrixRenderObject::set_applier(ApplySetMatrix); } diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 3a8777d8..675a1759 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -167,13 +167,75 @@ namespace cru::ui::render class OffsetRenderObject : public MatrixRenderObject { public: - OffsetRenderObject(const float offset_x, const float offset_y) : MatrixRenderObject(D2D1::Matrix3x2F::Translation(offset_x, offset_y)) + explicit OffsetRenderObject(const float offset_x = 0.0f, const float offset_y = 0.0f) + : MatrixRenderObject(D2D1::Matrix3x2F::Translation(offset_x, offset_y)), + offset_x_(offset_x), offset_y_(offset_y) { - + + } + + float GetOffsetX() const + { + return offset_x_; + } + + void SetOffsetX(float new_offset_x); + + float GetOffsetY() const + { + return offset_y_; } - }; + void SetOffsetY(float new_offset_y); + private: + float offset_x_; + float offset_y_; + }; + + class BorderRenderObject; //TODO! + + class FillGeometryRenderObject; //TODO! + + class CustomDrawHandlerRenderObject; //TODO! + + class ContainerRenderObject; //TODO! + + + + // Render object tree for a control. (RO for RenderObject) + // + // ControlRO (not a SingleChildRO because child is not changed) + // | + // MatrixRO (control transform, only matrix exposed) + // | + // ClipRO (control clip, only clip geometry exposed) + // | + // OffsetRO (border offset) + // | + // ContainerRO + // / | + // BorderRO OffsetRO (padding offset) + // / | \ + // / | \ + // / | \ + // / | \ + // / | \ + // / | \ + // ContainerRO (background) | ContainerRO (foreground, symmetrical to background) + // / \ | / \ + // GeometryFillRO CustomDrawHandlerRO | GeometryFillRO CustomDrawHandlerRO + // | + // OffsetRO (content offset) + // | + // ContainerRO (content) + // / | \ + // / | \ + // / | \ + // ContainerRO (control-define content ROs) | ContainerRO (child-control ROs) + // | + // CustomDrawHandlerRO (user-define drawing) + // class ControlRenderObject : public RenderObject { public: @@ -184,6 +246,19 @@ namespace cru::ui::render ControlRenderObject& operator=(ControlRenderObject&& other) = delete; ~ControlRenderObject() override = default; - + + D2D1_MATRIX_3X2_F GetControlTransform() const; + Microsoft::WRL::ComPtr GetControlClip() const; + + Point GetBorderOffset() const; + BorderRenderObject* GetBorderRenderObject() const; + + Point GetPaddingOffset() const; + Microsoft::WRL::ComPtr GetPaddingGeometry() const; + + Point GetContentOffset() const; + ContainerRenderObject* GetContentContainer() const; + + ContainerRenderObject* GetChildrenContainer() const; }; } -- cgit v1.2.3 From 769a4db24b64a2baf20960858ae24461328e5b7a Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 10 Dec 2018 23:29:54 +0800 Subject: ... --- CruUI.vcxproj | 5 +- CruUI.vcxproj.filters | 9 +- snippets/vc++snippets.snippet | 55 +++++++++++++ src/ui/control.cpp | 2 +- src/ui/controls/list_item.cpp | 2 +- src/ui/controls/scroll_control.cpp | 2 +- src/ui/controls/toggle_button.cpp | 2 +- src/ui/convert_util.hpp | 21 ----- src/ui/d2d_util.hpp | 64 +++++++++++++++ src/ui/render/render_object.cpp | 99 +++++++++++++++++++++- src/ui/render/render_object.hpp | 164 ++++++++++++++++++++++++++++++++++--- src/ui/ui_base.hpp | 14 ++++ 12 files changed, 398 insertions(+), 41 deletions(-) create mode 100644 snippets/vc++snippets.snippet delete mode 100644 src/ui/convert_util.hpp create mode 100644 src/ui/d2d_util.hpp (limited to 'src/ui/render/render_object.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 429e6377..23e51e34 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -172,7 +172,7 @@ - + @@ -181,6 +181,9 @@ + + + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 547a58c2..13976a7d 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -176,9 +176,6 @@ Header Files - - Header Files - Header Files @@ -197,6 +194,9 @@ Header Files + + Header Files + @@ -254,4 +254,7 @@ Source Files + + + \ No newline at end of file diff --git a/snippets/vc++snippets.snippet b/snippets/vc++snippets.snippet new file mode 100644 index 00000000..1a5f7666 --- /dev/null +++ b/snippets/vc++snippets.snippet @@ -0,0 +1,55 @@ + + + +
+ Deleted Copy Constructor/Assignment + crupest + Declare a deleted copy constructor and a deleted copy assignment operator for a class. + dcopy + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
+ +
+ Deleted Move Constructor/Assignment + crupest + Declare a deleted move constructor and a deleted move assignment operator for a class. + dmove + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
+
\ No newline at end of file diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 3987e818..9388c719 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -8,7 +8,7 @@ #include "graph/graph.hpp" #include "exception.hpp" #include "cru_debug.hpp" -#include "convert_util.hpp" +#include "d2d_util.hpp" #include "math_util.hpp" #ifdef CRU_DEBUG_LAYOUT diff --git a/src/ui/controls/list_item.cpp b/src/ui/controls/list_item.cpp index e0ca28a9..6dd37fe9 100644 --- a/src/ui/controls/list_item.cpp +++ b/src/ui/controls/list_item.cpp @@ -1,7 +1,7 @@ #include "list_item.hpp" #include "ui/ui_manager.hpp" -#include "ui/convert_util.hpp" +#include "ui/d2d_util.hpp" namespace cru::ui::controls { diff --git a/src/ui/controls/scroll_control.cpp b/src/ui/controls/scroll_control.cpp index 622b4e4c..a202e355 100644 --- a/src/ui/controls/scroll_control.cpp +++ b/src/ui/controls/scroll_control.cpp @@ -3,7 +3,7 @@ #include #include "cru_debug.hpp" -#include "ui/convert_util.hpp" +#include "ui/d2d_util.hpp" #include "exception.hpp" #include "math_util.hpp" #include "ui/ui_manager.hpp" diff --git a/src/ui/controls/toggle_button.cpp b/src/ui/controls/toggle_button.cpp index 6eb0bc40..db72d7bb 100644 --- a/src/ui/controls/toggle_button.cpp +++ b/src/ui/controls/toggle_button.cpp @@ -3,7 +3,7 @@ #include "graph/graph.hpp" #include "ui/animations/animation.hpp" #include "ui/ui_manager.hpp" -#include "ui/convert_util.hpp" +#include "ui/d2d_util.hpp" namespace cru::ui::controls { diff --git a/src/ui/convert_util.hpp b/src/ui/convert_util.hpp deleted file mode 100644 index 5408f2e4..00000000 --- a/src/ui/convert_util.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "system_headers.hpp" - -#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); - } -} diff --git a/src/ui/d2d_util.hpp b/src/ui/d2d_util.hpp new file mode 100644 index 00000000..d4fff620 --- /dev/null +++ b/src/ui/d2d_util.hpp @@ -0,0 +1,64 @@ +#pragma once + +// ReSharper disable once CppUnusedIncludeDirective +#include "pre.hpp" + +#include "system_headers.hpp" + +#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 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 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); + } +} diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 7b430f06..78445bf1 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -87,6 +87,9 @@ namespace cru::ui::render render_target->SetTransform(matrix); } + const MatrixRenderObject::MatrixApplier MatrixRenderObject::append_applier(ApplyAppendMatrix); + const MatrixRenderObject::MatrixApplier MatrixRenderObject::set_applier(ApplySetMatrix); + MatrixRenderObject::MatrixRenderObject(const D2D1_MATRIX_3X2_F& matrix, MatrixApplier applier) : matrix_(matrix), applier_(std::move(applier)) { @@ -128,6 +131,98 @@ namespace cru::ui::render SetMatrix(D2D1::Matrix3x2F::Translation(offset_x_, offset_y_)); } - const MatrixRenderObject::MatrixApplier MatrixRenderObject::append_applier(ApplyAppendMatrix); - const MatrixRenderObject::MatrixApplier MatrixRenderObject::set_applier(ApplySetMatrix); + void StrokeRenderObject::SetStrokeWidth(const float new_stroke_width) + { + if (stroke_width_ == new_stroke_width) + return; + + stroke_width_ = new_stroke_width; + InvalidateRenderHost(); + } + + void StrokeRenderObject::SetBrush(Microsoft::WRL::ComPtr new_brush) + { + if (brush_ == new_brush) + return; + + brush_ = std::move(new_brush); + InvalidateRenderHost(); + } + + void StrokeRenderObject::SetStrokeStyle(Microsoft::WRL::ComPtr new_stroke_style) + { + if (stroke_style_ == new_stroke_style) + return; + + stroke_style_ = std::move(new_stroke_style); + InvalidateRenderHost(); + } + + void FillRenderObject::SetBrush(Microsoft::WRL::ComPtr new_brush) + { + if (brush_ == new_brush) + return; + + brush_ = std::move(new_brush); + InvalidateRenderHost(); + } + + void RoundedRectangleRenderObject::SetRect(const Rect& rect) + { + const auto converted_rect = Convert(rect); + if (rounded_rect_.rect == converted_rect) + return; + + rounded_rect_.rect = converted_rect; + InvalidateRenderHost(); + } + + void RoundedRectangleRenderObject::SetRadiusX(const float new_radius_x) + { + if (rounded_rect_.radiusX == new_radius_x) + return; + + rounded_rect_.radiusX = new_radius_x; + InvalidateRenderHost(); + } + + void RoundedRectangleRenderObject::SetRadiusY(const float new_radius_y) + { + if (rounded_rect_.radiusY == new_radius_y) + return; + + rounded_rect_.radiusY = new_radius_y; + InvalidateRenderHost(); + } + + void RoundedRectangleRenderObject::SetRoundedRect(const D2D1_ROUNDED_RECT& new_rounded_rect) + { + if (rounded_rect_ == new_rounded_rect) + return; + + rounded_rect_ = new_rounded_rect; + InvalidateRenderHost(); + } + + void RoundedRectangleStrokeRenderObject::Draw(ID2D1RenderTarget* render_target) + { + const auto brush = GetBrush(); + if (brush != nullptr) + render_target->DrawRoundedRectangle(GetRoundedRect(), GetBrush().Get(), GetStrokeWidth()); + } + + void CustomDrawHandlerRenderObject::SetDrawHandler(DrawHandler new_draw_handler) + { + if (draw_handler_ == nullptr && new_draw_handler == nullptr) + return; + + draw_handler_ = std::move(new_draw_handler); + InvalidateRenderHost; + } + + void CustomDrawHandlerRenderObject::Draw(ID2D1RenderTarget * render_target) + { + if (draw_handler_ != nullptr) + draw_handler_(render_target); + } } diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 675a1759..6603728d 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -7,6 +7,7 @@ #include "base.hpp" #include "ui/ui_base.hpp" +#include "ui/d2d_util.hpp" namespace cru::ui::render { @@ -193,11 +194,154 @@ namespace cru::ui::render float offset_y_; }; - class BorderRenderObject; //TODO! - class FillGeometryRenderObject; //TODO! + class StrokeRenderObject : public virtual RenderObject + { + protected: + StrokeRenderObject() = default; + public: + StrokeRenderObject(const StrokeRenderObject& other) = delete; + StrokeRenderObject(StrokeRenderObject&& other) = delete; + StrokeRenderObject& operator=(const StrokeRenderObject& other) = delete; + StrokeRenderObject& operator=(StrokeRenderObject&& other) = delete; + ~StrokeRenderObject() override = default; + + float GetStrokeWidth() const + { + return stroke_width_; + } + + void SetStrokeWidth(float new_stroke_width); + + Microsoft::WRL::ComPtr GetBrush() const + { + return brush_; + } + + void SetBrush(Microsoft::WRL::ComPtr new_brush); + + Microsoft::WRL::ComPtr GetStrokeStyle() const + { + return stroke_style_; + } + + void SetStrokeStyle(Microsoft::WRL::ComPtr new_stroke_style); + + private: + float stroke_width_ = 1.0f; + Microsoft::WRL::ComPtr brush_ = nullptr; + Microsoft::WRL::ComPtr stroke_style_ = nullptr; + }; + + + class FillRenderObject : public virtual RenderObject + { + protected: + FillRenderObject() = default; + public: + FillRenderObject(const FillRenderObject& other) = delete; + FillRenderObject(FillRenderObject&& other) = delete; + FillRenderObject& operator=(const FillRenderObject& other) = delete; + FillRenderObject& operator=(FillRenderObject&& other) = delete; + ~FillRenderObject() override = default; + + Microsoft::WRL::ComPtr GetBrush() const + { + return brush_; + } + + void SetBrush(Microsoft::WRL::ComPtr new_brush); + + private: + Microsoft::WRL::ComPtr brush_ = nullptr; + }; + + + class RoundedRectangleRenderObject : public virtual RenderObject + { + protected: + RoundedRectangleRenderObject() = default; + public: + RoundedRectangleRenderObject(const RoundedRectangleRenderObject& other) = delete; + RoundedRectangleRenderObject(RoundedRectangleRenderObject&& other) = delete; + RoundedRectangleRenderObject& operator=(const RoundedRectangleRenderObject& other) = delete; + RoundedRectangleRenderObject& operator=(RoundedRectangleRenderObject&& other) = delete; + ~RoundedRectangleRenderObject() override = default; + + Rect GetRect() const + { + return Convert(rounded_rect_.rect); + } + + void SetRect(const Rect& rect); + + float GetRadiusX() const + { + return rounded_rect_.radiusX; + } + + void SetRadiusX(float new_radius_x); + + float GetRadiusY() const + { + return rounded_rect_.radiusY; + } + + void SetRadiusY(float new_radius_y); + + D2D1_ROUNDED_RECT GetRoundedRect() const + { + return rounded_rect_; + } + + void SetRoundedRect(const D2D1_ROUNDED_RECT& new_rounded_rect); + + private: + D2D1_ROUNDED_RECT rounded_rect_ = D2D1::RoundedRect(D2D1::RectF(), 0.0f, 0.0f); + }; + + + class RoundedRectangleStrokeRenderObject final : public StrokeRenderObject, public RoundedRectangleRenderObject + { + public: + RoundedRectangleStrokeRenderObject() = default; + RoundedRectangleStrokeRenderObject(const RoundedRectangleStrokeRenderObject& other) = delete; + RoundedRectangleStrokeRenderObject(RoundedRectangleStrokeRenderObject&& other) = delete; + RoundedRectangleStrokeRenderObject& operator=(const RoundedRectangleStrokeRenderObject& other) = delete; + RoundedRectangleStrokeRenderObject& operator=(RoundedRectangleStrokeRenderObject&& other) = delete; + ~RoundedRectangleStrokeRenderObject() override = default; + + protected: + void Draw(ID2D1RenderTarget* render_target) override; + }; + + + class CustomDrawHandlerRenderObject : public RenderObject + { + public: + using DrawHandler = std::function; + + CustomDrawHandlerRenderObject() = default; + CustomDrawHandlerRenderObject(const CustomDrawHandlerRenderObject& other) = delete; + CustomDrawHandlerRenderObject& operator=(const CustomDrawHandlerRenderObject& other) = delete; + CustomDrawHandlerRenderObject(CustomDrawHandlerRenderObject&& other) = delete; + CustomDrawHandlerRenderObject& operator=(CustomDrawHandlerRenderObject&& other) = delete; + ~CustomDrawHandlerRenderObject() override = default; + + DrawHandler GetDrawHandler() const + { + return draw_handler_; + } + + void SetDrawHandler(DrawHandler new_draw_handler); + + protected: + void Draw(ID2D1RenderTarget* render_target) override; + + private: + DrawHandler draw_handler_{}; + }; - class CustomDrawHandlerRenderObject; //TODO! class ContainerRenderObject; //TODO! @@ -215,7 +359,7 @@ namespace cru::ui::render // | // ContainerRO // / | - // BorderRO OffsetRO (padding offset) + // StrokeRO (border) OffsetRO (padding offset) // / | \ // / | \ // / | \ @@ -224,7 +368,7 @@ namespace cru::ui::render // / | \ // ContainerRO (background) | ContainerRO (foreground, symmetrical to background) // / \ | / \ - // GeometryFillRO CustomDrawHandlerRO | GeometryFillRO CustomDrawHandlerRO + // FillRO CustomDrawHandlerRO | FillRO CustomDrawHandlerRO // | // OffsetRO (content offset) // | @@ -247,13 +391,13 @@ namespace cru::ui::render ~ControlRenderObject() override = default; - D2D1_MATRIX_3X2_F GetControlTransform() const; - Microsoft::WRL::ComPtr GetControlClip() const; + MatrixRenderObject* GetControlTransformRenderObject() const; + ClipRenderObject* GetControlClipRenderObject() const; - Point GetBorderOffset() const; - BorderRenderObject* GetBorderRenderObject() const; + OffsetRenderObject* GetBorderOffsetRenderObject() const; + RoundedRectangleStrokeRenderObject* GetBorderRenderObject() const; - Point GetPaddingOffset() const; + OffsetRenderObject* GetPaddingOffsetRenderObject() const; Microsoft::WRL::ComPtr GetPaddingGeometry() const; Point GetContentOffset() const; diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp index b898b2ed..5350e4e9 100644 --- a/src/ui/ui_base.hpp +++ b/src/ui/ui_base.hpp @@ -188,6 +188,20 @@ namespace cru::ui float height = 0.0f; }; + constexpr bool operator==(const Rect& left, const Rect& right) + { + return left.left == right.left && + left.top == right.top && + left.width == right.width && + left.height == right.height; + } + + constexpr bool operator!=(const Rect& left, const Rect& right) + { + return !(left == right); + } + + enum class MouseButton { Left, -- cgit v1.2.3 From 5220ab4fee1281b2daeacf4dd50448e06eb1f5e8 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 11 Dec 2018 23:38:59 +0800 Subject: ... --- src/ui/render/render_object.cpp | 33 ++++++++++++++++++++++++++++++++- src/ui/render/render_object.hpp | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) (limited to 'src/ui/render/render_object.cpp') diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 78445bf1..6796f382 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -220,9 +220,40 @@ namespace cru::ui::render InvalidateRenderHost; } - void CustomDrawHandlerRenderObject::Draw(ID2D1RenderTarget * render_target) + void CustomDrawHandlerRenderObject::Draw(ID2D1RenderTarget* render_target) { if (draw_handler_ != nullptr) draw_handler_(render_target); } + + ContainerRenderObject::~ContainerRenderObject() + { + for (const auto child : children_) + delete child; + } + + void ContainerRenderObject::AddChild(RenderObject* child) + { + children_.push_back(child); + } + + void ContainerRenderObject::AddChild(RenderObject* child, const int position) + { + assert(position >= 0); + assert(position <= children_.size()); + children_.insert(children_.cbegin() + position, child); + } + + void ContainerRenderObject::RemoveChild(const int position) + { + assert(position >= 0); + assert(position < children_.size()); + children_.erase(children_.cbegin() + position); + } + + void ContainerRenderObject::Draw(ID2D1RenderTarget* render_target) + { + for (const auto child : children_) + child->Draw(render_target); + } } diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 6603728d..4432636c 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -4,6 +4,7 @@ #include "system_headers.hpp" #include +#include #include "base.hpp" #include "ui/ui_base.hpp" @@ -343,7 +344,40 @@ namespace cru::ui::render }; - class ContainerRenderObject; //TODO! + class ContainerRenderObject : public RenderObject + { + public: + ContainerRenderObject() = default; + ContainerRenderObject(const ContainerRenderObject& other) = delete; + ContainerRenderObject& operator=(const ContainerRenderObject& other) = delete; + ContainerRenderObject(ContainerRenderObject&& other) = delete; + ContainerRenderObject& operator=(ContainerRenderObject&& other) = delete; + ~ContainerRenderObject(); + + const std::vector& GetChildren() const + { + return children_; + } + + RenderObject* GetAt(const int position) const + { + assert(position >= 0); + assert(position < static_cast(children_.size())); + return children_.at(static_cast::size_type>(position)); + } + + void AddChild(RenderObject* child); + + void AddChild(RenderObject* child, int position); + + void RemoveChild(int position); + + protected: + void Draw(ID2D1RenderTarget* render_target) override; + + private: + std::vector children_; + }; -- cgit v1.2.3 From 3540c1718c74e9fa3538ae92a6418eb02d27f1c5 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 11 Dec 2018 23:59:26 +0800 Subject: ... --- src/ui/render/render_object.cpp | 138 ----------------------- src/ui/render/render_object.hpp | 236 ---------------------------------------- 2 files changed, 374 deletions(-) (limited to 'src/ui/render/render_object.cpp') diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 6796f382..3e2e5265 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -24,113 +24,6 @@ namespace cru::ui::render render_host_->InvalidateRender(); } - SingleChildRenderObject::~SingleChildRenderObject() - { - delete child_; - } - - void SingleChildRenderObject::SetChild(RenderObject* new_child) - { - const auto old = child_; - if (old) - old->SetRenderHost(nullptr); - child_ = new_child; - if (new_child) - new_child->SetRenderHost(GetRenderHost()); - OnChildChange(old, new_child); - InvalidateRenderHost(); - } - - void SingleChildRenderObject::OnRenderHostChanged(IRenderHost* old_render_host, IRenderHost* new_render_host) - { - if (child_) - child_->SetRenderHost(new_render_host); - } - - void SingleChildRenderObject::OnChildChange(RenderObject* old_child, RenderObject* new_object) - { - - } - - ClipRenderObject::ClipRenderObject(Microsoft::WRL::ComPtr clip_geometry) - : clip_geometry_(std::move(clip_geometry)) - { - - } - - void ClipRenderObject::SetClipGeometry(Microsoft::WRL::ComPtr new_clip_geometry) - { - clip_geometry_ = std::move(new_clip_geometry); - InvalidateRenderHost(); - } - - void ClipRenderObject::Draw(ID2D1RenderTarget* render_target) - { - if (clip_geometry_ != nullptr) - render_target->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), clip_geometry_.Get()), nullptr); - const auto child = GetChild(); - if (child != nullptr) - child->Draw(render_target); - if (clip_geometry_ != nullptr) - render_target->PopLayer(); - } - - void MatrixRenderObject::ApplyAppendMatrix(ID2D1RenderTarget* render_target, const D2D1_MATRIX_3X2_F& matrix) - { - D2D1::Matrix3x2F old_matrix; - render_target->GetTransform(&old_matrix); - render_target->SetTransform(old_matrix * matrix); - } - - void MatrixRenderObject::ApplySetMatrix(ID2D1RenderTarget* render_target, const D2D1_MATRIX_3X2_F& matrix) - { - render_target->SetTransform(matrix); - } - - const MatrixRenderObject::MatrixApplier MatrixRenderObject::append_applier(ApplyAppendMatrix); - const MatrixRenderObject::MatrixApplier MatrixRenderObject::set_applier(ApplySetMatrix); - - MatrixRenderObject::MatrixRenderObject(const D2D1_MATRIX_3X2_F& matrix, MatrixApplier applier) - : matrix_(matrix), applier_(std::move(applier)) - { - - } - - void MatrixRenderObject::SetMatrix(const D2D1_MATRIX_3X2_F& new_matrix) - { - matrix_ = new_matrix; - InvalidateRenderHost(); - } - - void MatrixRenderObject::SetMatrixApplier(MatrixApplier applier) - { - applier_ = std::move(applier); - InvalidateRenderHost(); - } - - void MatrixRenderObject::Draw(ID2D1RenderTarget* render_target) - { - D2D1_MATRIX_3X2_F old_matrix; - render_target->GetTransform(&old_matrix); - applier_(render_target, matrix_); - const auto child = GetChild(); - if (child) - child->Draw(render_target); - render_target->SetTransform(&old_matrix); - } - - void OffsetRenderObject::SetOffsetX(const float new_offset_x) - { - offset_x_ = new_offset_x; - SetMatrix(D2D1::Matrix3x2F::Translation(offset_x_, offset_y_)); - } - - void OffsetRenderObject::SetOffsetY(const float new_offset_y) - { - offset_y_ = new_offset_y; - SetMatrix(D2D1::Matrix3x2F::Translation(offset_x_, offset_y_)); - } - void StrokeRenderObject::SetStrokeWidth(const float new_stroke_width) { if (stroke_width_ == new_stroke_width) @@ -225,35 +118,4 @@ namespace cru::ui::render if (draw_handler_ != nullptr) draw_handler_(render_target); } - - ContainerRenderObject::~ContainerRenderObject() - { - for (const auto child : children_) - delete child; - } - - void ContainerRenderObject::AddChild(RenderObject* child) - { - children_.push_back(child); - } - - void ContainerRenderObject::AddChild(RenderObject* child, const int position) - { - assert(position >= 0); - assert(position <= children_.size()); - children_.insert(children_.cbegin() + position, child); - } - - void ContainerRenderObject::RemoveChild(const int position) - { - assert(position >= 0); - assert(position < children_.size()); - children_.erase(children_.cbegin() + position); - } - - void ContainerRenderObject::Draw(ID2D1RenderTarget* render_target) - { - for (const auto child : children_) - child->Draw(render_target); - } } diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 4432636c..b6f9f13b 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -18,24 +18,9 @@ namespace cru::ui::render * It represents a painting action on a d2d render target. By * overriding "Draw" virtual method, it can customize its painting * action. - * - * Render object may have implicit children to form a tree. - * RenderObject class doesn't provide any method to access children. - * Instead any concrete render object manage their own child model - * and interface and optionally call Draw on children so when root - * call Draw descendants' Draw will be called recursively. - * - * Related render objects of a control are a subtree of the whole tree. - * So render objects can be composed easily to form a whole control. - * - * Render object may do no actual drawing but perform particular - * actions on render target. Like ClipRenderObject apply a clip on - * render target, MatrixRenderObject apply a matrix on render - * target. */ - struct IRenderHost : Interface { virtual void InvalidateRender() = 0; @@ -72,130 +57,6 @@ namespace cru::ui::render }; - // It is subclass duty to call child's Draw. - class SingleChildRenderObject : public RenderObject - { - protected: - SingleChildRenderObject() = default; - public: - SingleChildRenderObject(const SingleChildRenderObject& other) = delete; - SingleChildRenderObject(SingleChildRenderObject&& other) = delete; - SingleChildRenderObject& operator=(const SingleChildRenderObject& other) = delete; - SingleChildRenderObject& operator=(SingleChildRenderObject&& other) = delete; - ~SingleChildRenderObject(); - - RenderObject* GetChild() const - { - return child_; - } - - void SetChild(RenderObject* new_child); - - protected: - void OnRenderHostChanged(IRenderHost* old_render_host, IRenderHost* new_render_host) override; - - virtual void OnChildChange(RenderObject* old_child, RenderObject* new_object); - - private: - RenderObject* child_ = nullptr; - }; - - - class ClipRenderObject final : public SingleChildRenderObject - { - public: - explicit ClipRenderObject(Microsoft::WRL::ComPtr clip_geometry = nullptr); - ClipRenderObject(const ClipRenderObject& other) = delete; - ClipRenderObject(ClipRenderObject&& other) = delete; - ClipRenderObject& operator=(const ClipRenderObject& other) = delete; - ClipRenderObject& operator=(ClipRenderObject&& other) = delete; - ~ClipRenderObject() = default; - - Microsoft::WRL::ComPtr GetClipGeometry() const - { - return clip_geometry_; - } - void SetClipGeometry(Microsoft::WRL::ComPtr new_clip_geometry); - - void Draw(ID2D1RenderTarget* render_target) override final; - - private: - Microsoft::WRL::ComPtr clip_geometry_; - }; - - - class MatrixRenderObject: public SingleChildRenderObject - { - private: - static void ApplyAppendMatrix(ID2D1RenderTarget* render_target, const D2D1_MATRIX_3X2_F& matrix); - static void ApplySetMatrix(ID2D1RenderTarget* render_target, const D2D1_MATRIX_3X2_F& matrix); - - public: - using MatrixApplier = std::function; - - static const MatrixApplier append_applier; - static const MatrixApplier set_applier; - - explicit MatrixRenderObject(const D2D1_MATRIX_3X2_F& matrix = D2D1::Matrix3x2F::Identity(), - MatrixApplier applier = append_applier); - MatrixRenderObject(const MatrixRenderObject& other) = delete; - MatrixRenderObject(MatrixRenderObject&& other) = delete; - MatrixRenderObject& operator=(const MatrixRenderObject& other) = delete; - MatrixRenderObject& operator=(MatrixRenderObject&& other) = delete; - ~MatrixRenderObject() = default; - - D2D1_MATRIX_3X2_F GetMatrix() const - { - return matrix_; - } - - void SetMatrix(const D2D1_MATRIX_3X2_F& new_matrix); - - MatrixApplier GetMatrixApplier() const - { - return applier_; - } - - void SetMatrixApplier(MatrixApplier applier); - - void Draw(ID2D1RenderTarget* render_target) override final; - - private: - D2D1_MATRIX_3X2_F matrix_; - MatrixApplier applier_; - }; - - - class OffsetRenderObject : public MatrixRenderObject - { - public: - explicit OffsetRenderObject(const float offset_x = 0.0f, const float offset_y = 0.0f) - : MatrixRenderObject(D2D1::Matrix3x2F::Translation(offset_x, offset_y)), - offset_x_(offset_x), offset_y_(offset_y) - { - - } - - float GetOffsetX() const - { - return offset_x_; - } - - void SetOffsetX(float new_offset_x); - - float GetOffsetY() const - { - return offset_y_; - } - - void SetOffsetY(float new_offset_y); - - private: - float offset_x_; - float offset_y_; - }; - - class StrokeRenderObject : public virtual RenderObject { protected: @@ -342,101 +203,4 @@ namespace cru::ui::render private: DrawHandler draw_handler_{}; }; - - - class ContainerRenderObject : public RenderObject - { - public: - ContainerRenderObject() = default; - ContainerRenderObject(const ContainerRenderObject& other) = delete; - ContainerRenderObject& operator=(const ContainerRenderObject& other) = delete; - ContainerRenderObject(ContainerRenderObject&& other) = delete; - ContainerRenderObject& operator=(ContainerRenderObject&& other) = delete; - ~ContainerRenderObject(); - - const std::vector& GetChildren() const - { - return children_; - } - - RenderObject* GetAt(const int position) const - { - assert(position >= 0); - assert(position < static_cast(children_.size())); - return children_.at(static_cast::size_type>(position)); - } - - void AddChild(RenderObject* child); - - void AddChild(RenderObject* child, int position); - - void RemoveChild(int position); - - protected: - void Draw(ID2D1RenderTarget* render_target) override; - - private: - std::vector children_; - }; - - - - // Render object tree for a control. (RO for RenderObject) - // - // ControlRO (not a SingleChildRO because child is not changed) - // | - // MatrixRO (control transform, only matrix exposed) - // | - // ClipRO (control clip, only clip geometry exposed) - // | - // OffsetRO (border offset) - // | - // ContainerRO - // / | - // StrokeRO (border) OffsetRO (padding offset) - // / | \ - // / | \ - // / | \ - // / | \ - // / | \ - // / | \ - // ContainerRO (background) | ContainerRO (foreground, symmetrical to background) - // / \ | / \ - // FillRO CustomDrawHandlerRO | FillRO CustomDrawHandlerRO - // | - // OffsetRO (content offset) - // | - // ContainerRO (content) - // / | \ - // / | \ - // / | \ - // ContainerRO (control-define content ROs) | ContainerRO (child-control ROs) - // | - // CustomDrawHandlerRO (user-define drawing) - // - class ControlRenderObject : public RenderObject - { - public: - ControlRenderObject() = default; - ControlRenderObject(const ControlRenderObject& other) = delete; - ControlRenderObject(ControlRenderObject&& other) = delete; - ControlRenderObject& operator=(const ControlRenderObject& other) = delete; - ControlRenderObject& operator=(ControlRenderObject&& other) = delete; - ~ControlRenderObject() override = default; - - - MatrixRenderObject* GetControlTransformRenderObject() const; - ClipRenderObject* GetControlClipRenderObject() const; - - OffsetRenderObject* GetBorderOffsetRenderObject() const; - RoundedRectangleStrokeRenderObject* GetBorderRenderObject() const; - - OffsetRenderObject* GetPaddingOffsetRenderObject() const; - Microsoft::WRL::ComPtr GetPaddingGeometry() const; - - Point GetContentOffset() const; - ContainerRenderObject* GetContentContainer() const; - - ContainerRenderObject* GetChildrenContainer() const; - }; } -- cgit v1.2.3 From edbdf3d2a166099f5af4dd859258468288919685 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 12 Dec 2018 00:40:57 +0800 Subject: ... --- src/ui/d2d_util.hpp | 30 +++++++++++++++ src/ui/render/render_object.cpp | 42 +++------------------ src/ui/render/render_object.hpp | 82 +++++++++++++++++++++-------------------- src/ui/ui_base.hpp | 80 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 76 deletions(-) (limited to 'src/ui/render/render_object.cpp') diff --git a/src/ui/d2d_util.hpp b/src/ui/d2d_util.hpp index d4fff620..5f2e10b2 100644 --- a/src/ui/d2d_util.hpp +++ b/src/ui/d2d_util.hpp @@ -19,6 +19,16 @@ namespace cru::ui 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); @@ -29,6 +39,16 @@ namespace cru::ui 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; @@ -61,4 +81,14 @@ namespace cru::ui { 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); + } } diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 3e2e5265..bfa250d6 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -60,48 +60,18 @@ namespace cru::ui::render InvalidateRenderHost(); } - void RoundedRectangleRenderObject::SetRect(const Rect& rect) + namespace details { - const auto converted_rect = Convert(rect); - if (rounded_rect_.rect == converted_rect) - return; - - rounded_rect_.rect = converted_rect; - InvalidateRenderHost(); - } - - void RoundedRectangleRenderObject::SetRadiusX(const float new_radius_x) - { - if (rounded_rect_.radiusX == new_radius_x) - return; - - rounded_rect_.radiusX = new_radius_x; - InvalidateRenderHost(); - } - - void RoundedRectangleRenderObject::SetRadiusY(const float new_radius_y) - { - if (rounded_rect_.radiusY == new_radius_y) - return; - - rounded_rect_.radiusY = new_radius_y; - InvalidateRenderHost(); - } - - void RoundedRectangleRenderObject::SetRoundedRect(const D2D1_ROUNDED_RECT& new_rounded_rect) - { - if (rounded_rect_ == new_rounded_rect) - return; - - rounded_rect_ = new_rounded_rect; - InvalidateRenderHost(); + template class ShapeRenderObject; + template class ShapeRenderObject; + template class ShapeRenderObject; } void RoundedRectangleStrokeRenderObject::Draw(ID2D1RenderTarget* render_target) { const auto brush = GetBrush(); if (brush != nullptr) - render_target->DrawRoundedRectangle(GetRoundedRect(), GetBrush().Get(), GetStrokeWidth()); + render_target->DrawRoundedRectangle(Convert(GetShape()), GetBrush().Get(), GetStrokeWidth()); } void CustomDrawHandlerRenderObject::SetDrawHandler(DrawHandler new_draw_handler) @@ -110,7 +80,7 @@ namespace cru::ui::render return; draw_handler_ = std::move(new_draw_handler); - InvalidateRenderHost; + InvalidateRenderHost(); } void CustomDrawHandlerRenderObject::Draw(ID2D1RenderTarget* render_target) diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index b6f9f13b..0fbeb8c7 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -119,48 +119,50 @@ namespace cru::ui::render }; - class RoundedRectangleRenderObject : public virtual RenderObject + namespace details { - protected: - RoundedRectangleRenderObject() = default; - public: - RoundedRectangleRenderObject(const RoundedRectangleRenderObject& other) = delete; - RoundedRectangleRenderObject(RoundedRectangleRenderObject&& other) = delete; - RoundedRectangleRenderObject& operator=(const RoundedRectangleRenderObject& other) = delete; - RoundedRectangleRenderObject& operator=(RoundedRectangleRenderObject&& other) = delete; - ~RoundedRectangleRenderObject() override = default; - - Rect GetRect() const - { - return Convert(rounded_rect_.rect); - } - - void SetRect(const Rect& rect); - - float GetRadiusX() const - { - return rounded_rect_.radiusX; - } - - void SetRadiusX(float new_radius_x); - - float GetRadiusY() const - { - return rounded_rect_.radiusY; - } - - void SetRadiusY(float new_radius_y); - - D2D1_ROUNDED_RECT GetRoundedRect() const + template + class ShapeRenderObject : public virtual RenderObject { - return rounded_rect_; - } - - void SetRoundedRect(const D2D1_ROUNDED_RECT& new_rounded_rect); - - private: - D2D1_ROUNDED_RECT rounded_rect_ = D2D1::RoundedRect(D2D1::RectF(), 0.0f, 0.0f); - }; + public: + using ShapeType = TShapeType; + protected: + ShapeRenderObject() = default; + public: + ShapeRenderObject(const ShapeRenderObject& other) = delete; + ShapeRenderObject& operator=(const ShapeRenderObject& other) = delete; + ShapeRenderObject(ShapeRenderObject&& other) = delete; + ShapeRenderObject& operator=(ShapeRenderObject&& other) = delete; + ~ShapeRenderObject() override = default; + + ShapeType GetShape() const + { + return shape_; + } + + void SetShape(const ShapeType& new_shape) + { + if (new_shape == shape_) + return; + + shape_ = new_shape; + InvalidateRenderHost(); + } + + private: + ShapeType shape_; + }; + + + extern template class ShapeRenderObject; + extern template class ShapeRenderObject; + extern template class ShapeRenderObject; + } + + + using RectangleRenderObject = details::ShapeRenderObject; + using RoundedRectangleRenderObject = details::ShapeRenderObject; + using EllipseRenderObject = details::ShapeRenderObject; class RoundedRectangleStrokeRenderObject final : public StrokeRenderObject, public RoundedRectangleRenderObject diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp index 5350e4e9..c26bfe0e 100644 --- a/src/ui/ui_base.hpp +++ b/src/ui/ui_base.hpp @@ -32,6 +32,7 @@ namespace cru::ui return !(left == right); } + struct Size { constexpr static Size Zero() @@ -66,6 +67,7 @@ namespace cru::ui return !(left == right); } + struct Thickness { constexpr static Thickness Zero() @@ -120,6 +122,20 @@ namespace cru::ui float bottom; }; + constexpr bool operator == (const Thickness& left, const Thickness& right) + { + return left.left == right.left && + left.top == right.top && + left.right == right.right && + left.bottom == right.bottom; + } + + constexpr bool operator != (const Thickness& left, const Thickness& right) + { + return !(left == right); + } + + struct Rect { constexpr Rect() = default; @@ -133,6 +149,11 @@ namespace cru::ui return Rect(left, top, right - left, bottom - top); } + constexpr static Rect FromCenter(const Point& center, const float width, const float height) + { + return Rect(center.x - width / 2.0f, center.y - height / 2.0f, width, height); + } + constexpr float GetRight() const { return left + width; @@ -163,6 +184,11 @@ namespace cru::ui return Point(left + width, top); } + constexpr Point GetCenter() const + { + return Point(left + width / 2.0f, top + height / 2.0f); + } + constexpr Size GetSize() const { return Size(width, height); @@ -202,6 +228,59 @@ namespace cru::ui } + struct RoundedRect + { + constexpr RoundedRect() = default; + constexpr RoundedRect(const Rect& rect, const float radius_x, const float radius_y) + : rect(rect), radius_x(radius_x), radius_y(radius_y) { } + + Rect rect{}; + float radius_x = 0.0f; + float radius_y = 0.0f; + }; + + constexpr bool operator == (const RoundedRect& left, const RoundedRect& right) + { + return left.rect == right.rect && left.radius_x == right.radius_x && left.radius_y == right.radius_y; + } + + constexpr bool operator != (const RoundedRect& left, const RoundedRect& right) + { + return !(left == right); + } + + struct Ellipse + { + constexpr Ellipse() = default; + constexpr Ellipse(const Point& center, const float radius_x, const float radius_y) + : center(center), radius_x(radius_x), radius_y(radius_y) { } + + constexpr static Ellipse FromRect(const Rect& rect) + { + return Ellipse(rect.GetCenter(), rect.width / 2.0f, rect.height / 2.0f); + } + + constexpr Rect GetBoundRect() const + { + return Rect::FromCenter(center, radius_x * 2.0f, radius_y * 2.0f); + } + + Point center{}; + float radius_x = 0.0f; + float radius_y = 0.0f; + }; + + constexpr bool operator == (const Ellipse& left, const Ellipse& right) + { + return left.center == right.center && left.radius_x == right.radius_x && left.radius_y == right.radius_y; + } + + constexpr bool operator != (const Ellipse& left, const Ellipse& right) + { + return !(left == right); + } + + enum class MouseButton { Left, @@ -209,6 +288,7 @@ namespace cru::ui Middle }; + struct TextRange { constexpr static std::optional FromTwoSides(unsigned first, unsigned second) -- cgit v1.2.3 From 4683f1565b7fdb2e5f9367d5cd365a00cc3a7e1d Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 12 Dec 2018 01:02:10 +0800 Subject: ... --- src/ui/render/render_object.cpp | 15 ++++++--- src/ui/render/render_object.hpp | 71 ++++++++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 15 deletions(-) (limited to 'src/ui/render/render_object.cpp') diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index bfa250d6..0828fc9c 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -67,11 +67,18 @@ namespace cru::ui::render template class ShapeRenderObject; } - void RoundedRectangleStrokeRenderObject::Draw(ID2D1RenderTarget* render_target) + namespace details + { + template ShapeStrokeRenderObject; + template ShapeStrokeRenderObject; + template ShapeStrokeRenderObject; + } + + namespace details { - const auto brush = GetBrush(); - if (brush != nullptr) - render_target->DrawRoundedRectangle(Convert(GetShape()), GetBrush().Get(), GetStrokeWidth()); + template ShapeFillRenderObject; + template ShapeFillRenderObject; + template ShapeFillRenderObject; } void CustomDrawHandlerRenderObject::SetDrawHandler(DrawHandler new_draw_handler) diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 0fbeb8c7..31745be5 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -165,19 +165,68 @@ namespace cru::ui::render using EllipseRenderObject = details::ShapeRenderObject; - class RoundedRectangleStrokeRenderObject final : public StrokeRenderObject, public RoundedRectangleRenderObject + namespace details { - public: - RoundedRectangleStrokeRenderObject() = default; - RoundedRectangleStrokeRenderObject(const RoundedRectangleStrokeRenderObject& other) = delete; - RoundedRectangleStrokeRenderObject(RoundedRectangleStrokeRenderObject&& other) = delete; - RoundedRectangleStrokeRenderObject& operator=(const RoundedRectangleStrokeRenderObject& other) = delete; - RoundedRectangleStrokeRenderObject& operator=(RoundedRectangleStrokeRenderObject&& other) = delete; - ~RoundedRectangleStrokeRenderObject() override = default; + template + class ShapeStrokeRenderObject : public ShapeRenderObject, public StrokeRenderObject + { + public: + ShapeStrokeRenderObject() = default; + ShapeStrokeRenderObject(const ShapeStrokeRenderObject& other) = delete; + ShapeStrokeRenderObject& operator=(const ShapeStrokeRenderObject& other) = delete; + ShapeStrokeRenderObject(ShapeStrokeRenderObject&& other) = delete; + ShapeStrokeRenderObject& operator=(ShapeStrokeRenderObject&& other) = delete; + ~ShapeStrokeRenderObject() = default; - protected: - void Draw(ID2D1RenderTarget* render_target) override; - }; + protected: + void Draw(ID2D1RenderTarget* render_target) override + { + const auto brush = GetBrush(); + if (brush != nullptr) + (render_target->*draw_function)(Convert(GetShape()), brush.Get(), GetStrokeWidth(), GetStrokeStyle().Get()); + } + }; + + extern template ShapeStrokeRenderObject; + extern template ShapeStrokeRenderObject; + extern template ShapeStrokeRenderObject; + } + + using RectangleStrokeRenderObject = details::ShapeStrokeRenderObject; + using RoundedRectangleStrokeRenderObject = details::ShapeStrokeRenderObject; + using EllipseStrokeRenderObject = details::ShapeStrokeRenderObject; + + + namespace details + { + template + class ShapeFillRenderObject : public ShapeRenderObject, public StrokeRenderObject + { + public: + ShapeFillRenderObject() = default; + ShapeFillRenderObject(const ShapeFillRenderObject& other) = delete; + ShapeFillRenderObject& operator=(const ShapeFillRenderObject& other) = delete; + ShapeFillRenderObject(ShapeFillRenderObject&& other) = delete; + ShapeFillRenderObject& operator=(ShapeFillRenderObject&& other) = delete; + ~ShapeFillRenderObject() = default; + + protected: + void Draw(ID2D1RenderTarget* render_target) override + { + const auto brush = GetBrush(); + if (brush != nullptr) + (render_target->*fill_function)(Convert(GetShape()), brush.Get()); + } + }; + + extern template ShapeFillRenderObject; + extern template ShapeFillRenderObject; + extern template ShapeFillRenderObject; + } + + using RectangleFillRenderObject = details::ShapeFillRenderObject; + using RoundedRectangleFillRenderObject = details::ShapeFillRenderObject; + using EllipseFillRenderObject = details::ShapeFillRenderObject; class CustomDrawHandlerRenderObject : public RenderObject -- cgit v1.2.3 From 5dc738a57930271194bd86673eb86f149096a7b2 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 19 Mar 2019 16:21:54 +0800 Subject: ... --- .clang-format | 1 + CruUI.vcxproj | 18 +- CruUI.vcxproj.filters | 54 +- snippets/vc++snippets.snippet | 124 ++++- src/any_map.cpp | 33 -- src/any_map.hpp | 103 ---- src/application.cpp | 192 ++++---- src/application.hpp | 177 ++++--- src/base.cpp | 20 - src/base.hpp | 91 ++-- src/cru_debug.cpp | 10 +- src/cru_debug.hpp | 67 ++- src/cru_event.hpp | 134 +++-- src/exception.cpp | 76 +-- src/exception.hpp | 94 ++-- src/format.hpp | 110 ----- src/math_util.hpp | 69 --- src/pre.hpp | 5 +- src/system_headers.hpp | 2 - src/timer.cpp | 105 ++-- src/timer.hpp | 90 ++-- src/ui/control.cpp | 681 ++------------------------ src/ui/control.hpp | 301 ++---------- src/ui/controls/button.hpp | 2 +- src/ui/controls/list_item.hpp | 2 +- src/ui/controls/scroll_control.hpp | 2 +- src/ui/render/linear_layout_render_object.cpp | 0 src/ui/render/linear_layout_render_object.hpp | 1 + src/ui/render/render_object.cpp | 176 ++++--- src/ui/render/render_object.hpp | 302 +++--------- src/ui/ui_base.hpp | 39 +- src/ui/window.cpp | 23 - src/ui/window.hpp | 28 +- src/ui/window_class.cpp | 25 + src/ui/window_class.hpp | 26 + src/util/any_map.cpp | 30 ++ src/util/any_map.hpp | 94 ++++ src/util/format.hpp | 94 ++++ src/util/math_util.hpp | 51 ++ src/util/string_util.cpp | 21 + src/util/string_util.hpp | 8 + 41 files changed, 1260 insertions(+), 2221 deletions(-) create mode 100644 .clang-format delete mode 100644 src/any_map.cpp delete mode 100644 src/any_map.hpp delete mode 100644 src/base.cpp delete mode 100644 src/format.hpp delete mode 100644 src/math_util.hpp create mode 100644 src/ui/render/linear_layout_render_object.cpp create mode 100644 src/ui/render/linear_layout_render_object.hpp create mode 100644 src/ui/window_class.cpp create mode 100644 src/ui/window_class.hpp create mode 100644 src/util/any_map.cpp create mode 100644 src/util/any_map.hpp create mode 100644 src/util/format.hpp create mode 100644 src/util/math_util.hpp create mode 100644 src/util/string_util.cpp create mode 100644 src/util/string_util.hpp (limited to 'src/ui/render/render_object.cpp') diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..f6cb8ad9 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: Google diff --git a/CruUI.vcxproj b/CruUI.vcxproj index ee2c7eb1..9bedd3f8 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -117,10 +117,10 @@
- + + - @@ -136,9 +136,11 @@ - - - + + + + + @@ -150,10 +152,12 @@ + + @@ -178,6 +182,7 @@ + @@ -186,6 +191,9 @@ + + + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 13976a7d..9a85da3f 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -18,9 +18,6 @@ Source Files - - Source Files - Source Files @@ -75,9 +72,6 @@ Source Files - - Source Files - Source Files @@ -96,6 +90,21 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -158,9 +167,6 @@ Header Files - - Header Files - Header Files @@ -170,9 +176,6 @@ Header Files - - Header Files - Header Files @@ -185,9 +188,6 @@ Header Files - - Header Files - Header Files @@ -197,6 +197,27 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -257,4 +278,7 @@ + + + \ No newline at end of file diff --git a/snippets/vc++snippets.snippet b/snippets/vc++snippets.snippet index 1a5f7666..ebd72463 100644 --- a/snippets/vc++snippets.snippet +++ b/snippets/vc++snippets.snippet @@ -5,7 +5,7 @@ Deleted Copy Constructor/Assignment crupest Declare a deleted copy constructor and a deleted copy assignment operator for a class. - dcopy + deletecopy Expansion @@ -26,12 +26,38 @@ + +
+ Default Copy Constructor/Assignment + crupest + Declare a default copy constructor and a default copy assignment operator for a class. + defaultcopy + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
Deleted Move Constructor/Assignment crupest Declare a deleted move constructor and a deleted move assignment operator for a class. - dmove + deletemove Expansion @@ -52,4 +78,98 @@ + +
+ Default Move Constructor/Assignment + crupest + Declare a default move constructor and a default move assignment operator for a class. + defaultmove + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
+ +
+ Default Copyable/Movable Class + crupest + Define a class with default constructor, destructor, move/copy constructor/assignment-operator. + defaultclass + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
+ +
+ Default Noncopyable/Nonmovable Class + crupest + Define a class with default constructor, destructor and deleted move/copy constructor/assignment-operator. + objectclass + + Expansion + +
+ + + + classname + class_name + The name of the class. + + + + + + +
\ No newline at end of file diff --git a/src/any_map.cpp b/src/any_map.cpp deleted file mode 100644 index de13f85e..00000000 --- a/src/any_map.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "any_map.hpp" - -namespace cru -{ - AnyMap::ListenerToken AnyMap::RegisterValueChangeListener(const String& key, const Listener& listener) - { - const auto token = current_listener_token_++; - map_[key].second.push_back(token); - listeners_.emplace(token, listener); - return token; - } - - void AnyMap::UnregisterValueChangeListener(const ListenerToken token) - { - const auto find_result = listeners_.find(token); - if (find_result != listeners_.cend()) - listeners_.erase(find_result); - } - - void AnyMap::InvokeListeners(std::list& listener_list, const std::any& value) - { - auto i = listener_list.cbegin(); - while (i != listener_list.cend()) - { - auto current_i = i++; - const auto find_result = listeners_.find(*current_i); - if (find_result != listeners_.cend()) - find_result->second(value); - else - listener_list.erase(current_i); // otherwise remove the invalid listener token. - } - } -} diff --git a/src/any_map.hpp b/src/any_map.hpp deleted file mode 100644 index dfc54f3f..00000000 --- a/src/any_map.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include -#include -#include -#include -#include - -#include "base.hpp" -#include "format.hpp" - - -namespace cru -{ - // A map with String as key and any type as value. - // It also has notification when value with specified key changed. - class AnyMap : public Object - { - public: - using ListenerToken = long; - using Listener = std::function; - - AnyMap() = default; - AnyMap(const AnyMap& other) = delete; - AnyMap(AnyMap&& other) = delete; - AnyMap& operator=(const AnyMap& other) = delete; - AnyMap& operator=(AnyMap&& other) = delete; - ~AnyMap() override = default; - - - // return the value if the value exists and the type of value is T. - // return a null optional if value doesn't exists. - // throw std::runtime_error if type is mismatch. - template - std::optional GetOptionalValue(const String& key) const - { - try - { - const auto find_result = map_.find(key); - if (find_result != map_.cend()) - { - const auto& value = find_result->second.first; - if (value.has_value()) - return std::any_cast(value); - return std::nullopt; - } - return std::nullopt; - } - catch (const std::bad_any_cast&) - { - throw std::runtime_error(Format("Value of key \"{}\" in AnyMap is not of the type {}.", ToUtf8String(key), typeid(T).name())); - } - } - - // return the value if the value exists and the type of value is T. - // throw if value doesn't exists. (different from "GetOptionalValue"). - // throw std::runtime_error if type is mismatch. - template - T GetValue(const String& key) const - { - const auto optional_value = GetOptionalValue(key); - if (optional_value.has_value()) - return optional_value.value(); - else - throw std::runtime_error(Format("Key \"{}\" does not exists in AnyMap.", ToUtf8String(key))); - } - - // Set the value of key, and trigger all related listeners. - template - void SetValue(const String& key, T&& value) - { - auto& p = map_[key]; - p.first = std::make_any(std::forward(value)); - InvokeListeners(p.second, p.first); - } - - // Remove the value of the key. - void ClearValue(const String& key) - { - auto& p = map_[key]; - p.first = std::any{}; - InvokeListeners(p.second, std::any{}); - } - - // Add a listener which is called when value of key is changed. - // Return a token used to remove the listener. - ListenerToken RegisterValueChangeListener(const String& key, const Listener& listener); - - // Remove a listener by token. - void UnregisterValueChangeListener(ListenerToken token); - - private: - void InvokeListeners(std::list& listener_list, const std::any& value); - - private: - std::unordered_map>> map_{}; - std::unordered_map listeners_{}; - ListenerToken current_listener_token_ = 0; - }; -} diff --git a/src/application.cpp b/src/application.cpp index c3669f72..e580b56b 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -2,135 +2,113 @@ #include "exception.hpp" #include "timer.hpp" -#include "ui/window.hpp" #include "ui/cursor.hpp" +#include "ui/window_class.hpp" namespace cru { - constexpr auto god_window_class_name = L"GodWindowClass"; - constexpr int invoke_later_message_id = WM_USER + 2000; - - - LRESULT CALLBACK GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - const auto app = Application::GetInstance(); - - if (app) - { - const auto result = app->GetGodWindow()->HandleGodWindowMessage(hWnd, uMsg, wParam, lParam); - if (result.has_value()) - return result.value(); - else - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } - else - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } +constexpr auto god_window_class_name = L"GodWindowClass"; +constexpr int invoke_later_message_id = WM_USER + 2000; + +LRESULT CALLBACK GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) { + const auto app = Application::GetInstance(); + + if (app) { + const auto result = + app->GetGodWindow()->HandleGodWindowMessage(hWnd, uMsg, wParam, lParam); + if (result.has_value()) + return result.value(); + else + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } else + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} - GodWindow::GodWindow(Application* application) - { - const auto h_instance = application->GetInstanceHandle(); +GodWindow::GodWindow(Application* application) { + const auto h_instance = application->GetInstanceHandle(); - god_window_class_ = std::make_unique(god_window_class_name, GodWndProc, h_instance); + god_window_class_ = std::make_unique(god_window_class_name, + GodWndProc, h_instance); - hwnd_ = CreateWindowEx(0, - god_window_class_name, - L"", 0, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - HWND_MESSAGE, nullptr, h_instance, nullptr - ); + hwnd_ = CreateWindowEx(0, god_window_class_name, L"", 0, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + HWND_MESSAGE, nullptr, h_instance, nullptr); - if (hwnd_ == nullptr) - throw std::runtime_error("Failed to create window."); - } + if (hwnd_ == nullptr) throw std::runtime_error("Failed to create window."); +} - GodWindow::~GodWindow() - { - ::DestroyWindow(hwnd_); +GodWindow::~GodWindow() { ::DestroyWindow(hwnd_); } + +std::optional GodWindow::HandleGodWindowMessage(HWND hwnd, int msg, + WPARAM w_param, + LPARAM l_param) { + switch (msg) { + case invoke_later_message_id: { + const auto p_action = reinterpret_cast*>(w_param); + (*p_action)(); + delete p_action; + return 0; } - - std::optional GodWindow::HandleGodWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param) - { - switch (msg) - { - case invoke_later_message_id: - { - const auto p_action = reinterpret_cast*>(w_param); - (*p_action)(); - delete p_action; - return 0; - } - case WM_TIMER: - { - const auto id = static_cast(w_param); - const auto action = TimerManager::GetInstance()->GetAction(id); - if (action.has_value()) - { - (action.value().second)(); - if (!action.value().first) - TimerManager::GetInstance()->KillTimer(id); - return 0; - } - break; - } - default: - return std::nullopt; - } - return std::nullopt; + case WM_TIMER: { + const auto id = static_cast(w_param); + const auto action = TimerManager::GetInstance()->GetAction(id); + if (action.has_value()) { + (action.value().second)(); + if (!action.value().first) TimerManager::GetInstance()->KillTimer(id); + return 0; + } + break; } + default: + return std::nullopt; + } + return std::nullopt; +} +Application* Application::instance_ = nullptr; +Application* Application::GetInstance() { return instance_; } - Application* Application::instance_ = nullptr; - - Application * Application::GetInstance() { - return instance_; - } - - Application::Application(HINSTANCE h_instance) - : h_instance_(h_instance) { - - if (instance_) - throw std::runtime_error("A application instance already exists."); +Application::Application(HINSTANCE h_instance) : h_instance_(h_instance) { + if (instance_) + throw std::runtime_error("A application instance already exists."); - instance_ = this; + instance_ = this; - if (!::IsWindows8OrGreater()) - throw std::runtime_error("Must run on Windows 8 or later."); + if (!::IsWindows8OrGreater()) + throw std::runtime_error("Must run on Windows 8 or later."); - god_window_ = std::make_unique(this); + god_window_ = std::make_unique(this); - ui::cursors::LoadSystemCursors(); - } + ui::cursors::LoadSystemCursors(); +} - Application::~Application() - { - for (auto i = singleton_list_.crbegin(); i != singleton_list_.crend(); ++i) - delete *i; - instance_ = nullptr; - } +Application::~Application() { + for (auto i = singleton_list_.crbegin(); i != singleton_list_.crend(); ++i) + delete *i; + instance_ = nullptr; +} - int Application::Run() - { - MSG msg; +int Application::Run() { + MSG msg; - while (GetMessage(&msg, nullptr, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + while (GetMessage(&msg, nullptr, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } - return static_cast(msg.wParam); - } + return static_cast(msg.wParam); +} - void Application::Quit(const int quit_code) { - ::PostQuitMessage(quit_code); - } +void Application::Quit(const int quit_code) { ::PostQuitMessage(quit_code); } - void InvokeLater(const std::function& action) { - //copy the action to a safe place - auto p_action_copy = new std::function(action); +void InvokeLater(const std::function& action) { + // copy the action to a safe place + auto p_action_copy = new std::function(action); - if (PostMessageW(Application::GetInstance()->GetGodWindow()->GetHandle(), invoke_later_message_id, reinterpret_cast(p_action_copy), 0) == 0) - throw Win32Error(::GetLastError(), "InvokeLater failed to post message."); - } + if (PostMessageW(Application::GetInstance()->GetGodWindow()->GetHandle(), + invoke_later_message_id, + reinterpret_cast(p_action_copy), 0) == 0) + throw Win32Error(::GetLastError(), "InvokeLater failed to post message."); } +} // namespace cru diff --git a/src/application.hpp b/src/application.hpp index 8d739938..acf264c3 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -1,14 +1,12 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" +#include #include #include -#include -#include #include +#include +#include "system_headers.hpp" #include "base.hpp" @@ -16,103 +14,92 @@ #include #endif -namespace cru -{ - class Application; - - namespace ui - { - class WindowClass; - } - - - class GodWindow : public Object - { - public: - explicit GodWindow(Application* application); - GodWindow(const GodWindow& other) = delete; - GodWindow(GodWindow&& other) = delete; - GodWindow& operator=(const GodWindow& other) = delete; - GodWindow& operator=(GodWindow&& other) = delete; - ~GodWindow() override; - - HWND GetHandle() const - { - return hwnd_; - } - - std::optional HandleGodWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param); - - private: - std::unique_ptr god_window_class_; - HWND hwnd_; - }; - - class Application : public Object - { - public: - static Application* GetInstance(); - private: - static Application* instance_; - - public: - explicit Application(HINSTANCE h_instance); - Application(const Application&) = delete; - Application(Application&&) = delete; - Application& operator = (const Application&) = delete; - Application& operator = (Application&&) = delete; - ~Application() override; - - public: - int Run(); - void Quit(int quit_code); - - - HINSTANCE GetInstanceHandle() const - { - return h_instance_; - } - - GodWindow* GetGodWindow() const - { - return god_window_.get(); - } - - // Resolve a singleton. - // All singletons will be delete in reverse order of resolve. - template>> - T* ResolveSingleton(const std::function& creator) - { - const auto& index = std::type_index{typeid(T)}; - const auto find_result = singleton_map_.find(index); - if (find_result != singleton_map_.cend()) - return static_cast(find_result->second); +namespace cru { +class Application; + +namespace ui { +class WindowClass; +} + +class GodWindow : public Object { + public: + explicit GodWindow(Application* application); + GodWindow(const GodWindow& other) = delete; + GodWindow(GodWindow&& other) = delete; + GodWindow& operator=(const GodWindow& other) = delete; + GodWindow& operator=(GodWindow&& other) = delete; + ~GodWindow() override; + + HWND GetHandle() const { return hwnd_; } + + std::optional HandleGodWindowMessage(HWND hwnd, int msg, + WPARAM w_param, LPARAM l_param); + + private: + std::unique_ptr god_window_class_; + HWND hwnd_; +}; + +class Application : public Object { + public: + static Application* GetInstance(); + + private: + static Application* instance_; + + public: + explicit Application(HINSTANCE h_instance); + Application(const Application&) = delete; + Application(Application&&) = delete; + Application& operator=(const Application&) = delete; + Application& operator=(Application&&) = delete; + ~Application() override; + + public: + int Run(); + void Quit(int quit_code); + + HINSTANCE GetInstanceHandle() const { return h_instance_; } + + GodWindow* GetGodWindow() const { return god_window_.get(); } + + // Resolve a singleton. + // All singletons will be delete in reverse order of resolve. + template >> + T* ResolveSingleton(const std::function& creator) { + const auto& index = std::type_index{typeid(T)}; + const auto find_result = singleton_map_.find(index); + if (find_result != singleton_map_.cend()) + return static_cast(find_result->second); #ifdef CRU_DEBUG - const auto type_find_result = singleton_type_set_.find(index); - if (type_find_result != singleton_type_set_.cend()) - throw std::logic_error("The singleton of that type is being constructed. This may cause a dead recursion."); - singleton_type_set_.insert(index); + const auto type_find_result = singleton_type_set_.find(index); + if (type_find_result != singleton_type_set_.cend()) + throw std::logic_error( + "The singleton of that type is being constructed. This may cause a " + "dead recursion."); + singleton_type_set_.insert(index); #endif - auto singleton = creator(this); - singleton_map_.emplace(index, static_cast(singleton)); - singleton_list_.push_back(singleton); - return singleton; - } + auto singleton = creator(this); + singleton_map_.emplace(index, static_cast(singleton)); + singleton_list_.push_back(singleton); + return singleton; + } - private: - HINSTANCE h_instance_; + private: + HINSTANCE h_instance_; - std::unique_ptr god_window_; + std::unique_ptr god_window_; - std::unordered_map singleton_map_; - std::list singleton_list_; // used for reverse destroy. + std::unordered_map singleton_map_; + std::list singleton_list_; // used for reverse destroy. #ifdef CRU_DEBUG - std::unordered_set singleton_type_set_; // used for detecting dead recursion. + std::unordered_set + singleton_type_set_; // used for detecting dead recursion. #endif - }; - +}; - void InvokeLater(const std::function& action); -} +void InvokeLater(const std::function& action); +} // namespace cru diff --git a/src/base.cpp b/src/base.cpp deleted file mode 100644 index a2d20fc4..00000000 --- a/src/base.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "base.hpp" - -#include "system_headers.hpp" -#include "exception.hpp" - -namespace cru -{ - MultiByteString ToUtf8String(const StringView& string) - { - if (string.empty()) - return MultiByteString(); - - const auto length = ::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, nullptr, 0, nullptr, nullptr); - MultiByteString result; - result.reserve(length); - if (::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, result.data(), static_cast(result.capacity()), nullptr, nullptr) == 0) - throw Win32Error(::GetLastError(), "Failed to convert wide string to UTF-8."); - return result; - } -} diff --git a/src/base.hpp b/src/base.hpp index 64ce7f6e..a62a745b 100644 --- a/src/base.hpp +++ b/src/base.hpp @@ -1,59 +1,46 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include +#include #include +#include #include -#include -namespace cru -{ - template struct type_tag {}; - - //typedefs - using String = std::wstring; - using MultiByteString = std::string; - - using StringView = std::wstring_view; - using MultiByteStringView = std::string_view; - - using FloatSecond = std::chrono::duration; - - enum class FlowControl - { - Continue, - Break - }; - - - class Object - { - public: - Object() = default; - Object(const Object&) = default; - Object& operator = (const Object&) = default; - Object(Object&&) = default; - Object& operator = (Object&&) = default; - virtual ~Object() = default; - }; - - struct Interface - { - virtual ~Interface() = default; - }; - - [[noreturn]] inline void UnreachableCode() - { - throw std::logic_error("Unreachable code."); - } - - MultiByteString ToUtf8String(const StringView& string); - - inline void Require(const bool condition, const MultiByteStringView& error_message) - { - if (!condition) - throw std::invalid_argument(error_message.data()); - } +namespace cru { +template +struct type_tag {}; + +// typedefs +using String = std::wstring; +using MultiByteString = std::string; + +using StringView = std::wstring_view; +using MultiByteStringView = std::string_view; + +using FloatSecond = std::chrono::duration; + +enum class FlowControl { Continue, Break }; + +class Object { + public: + Object() = default; + Object(const Object&) = default; + Object& operator=(const Object&) = default; + Object(Object&&) = default; + Object& operator=(Object&&) = default; + virtual ~Object() = default; +}; + +struct Interface { + virtual ~Interface() = default; +}; + +[[noreturn]] inline void UnreachableCode() { + throw std::logic_error("Unreachable code."); +} + +inline void Require(const bool condition, + const MultiByteStringView& error_message) { + if (!condition) throw std::invalid_argument(error_message.data()); } +} // namespace cru diff --git a/src/cru_debug.cpp b/src/cru_debug.cpp index 9c61d052..b9226132 100644 --- a/src/cru_debug.cpp +++ b/src/cru_debug.cpp @@ -2,10 +2,8 @@ #include "system_headers.hpp" -namespace cru::debug -{ - void DebugMessage(const StringView& message) - { - ::OutputDebugStringW(message.data()); - } +namespace cru::debug { +void DebugMessage(const StringView& message) { + ::OutputDebugStringW(message.data()); } +} // namespace cru::debug diff --git a/src/cru_debug.hpp b/src/cru_debug.hpp index 17cc7b53..9c22a24f 100644 --- a/src/cru_debug.hpp +++ b/src/cru_debug.hpp @@ -1,47 +1,46 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" #include #include "base.hpp" -#include "format.hpp" +#include "util/format.hpp" -namespace cru::debug -{ - void DebugMessage(const StringView& message); +namespace cru::debug { +void DebugMessage(const StringView& message); #ifdef CRU_DEBUG - inline void DebugTime(const std::function& action, const StringView& hint_message) - { - const auto before = std::chrono::steady_clock::now(); - action(); - const auto after = std::chrono::steady_clock::now(); - const auto duration = std::chrono::duration_cast(after - before); - DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count())); - } +inline void DebugTime(const std::function& action, + const StringView& hint_message) { + const auto before = std::chrono::steady_clock::now(); + action(); + const auto after = std::chrono::steady_clock::now(); + const auto duration = + std::chrono::duration_cast(after - before); + DebugMessage(util::Format(L"{}: {}ms.\n", hint_message, duration.count())); +} - template - TReturn DebugTime(const std::function& action, const StringView& hint_message) - { - const auto before = std::chrono::steady_clock::now(); - auto&& result = action(); - const auto after = std::chrono::steady_clock::now(); - const auto duration = std::chrono::duration_cast(after - before); - DebugMessage(Format(L"{}: {}ms.\n", hint_message, duration.count())); - return std::move(result); - } +template +TReturn DebugTime(const std::function& action, + const StringView& hint_message) { + const auto before = std::chrono::steady_clock::now(); + auto&& result = action(); + const auto after = std::chrono::steady_clock::now(); + const auto duration = + std::chrono::duration_cast(after - before); + DebugMessage(util::Format(L"{}: {}ms.\n", hint_message, duration.count())); + return std::move(result); +} #else - inline void DebugTime(const std::function& action, const StringView& hint_message) - { - action(); - } +inline void DebugTime(const std::function& action, + const StringView& hint_message) { + action(); +} - template - TReturn DebugTime(const std::function& action, const StringView& hint_message) - { - return action(); - } -#endif +template +TReturn DebugTime(const std::function& action, + const StringView& hint_message) { + return action(); } +#endif +} // namespace cru::debug diff --git a/src/cru_event.hpp b/src/cru_event.hpp index fa41d646..a669695f 100644 --- a/src/cru_event.hpp +++ b/src/cru_event.hpp @@ -1,84 +1,68 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include #include #include +#include #include "base.hpp" namespace cru { - //Base class of all event args. - class BasicEventArgs : public Object - { - public: - explicit BasicEventArgs(Object* sender) - : sender_(sender) - { - - } - BasicEventArgs(const BasicEventArgs& other) = default; - BasicEventArgs(BasicEventArgs&& other) = default; - BasicEventArgs& operator=(const BasicEventArgs& other) = default; - BasicEventArgs& operator=(BasicEventArgs&& other) = default; - ~BasicEventArgs() override = default; - - //Get the sender of the event. - Object* GetSender() const - { - return sender_; - } - - private: - Object* sender_; - }; - - - //A non-copyable non-movable Event class. - //It stores a list of event handlers. - //TArgsType must be subclass of BasicEventArgs. - template - class Event - { - public: - static_assert(std::is_base_of_v, - "TArgsType must be subclass of BasicEventArgs."); - - - using ArgsType = TArgsType; - using EventHandler = std::function; - using EventHandlerToken = long; - - Event() = default; - Event(const Event&) = delete; - Event& operator = (const Event&) = delete; - Event(Event&&) = delete; - Event& operator = (Event&&) = delete; - ~Event() = default; - - EventHandlerToken AddHandler(const EventHandler& handler) - { - const auto token = current_token_++; - handlers_.emplace(token, handler); - return token; - } - - void RemoveHandler(const EventHandlerToken token) { - auto find_result = handlers_.find(token); - if (find_result != handlers_.cend()) - handlers_.erase(find_result); - } - - void Raise(ArgsType& args) { - for (const auto& handler : handlers_) - (handler.second)(args); - } - - private: - std::map handlers_; - - EventHandlerToken current_token_ = 0; - }; -} +// Base class of all event args. +class BasicEventArgs : public Object { + public: + explicit BasicEventArgs(Object* sender) : sender_(sender) {} + BasicEventArgs(const BasicEventArgs& other) = default; + BasicEventArgs(BasicEventArgs&& other) = default; + BasicEventArgs& operator=(const BasicEventArgs& other) = default; + BasicEventArgs& operator=(BasicEventArgs&& other) = default; + ~BasicEventArgs() override = default; + + // Get the sender of the event. + Object* GetSender() const { return sender_; } + + private: + Object* sender_; +}; + +// A non-copyable non-movable Event class. +// It stores a list of event handlers. +// TArgsType must be subclass of BasicEventArgs. +template +class Event { + public: + static_assert(std::is_base_of_v, + "TArgsType must be subclass of BasicEventArgs."); + + using ArgsType = TArgsType; + using EventHandler = std::function; + using EventHandlerToken = long; + + Event() = default; + Event(const Event&) = delete; + Event& operator=(const Event&) = delete; + Event(Event&&) = delete; + Event& operator=(Event&&) = delete; + ~Event() = default; + + EventHandlerToken AddHandler(const EventHandler& handler) { + const auto token = current_token_++; + handlers_.emplace(token, handler); + return token; + } + + void RemoveHandler(const EventHandlerToken token) { + auto find_result = handlers_.find(token); + if (find_result != handlers_.cend()) handlers_.erase(find_result); + } + + void Raise(ArgsType& args) { + for (const auto& handler : handlers_) (handler.second)(args); + } + + private: + std::map handlers_; + + EventHandlerToken current_token_ = 0; +}; +} // namespace cru diff --git a/src/exception.cpp b/src/exception.cpp index cb1ca2c7..dbc98453 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -1,40 +1,42 @@ #include "exception.hpp" -#include "format.hpp" - -namespace cru -{ - inline std::string HResultMakeMessage(HRESULT h_result, std::optional message) - { - char buffer[10]; - sprintf_s(buffer, "%#08x", h_result); - - if (message.has_value()) - return Format("An HResultError is thrown. HRESULT: {}.\nAdditional message: {}\n", buffer, message.value()); - else - return Format("An HResultError is thrown. HRESULT: {}.\n", buffer); - } - - HResultError::HResultError(HRESULT h_result, std::optional additional_message) - : runtime_error(HResultMakeMessage(h_result, std::nullopt)), h_result_(h_result) - { - - } - - inline std::string Win32MakeMessage(DWORD error_code, std::optional message) - { - char buffer[10]; - sprintf_s(buffer, "%#04x", error_code); - - if (message.has_value()) - return Format("Last error code: {}.\nAdditional message: {}\n", buffer, message.value()); - else - return Format("Last error code: {}.\n", buffer); - } - - Win32Error::Win32Error(DWORD error_code, std::optional additional_message) - : runtime_error(Win32MakeMessage(error_code, std::nullopt)), error_code_(error_code) - { - - } +#include "util/format.hpp" + +namespace cru { +using util::Format; + +inline std::string HResultMakeMessage( + HRESULT h_result, std::optional message) { + char buffer[10]; + sprintf_s(buffer, "%#08x", h_result); + + if (message.has_value()) + return Format( + "An HResultError is thrown. HRESULT: {}.\nAdditional message: {}\n", + buffer, message.value()); + else + return Format("An HResultError is thrown. HRESULT: {}.\n", buffer); } + +HResultError::HResultError( + HRESULT h_result, std::optional additional_message) + : runtime_error(HResultMakeMessage(h_result, std::nullopt)), + h_result_(h_result) {} + +inline std::string Win32MakeMessage( + DWORD error_code, std::optional message) { + char buffer[10]; + sprintf_s(buffer, "%#04x", error_code); + + if (message.has_value()) + return Format("Last error code: {}.\nAdditional message: {}\n", buffer, + message.value()); + else + return Format("Last error code: {}.\n", buffer); +} + +Win32Error::Win32Error(DWORD error_code, + std::optional additional_message) + : runtime_error(Win32MakeMessage(error_code, std::nullopt)), + error_code_(error_code) {} +} // namespace cru diff --git a/src/exception.hpp b/src/exception.hpp index b8cef604..db2572f1 100644 --- a/src/exception.hpp +++ b/src/exception.hpp @@ -1,60 +1,52 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" #include +#include "system_headers.hpp" #include "base.hpp" - namespace cru { - class HResultError : public std::runtime_error - { - public: - explicit HResultError(HRESULT h_result, std::optional additional_message = std::nullopt); - HResultError(const HResultError& other) = default; - HResultError(HResultError&& other) = default; - HResultError& operator=(const HResultError& other) = default; - HResultError& operator=(HResultError&& other) = default; - ~HResultError() override = default; - - HRESULT GetHResult() const - { - return h_result_; - } - - private: - HRESULT h_result_; - }; - - inline void ThrowIfFailed(const HRESULT h_result) { - if (FAILED(h_result)) - throw HResultError(h_result); - } - - inline void ThrowIfFailed(const HRESULT h_result, const MultiByteStringView& message) { - if (FAILED(h_result)) - throw HResultError(h_result, message); - } - - class Win32Error : public std::runtime_error - { - public: - explicit Win32Error(DWORD error_code, std::optional additional_message = std::nullopt); - Win32Error(const Win32Error& other) = default; - Win32Error(Win32Error&& other) = default; - Win32Error& operator=(const Win32Error& other) = default; - Win32Error& operator=(Win32Error&& other) = default; - ~Win32Error() override = default; - - HRESULT GetErrorCode() const - { - return error_code_; - } - - private: - DWORD error_code_; - }; +class HResultError : public std::runtime_error { + public: + explicit HResultError( + HRESULT h_result, + std::optional additional_message = std::nullopt); + HResultError(const HResultError& other) = default; + HResultError(HResultError&& other) = default; + HResultError& operator=(const HResultError& other) = default; + HResultError& operator=(HResultError&& other) = default; + ~HResultError() override = default; + + HRESULT GetHResult() const { return h_result_; } + + private: + HRESULT h_result_; +}; + +inline void ThrowIfFailed(const HRESULT h_result) { + if (FAILED(h_result)) throw HResultError(h_result); } + +inline void ThrowIfFailed(const HRESULT h_result, + const MultiByteStringView& message) { + if (FAILED(h_result)) throw HResultError(h_result, message); +} + +class Win32Error : public std::runtime_error { + public: + explicit Win32Error( + DWORD error_code, + std::optional additional_message = std::nullopt); + Win32Error(const Win32Error& other) = default; + Win32Error(Win32Error&& other) = default; + Win32Error& operator=(const Win32Error& other) = default; + Win32Error& operator=(Win32Error&& other) = default; + ~Win32Error() override = default; + + HRESULT GetErrorCode() const { return error_code_; } + + private: + DWORD error_code_; +}; +} // namespace cru diff --git a/src/format.hpp b/src/format.hpp deleted file mode 100644 index efd25f89..00000000 --- a/src/format.hpp +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "base.hpp" - -namespace cru -{ - namespace details - { - constexpr StringView PlaceHolder(type_tag) - { - return StringView(L"{}"); - } - - constexpr MultiByteStringView PlaceHolder(type_tag) - { - return MultiByteStringView("{}"); - } - - template - void FormatInternal(TString& string) - { - const auto find_result = string.find(PlaceHolder(type_tag{})); - if (find_result != TString::npos) - throw std::invalid_argument("There is more placeholders than args."); - } - - template - void FormatInternal(TString& string, const T& arg, const TRest&... args) - { - const auto find_result = string.find(PlaceHolder(type_tag{})); - if (find_result == TString::npos) - throw std::invalid_argument("There is less placeholders than args."); - - string.replace(find_result, 2, FormatToString(arg, type_tag{})); - FormatInternal(string, args...); - } - } - - template - String Format(const StringView& format, const T&... args) - { - String result(format); - details::FormatInternal(result, args...); - return result; - } - - template - MultiByteString Format(const MultiByteStringView& format, const T&... args) - { - MultiByteString result(format); - details::FormatInternal(result, args...); - return result; - } - -#define CRU_FORMAT_NUMBER(type) \ - inline String FormatToString(const type number, type_tag) \ - { \ - return std::to_wstring(number); \ - } \ - inline MultiByteString FormatToString(const type number, type_tag) \ - { \ - return std::to_string(number); \ - } - - CRU_FORMAT_NUMBER(int) - CRU_FORMAT_NUMBER(short) - CRU_FORMAT_NUMBER(long) - CRU_FORMAT_NUMBER(long long) - CRU_FORMAT_NUMBER(unsigned int) - CRU_FORMAT_NUMBER(unsigned short) - CRU_FORMAT_NUMBER(unsigned long) - CRU_FORMAT_NUMBER(unsigned long long) - CRU_FORMAT_NUMBER(float) - CRU_FORMAT_NUMBER(double) - -#undef CRU_FORMAT_NUMBER - - inline StringView FormatToString(const String& string, type_tag) - { - return string; - } - - inline MultiByteString FormatToString(const MultiByteString& string, type_tag) - { - return string; - } - - inline StringView FormatToString(const StringView& string, type_tag) - { - return string; - } - - inline MultiByteStringView FormatToString(const MultiByteStringView& string, type_tag) - { - return string; - } - - inline StringView FormatToString(const wchar_t* string, type_tag) - { - return StringView(string); - } - - inline MultiByteStringView FormatToString(const char* string, type_tag) - { - return MultiByteString(string); - } -} diff --git a/src/math_util.hpp b/src/math_util.hpp deleted file mode 100644 index 8f0741b8..00000000 --- a/src/math_util.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -// ReSharper disable once CppUnusedIncludeDirective -#include -#include - -namespace cru -{ - template >> - float Coerce(const T n, const std::optional min, const std::optional max) - { - if (min.has_value() && n < min.value()) - return min.value(); - if (max.has_value() && n > max.value()) - return max.value(); - return n; - } - - template >> - float Coerce(const T n, const T min, const T max) - { - if (n < min) - return min; - if (n > max) - return max; - return n; - } - - template >> - float Coerce(const T n, const std::nullopt_t, const std::optional max) - { - if (max.has_value() && n > max.value()) - return max.value(); - return n; - } - - template >> - float Coerce(const T n, const std::optional min, const std::nullopt_t) - { - if (min.has_value() && n < min.value()) - return min.value(); - return n; - } - - template >> - float Coerce(const T n, const std::nullopt_t, const T max) - { - if (n > max) - return max; - return n; - } - - template >> - float Coerce(const T n, const T min, const std::nullopt_t) - { - if (n < min) - return min; - return n; - } - - template >> - T AtLeast0(const T value) - { - return value < static_cast(0) ? static_cast(0) : value; - } -} diff --git a/src/pre.hpp b/src/pre.hpp index 03c51a94..ba47dc6c 100644 --- a/src/pre.hpp +++ b/src/pre.hpp @@ -8,11 +8,8 @@ #define CRU_DEBUG_LAYOUT #endif - #ifdef CRU_DEBUG -// ReSharper disable once IdentifierTypo -// ReSharper disable once CppInconsistentNaming #define _CRTDBG_MAP_ALLOC -#include #include +#include #endif diff --git a/src/system_headers.hpp b/src/system_headers.hpp index eabc7c25..6cbad697 100644 --- a/src/system_headers.hpp +++ b/src/system_headers.hpp @@ -1,6 +1,4 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" //include system headers diff --git a/src/timer.cpp b/src/timer.cpp index c839a48d..40e32640 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -2,62 +2,51 @@ #include "application.hpp" -namespace cru -{ - TimerManager* TimerManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton([](auto) - { - return new TimerManager{}; - }); - } - - UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop, const TimerAction& action) - { - const auto id = current_count_++; - ::SetTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id, milliseconds, nullptr); - map_.emplace(id, std::make_pair(loop, action)); - return id; - } - - void TimerManager::KillTimer(const UINT_PTR id) - { - const auto find_result = map_.find(id); - if (find_result != map_.cend()) - { - ::KillTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id); - map_.erase(find_result); - } - } - - std::optional> TimerManager::GetAction(const UINT_PTR id) - { - const auto find_result = map_.find(id); - if (find_result == map_.cend()) - return std::nullopt; - return find_result->second; - } - - TimerTask::TimerTask(const UINT_PTR id) - : id_(id) - { - - } - - void TimerTask::Cancel() const - { - TimerManager::GetInstance()->KillTimer(id_); - } - - TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action) - { - const auto id = TimerManager::GetInstance()->CreateTimer(static_cast(milliseconds.count()), false, action); - return TimerTask(id); - } - - TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action) - { - const auto id = TimerManager::GetInstance()->CreateTimer(static_cast(milliseconds.count()), true, action); - return TimerTask(id); - } +namespace cru { +TimerManager* TimerManager::GetInstance() { + return Application::GetInstance()->ResolveSingleton( + [](auto) { return new TimerManager{}; }); } + +UINT_PTR TimerManager::CreateTimer(const UINT milliseconds, const bool loop, + const TimerAction& action) { + const auto id = current_count_++; + ::SetTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id, + milliseconds, nullptr); + map_.emplace(id, std::make_pair(loop, action)); + return id; +} + +void TimerManager::KillTimer(const UINT_PTR id) { + const auto find_result = map_.find(id); + if (find_result != map_.cend()) { + ::KillTimer(Application::GetInstance()->GetGodWindow()->GetHandle(), id); + map_.erase(find_result); + } +} + +std::optional> TimerManager::GetAction( + const UINT_PTR id) { + const auto find_result = map_.find(id); + if (find_result == map_.cend()) return std::nullopt; + return find_result->second; +} + +TimerTask::TimerTask(const UINT_PTR id) : id_(id) {} + +void TimerTask::Cancel() const { TimerManager::GetInstance()->KillTimer(id_); } + +TimerTask SetTimeout(std::chrono::milliseconds milliseconds, + const TimerAction& action) { + const auto id = TimerManager::GetInstance()->CreateTimer( + static_cast(milliseconds.count()), false, action); + return TimerTask(id); +} + +TimerTask SetInterval(std::chrono::milliseconds milliseconds, + const TimerAction& action) { + const auto id = TimerManager::GetInstance()->CreateTimer( + static_cast(milliseconds.count()), true, action); + return TimerTask(id); +} +} // namespace cru diff --git a/src/timer.hpp b/src/timer.hpp index 5055a3d8..685e83b9 100644 --- a/src/timer.hpp +++ b/src/timer.hpp @@ -1,64 +1,64 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" -#include #include #include +#include #include +#include "system_headers.hpp" #include "base.hpp" -namespace cru -{ - using TimerAction = std::function; +namespace cru { +using TimerAction = std::function; + +class TimerManager : public Object { + public: + static TimerManager* GetInstance(); - class TimerManager : public Object - { - public: - static TimerManager* GetInstance(); + private: + TimerManager() = default; - private: - TimerManager() = default; - public: - TimerManager(const TimerManager& other) = delete; - TimerManager(TimerManager&& other) = delete; - TimerManager& operator=(const TimerManager& other) = delete; - TimerManager& operator=(TimerManager&& other) = delete; - ~TimerManager() override = default; + public: + TimerManager(const TimerManager& other) = delete; + TimerManager(TimerManager&& other) = delete; + TimerManager& operator=(const TimerManager& other) = delete; + TimerManager& operator=(TimerManager&& other) = delete; + ~TimerManager() override = default; - UINT_PTR CreateTimer(UINT milliseconds, bool loop, const TimerAction& action); - void KillTimer(UINT_PTR id); - std::optional> GetAction(UINT_PTR id); + UINT_PTR CreateTimer(UINT milliseconds, bool loop, const TimerAction& action); + void KillTimer(UINT_PTR id); + std::optional> GetAction(UINT_PTR id); - private: - std::map> map_{}; - UINT_PTR current_count_ = 0; - }; + private: + std::map> map_{}; + UINT_PTR current_count_ = 0; +}; - class TimerTask - { - friend TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action); - friend TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action); +class TimerTask { + friend TimerTask SetTimeout(std::chrono::milliseconds milliseconds, + const TimerAction& action); + friend TimerTask SetInterval(std::chrono::milliseconds milliseconds, + const TimerAction& action); - private: - explicit TimerTask(UINT_PTR id); + private: + explicit TimerTask(UINT_PTR id); - public: - TimerTask(const TimerTask& other) = default; - TimerTask(TimerTask&& other) = default; - TimerTask& operator=(const TimerTask& other) = default; - TimerTask& operator=(TimerTask&& other) = default; - ~TimerTask() = default; + public: + TimerTask(const TimerTask& other) = default; + TimerTask(TimerTask&& other) = default; + TimerTask& operator=(const TimerTask& other) = default; + TimerTask& operator=(TimerTask&& other) = default; + ~TimerTask() = default; - void Cancel() const; + void Cancel() const; - private: - UINT_PTR id_; - }; + private: + UINT_PTR id_; +}; - TimerTask SetTimeout(std::chrono::milliseconds milliseconds, const TimerAction& action); - TimerTask SetInterval(std::chrono::milliseconds milliseconds, const TimerAction& action); -} +TimerTask SetTimeout(std::chrono::milliseconds milliseconds, + const TimerAction& action); +TimerTask SetInterval(std::chrono::milliseconds milliseconds, + const TimerAction& action); +} // namespace cru diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 9388c719..617c50c7 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -17,53 +17,6 @@ namespace cru::ui { - Control::Control() - { - mouse_leave_event.bubble.AddHandler([this](events::MouseEventArgs& args) - { - if (args.GetOriginalSender() != this) - return; - for (auto& is_mouse_click_valid : is_mouse_click_valid_map_) - { - if (is_mouse_click_valid.second) - { - is_mouse_click_valid.second = false; - OnMouseClickEnd(is_mouse_click_valid.first); - } - } - }); - - mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetOriginalSender() != this) - return; - - if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender()) - RequestFocus(); - const auto button = args.GetMouseButton(); - is_mouse_click_valid_map_[button] = true; - OnMouseClickBegin(button); - }); - - mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args) - { - if (args.GetOriginalSender() != this) - return; - - const auto button = args.GetMouseButton(); - if (is_mouse_click_valid_map_[button]) - { - is_mouse_click_valid_map_[button] = false; - OnMouseClickEnd(button); - const auto point = args.GetPoint(GetWindow()); - InvokeLater([this, button, point] - { - DispatchEvent(this, &Control::mouse_click_event, nullptr, point, button); - }); - } - }); - } - void Control::SetParent(Control* parent) { @@ -74,19 +27,6 @@ namespace cru::ui OnParentChanged(old_parent, new_parent); } - void Control::SetInternalParent(Control* internal_parent) - { - const auto old_internal_parent = GetInternalParent(); - const auto old_parent = GetParent(); - internal_parent_ = internal_parent; - const auto new_internal_parent = GetInternalParent(); - const auto new_parent = GetParent(); - if (old_parent != new_parent) - OnParentChanged(old_parent, new_parent); - if (old_internal_parent != new_internal_parent) - OnInternalParentChanged(old_internal_parent, new_internal_parent); - } - void Control::SetDescendantWindow(Window* window) { if (window == nullptr && window_ == nullptr) @@ -112,199 +52,29 @@ namespace cru::ui }); } - - void TraverseDescendantsInternal(Control* control, const std::function& predicate) - { - predicate(control); - for (auto c: control->GetInternalChildren()) - TraverseDescendantsInternal(c, predicate); - } - void Control::TraverseDescendants(const std::function& predicate) { TraverseDescendantsInternal(this, predicate); } - Point Control::GetOffset() - { - return rect_.GetLeftTop(); - } - - Size Control::GetSize() - { - return rect_.GetSize(); - } - - void Control::SetRect(const Rect& rect) - { - const auto old_rect = rect_; - rect_ = rect; - - RegenerateGeometryInfo(); - - OnRectChange(old_rect, rect); - - if (auto window = GetWindow()) - window->InvalidateDraw(); - } - - namespace + void Control::TraverseDescendantsInternal(Control * control, const std::function& predicate) { -#ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr CalculateSquareRingGeometry(const Rect& out, const Rect& in) - { - const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); - Microsoft::WRL::ComPtr out_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry)); - Microsoft::WRL::ComPtr in_geometry; - ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry)); - Microsoft::WRL::ComPtr result_geometry; - ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry)); - Microsoft::WRL::ComPtr sink; - ThrowIfFailed(result_geometry->Open(&sink)); - ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get())); - ThrowIfFailed(sink->Close()); - return result_geometry; - } -#endif + predicate(control); + for (auto c: control->GetInternalChildren()) + TraverseDescendantsInternal(c, predicate); } - Point Control::GetPositionAbsolute() const - { - return position_cache_.lefttop_position_absolute; - } Point Control::ControlToWindow(const Point& point) const { - return Point(point.x + position_cache_.lefttop_position_absolute.x, - point.y + position_cache_.lefttop_position_absolute.y); + const auto position = GetPositionInWindow(); + return Point(point.x + position.x, point.y + position.y); } Point Control::WindowToControl(const Point & point) const { - return Point(point.x - position_cache_.lefttop_position_absolute.x, - point.y - position_cache_.lefttop_position_absolute.y); - } - - void Control::RefreshDescendantPositionCache() - { - auto point = Point::Zero(); - auto parent = this; - while ((parent = parent->GetParent())) - { - const auto p = parent->GetOffset(); - point.x += p.x; - point.y += p.y; - } - RefreshControlPositionCacheInternal(this, point); - } - - void Control::RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute) - { - const auto position = control->GetOffset(); - const Point lefttop( - parent_lefttop_absolute.x + position.x, - parent_lefttop_absolute.y + position.y - ); - control->position_cache_.lefttop_position_absolute = lefttop; - for(auto c : control->GetInternalChildren()) - { - RefreshControlPositionCacheInternal(c, lefttop); - } - } - - bool Control::IsPointInside(const Point & point) - { - const auto border_geometry = geometry_info_.border_geometry; - if (border_geometry != nullptr) - { - if (IsBordered()) - { - BOOL contains; - border_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains); - if (!contains) - border_geometry->StrokeContainsPoint(Convert(point), GetBorderProperty().GetStrokeWidth(), nullptr, D2D1::Matrix3x2F::Identity(), &contains); - return contains != 0; - } - else - { - BOOL contains; - border_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains); - return contains != 0; - } - } - return false; - } - - Control* Control::HitTest(const Point& point) - { - const auto point_inside = IsPointInside(point); - - if (IsClipContent()) - { - if (!point_inside) - return nullptr; - if (geometry_info_.content_geometry != nullptr) - { - BOOL contains; - ThrowIfFailed(geometry_info_.content_geometry->FillContainsPoint(Convert(point), D2D1::Matrix3x2F::Identity(), &contains)); - if (contains == 0) - return this; - } - } - - const auto& children = GetInternalChildren(); - - for (auto i = children.crbegin(); i != children.crend(); ++i) - { - const auto&& lefttop = (*i)->GetOffset(); - const auto&& coerced_point = Point(point.x - lefttop.x, point.y - lefttop.y); - const auto child_hit_test_result = (*i)->HitTest(coerced_point); - if (child_hit_test_result != nullptr) - return child_hit_test_result; - } - - return point_inside ? this : nullptr; - } - - void Control::SetClipContent(const bool clip) - { - if (clip_content_ == clip) - return; - - clip_content_ = clip; - InvalidateDraw(); - } - - void Control::Draw(ID2D1DeviceContext* device_context) - { - D2D1::Matrix3x2F old_transform; - device_context->GetTransform(&old_transform); - - const auto position = GetOffset(); - device_context->SetTransform(old_transform * D2D1::Matrix3x2F::Translation(position.x, position.y)); - - OnDrawDecoration(device_context); - - const auto set_layer = geometry_info_.content_geometry != nullptr && IsClipContent(); - if (set_layer) - device_context->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), geometry_info_.content_geometry.Get()), nullptr); - - OnDrawCore(device_context); - - for (auto child : GetInternalChildren()) - child->Draw(device_context); - - if (set_layer) - device_context->PopLayer(); - - device_context->SetTransform(old_transform); - } - - void Control::InvalidateDraw() - { - if (window_ != nullptr) - window_->InvalidateDraw(); + const auto position = GetPositionInWindow(); + return Point(point.x - position.x, point.y - position.y); } bool Control::RequestFocus() @@ -325,109 +95,6 @@ namespace cru::ui return window->GetFocusControl() == this; } - void Control::InvalidateLayout() - { - if (const auto window = GetWindow()) - window->WindowInvalidateLayout(); - } - - void Control::Measure(const Size& available_size, const AdditionalMeasureInfo& additional_info) - { - SetDesiredSize(OnMeasureCore(available_size, additional_info)); - } - - void Control::Layout(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - auto my_additional_info = additional_info; - my_additional_info.total_offset.x += rect.left; - my_additional_info.total_offset.y += rect.top; - position_cache_.lefttop_position_absolute.x = my_additional_info.total_offset.x; - position_cache_.lefttop_position_absolute.y = my_additional_info.total_offset.y; - - SetRect(rect); - OnLayoutCore(Rect(Point::Zero(), rect.GetSize()), my_additional_info); - } - - Size Control::GetDesiredSize() const - { - return desired_size_; - } - - void Control::SetDesiredSize(const Size& desired_size) - { - desired_size_ = desired_size; - } - - inline void Shrink(Rect& rect, const Thickness& thickness) - { - rect.left += thickness.left; - rect.top += thickness.top; - rect.width -= thickness.GetHorizontalTotal(); - rect.height -= thickness.GetVerticalTotal(); - } - - Rect Control::GetRect(const RectRange range) - { - if (GetSize() == Size::Zero()) - return Rect(); - - const auto layout_params = GetLayoutParams(); - - auto result = Rect(Point::Zero(), GetSize()); - - if (range == RectRange::Margin) - return result; - - Shrink(result, layout_params->margin); - - if (range == RectRange::FullBorder) - return result; - - if (is_bordered_) - Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); - - if (range == RectRange::HalfBorder) - return result; - - if (is_bordered_) - Shrink(result, Thickness(GetBorderProperty().GetStrokeWidth() / 2.0f)); - - if (range == RectRange::Padding) - return result; - - Shrink(result, layout_params->padding); - - return result; - } - - Point Control::TransformPoint(const Point& point, const RectRange from, const RectRange to) - { - const auto rect_from = GetRect(from); - const auto rect_to = GetRect(to); - auto p = point; - p.x += rect_from.left; - p.y += rect_from.top; - p.x -= rect_to.left; - p.y -= rect_to.top; - return p; - } - - void Control::UpdateBorder() - { - RegenerateGeometryInfo(); - InvalidateLayout(); - InvalidateDraw(); - } - - void Control::SetBordered(const bool bordered) - { - if (bordered != is_bordered_) - { - is_bordered_ = bordered; - UpdateBorder(); - } - } - void Control::SetCursor(const Cursor::Ptr& cursor) { if (cursor != cursor_) @@ -444,289 +111,41 @@ namespace cru::ui } - void Control::OnInternalParentChanged(Control* old_internal_parent, Control* new_internal_parent) - { - - } - void Control::OnAttachToWindow(Window* window) - { - window_ = window; - } - - void Control::OnDetachToWindow(Window * window) - { - window_ = nullptr; - } - - void Control::OnDrawDecoration(ID2D1DeviceContext* device_context) - { -#ifdef CRU_DEBUG_LAYOUT - if (GetWindow()->IsDebugLayout()) - { - if (padding_geometry_ != nullptr) - device_context->FillGeometry(padding_geometry_.Get(), UiManager::GetInstance()->GetPredefineResources()->debug_layout_padding_brush.Get()); - if (margin_geometry_ != nullptr) - device_context->FillGeometry(margin_geometry_.Get(), UiManager::GetInstance()->GetPredefineResources()->debug_layout_margin_brush.Get()); - device_context->DrawRectangle(Convert(GetRect(RectRange::Margin)), UiManager::GetInstance()->GetPredefineResources()->debug_layout_out_border_brush.Get()); - } -#endif - - if (is_bordered_ && geometry_info_.border_geometry != nullptr) - device_context->DrawGeometry( - geometry_info_.border_geometry.Get(), - GetBorderProperty().GetBrush().Get(), - GetBorderProperty().GetStrokeWidth(), - GetBorderProperty().GetStrokeStyle().Get() - ); - } - - void Control::OnDrawCore(ID2D1DeviceContext* device_context) - { - const auto ground_geometry = geometry_info_.padding_content_geometry; - //draw background. - if (ground_geometry != nullptr && background_brush_ != nullptr) - device_context->FillGeometry(ground_geometry.Get(), background_brush_.Get()); - const auto padding_rect = GetRect(RectRange::Padding); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(padding_rect.left, padding_rect.top), - [this](ID2D1DeviceContext* device_context) - { - events::DrawEventArgs args(this, this, device_context); - draw_background_event.Raise(args); - }); - - - const auto rect = GetRect(RectRange::Content); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top), - [this](ID2D1DeviceContext* device_context) - { - events::DrawEventArgs args(this, this, device_context); - draw_content_event.Raise(args); - }); - - - //draw foreground. - if (ground_geometry != nullptr && foreground_brush_ != nullptr) - device_context->FillGeometry(ground_geometry.Get(), foreground_brush_.Get()); - graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(padding_rect.left, padding_rect.top), - [this](ID2D1DeviceContext* device_context) - { - events::DrawEventArgs args(this, this, device_context); - draw_foreground_event.Raise(args); - }); - } - - void Control::OnRectChange(const Rect& old_rect, const Rect& new_rect) { } - void Control::RegenerateGeometryInfo() + void Control::OnDetachToWindow(Window * window) { - if (IsBordered()) - { - const auto bound_rect = GetRect(RectRange::HalfBorder); - const auto bound_rounded_rect = D2D1::RoundedRect(Convert(bound_rect), - GetBorderProperty().GetRadiusX(), - GetBorderProperty().GetRadiusY()); - - Microsoft::WRL::ComPtr geometry; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(bound_rounded_rect, &geometry) - ); - geometry_info_.border_geometry = std::move(geometry); - - const auto padding_rect = GetRect(RectRange::Padding); - const auto in_border_rounded_rect = D2D1::RoundedRect(Convert(padding_rect), - GetBorderProperty().GetRadiusX() - GetBorderProperty().GetStrokeWidth() / 2.0f, - GetBorderProperty().GetRadiusY() - GetBorderProperty().GetStrokeWidth() / 2.0f); - - Microsoft::WRL::ComPtr geometry2; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRoundedRectangleGeometry(in_border_rounded_rect, &geometry2) - ); - geometry_info_.padding_content_geometry = geometry2; - - - Microsoft::WRL::ComPtr geometry3; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(GetRect(RectRange::Content)), &geometry3) - ); - Microsoft::WRL::ComPtr geometry4; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreatePathGeometry(&geometry4) - ); - Microsoft::WRL::ComPtr sink; - geometry4->Open(&sink); - ThrowIfFailed( - geometry3->CombineWithGeometry(geometry2.Get(), D2D1_COMBINE_MODE_INTERSECT, D2D1::Matrix3x2F::Identity(), sink.Get()) - ); - sink->Close(); - geometry_info_.content_geometry = std::move(geometry4); - } - else - { - const auto bound_rect = GetRect(RectRange::Padding); - Microsoft::WRL::ComPtr geometry; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(bound_rect), &geometry) - ); - geometry_info_.border_geometry = geometry; - geometry_info_.padding_content_geometry = std::move(geometry); - - Microsoft::WRL::ComPtr geometry2; - ThrowIfFailed( - graph::GraphManager::GetInstance()->GetD2D1Factory()->CreateRectangleGeometry(Convert(GetRect(RectRange::Content)), &geometry2) - ); - geometry_info_.content_geometry = std::move(geometry2); - } - //TODO: generate debug geometry -#ifdef CRU_DEBUG_LAYOUT - margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder)); - padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content)); -#endif } void Control::OnMouseClickBegin(MouseButton button) { - } - void Control::OnMouseClickEnd(MouseButton button) - { } - inline Size ThicknessToSize(const Thickness& thickness) - { - return Size(thickness.left + thickness.right, thickness.top + thickness.bottom); - } - - Size Control::OnMeasureCore(const Size& available_size, const AdditionalMeasureInfo& additional_info) + void Control::OnMouseClickEnd(MouseButton button) { - const auto layout_params = GetLayoutParams(); - - if (!layout_params->Validate()) - throw std::runtime_error("LayoutParams is not valid. Please check it."); - - auto my_additional_info = additional_info; - - if (layout_params->width.mode == MeasureMode::Content) - my_additional_info.horizontal_stretchable = false; - else if (layout_params->width.mode == MeasureMode::Exactly) - my_additional_info.horizontal_stretchable = true; - // if stretch, then inherent parent's value - - if (layout_params->height.mode == MeasureMode::Content) - my_additional_info.vertical_stretchable = false; - else if (layout_params->height.mode == MeasureMode::Exactly) - my_additional_info.vertical_stretchable = true; - // if stretch, then inherent parent's value - - auto border_size = Size::Zero(); - if (is_bordered_) - { - const auto border_width = GetBorderProperty().GetStrokeWidth(); - border_size = Size(border_width * 2.0f, border_width * 2.0f); - } - - // the total size of padding, border and margin - const auto outer_size = ThicknessToSize(layout_params->padding) + - ThicknessToSize(layout_params->margin) + border_size; - - - auto&& get_content_measure_length = [](const LayoutSideParams& layout_length, const float available_length, const float outer_length) -> float - { - float length; - if (layout_length.mode == MeasureMode::Exactly) - length = layout_length.length; - else if (available_length > outer_length) - length = available_length - outer_length; - else - length = 0; - return Coerce(length, layout_length.min, layout_length.max); - }; - - // if padding, margin and border exceeded, then content size is 0. - const auto content_measure_size = Size( - get_content_measure_length(layout_params->width, available_size.width, outer_size.width), - get_content_measure_length(layout_params->height, available_size.height, outer_size.height) - ); - - const auto content_actual_size = OnMeasureContent(content_measure_size, my_additional_info); - - - - auto&& calculate_final_length = [](const bool stretch, const std::optional min_length, const float measure_length, const float actual_length) -> float - { - // only use measure length when stretch and actual length is smaller than measure length, that is "stretch" - if (stretch && actual_length < measure_length) - return measure_length; - return Coerce(actual_length, min_length, std::nullopt); - }; - - const auto final_size = Size( - calculate_final_length(my_additional_info.horizontal_stretchable, layout_params->width.min, content_measure_size.width, content_actual_size.width), - calculate_final_length(my_additional_info.vertical_stretchable, layout_params->height.min, content_measure_size.height, content_actual_size.height) - ) + outer_size; - - return final_size; } - void Control::OnLayoutCore(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - const auto layout_params = GetLayoutParams(); - - auto border_width = 0.0f; - if (is_bordered_) - { - border_width = GetBorderProperty().GetStrokeWidth(); - } - - const Rect content_rect( - rect.left + layout_params->padding.left + layout_params->margin.right + border_width, - rect.top + layout_params->padding.top + layout_params->margin.top + border_width, - rect.width - layout_params->padding.GetHorizontalTotal() - layout_params->margin.GetHorizontalTotal() - border_width * 2.0f, - rect.height - layout_params->padding.GetVerticalTotal() - layout_params->margin.GetVerticalTotal() - border_width * 2.0f - ); - - if (content_rect.width < 0.0) - throw std::runtime_error(Format("Width to layout must sufficient. But in {}, width for content is {}.", ToUtf8String(GetControlType()), content_rect.width)); - if (content_rect.height < 0.0) - throw std::runtime_error(Format("Height to layout must sufficient. But in {}, height for content is {}.", ToUtf8String(GetControlType()), content_rect.height)); - - OnLayoutContent(content_rect, additional_info); - } const std::vector NoChildControl::empty_control_vector{}; - std::list GetAncestorList(Control* control) - { - std::list l; - while (control != nullptr) - { - l.push_front(control); - control = control->GetInternalParent(); - } - return l; - } - - void NoChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - } - - SingleChildControl::SingleChildControl() : child_vector_{nullptr}, child_(child_vector_[0]) + ContentControl::ContentControl() : child_vector_{nullptr}, child_(child_vector_[0]) { } - SingleChildControl::~SingleChildControl() + ContentControl::~ContentControl() { delete child_; } - void SingleChildControl::SetChild(Control* child) + void ContentControl::SetChild(Control* child) { if (child == child_) return; @@ -736,66 +155,25 @@ namespace cru::ui child_ = child; if (old_child) { - old_child->SetInternalParent(nullptr); + old_child->SetParent(nullptr); old_child->SetDescendantWindow(nullptr); } if (child) { - child->SetInternalParent(this); + child->SetParent(this); child->SetDescendantWindow(window); } OnChildChanged(old_child, child); } - void SingleChildControl::OnChildChanged(Control* old_child, Control* new_child) + void ContentControl::OnChildChanged(Control* old_child, Control* new_child) { } - Size SingleChildControl::OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) + void ControlAddChildCheck(Control* control) { - auto child_size = Size::Zero(); - if (child_) - { - child_->Measure(available_size, additional_info); - child_size = child_->GetDesiredSize(); - } - - return child_size; - } - - void SingleChildControl::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) - { - if (child_) - { - const auto layout_params = child_->GetLayoutParams(); - const auto size = child_->GetDesiredSize(); - - auto&& calculate_anchor = [](const float anchor, const Alignment alignment, const float layout_length, const float control_length) -> float - { - switch (alignment) - { - case Alignment::Center: - return anchor + (layout_length - control_length) / 2; - case Alignment::Start: - return anchor; - case Alignment::End: - return anchor + layout_length - control_length; - default: - UnreachableCode(); - } - }; - - child_->Layout(Rect(Point( - calculate_anchor(rect.left, layout_params->width.alignment, rect.width, size.width), - calculate_anchor(rect.top, layout_params->height.alignment, rect.height, size.height) - ), size), additional_info); - } - } - - void AddChildCheck(Control* control) - { - if (control->GetInternalParent() != nullptr) + if (control->GetParent() != nullptr) throw std::invalid_argument("The control already has a parent."); if (dynamic_cast(control)) @@ -810,11 +188,11 @@ namespace cru::ui void MultiChildControl::AddChild(Control* control) { - AddChildCheck(control); + ControlAddChildCheck(control); children_.push_back(control); - control->SetInternalParent(this); + control->SetParent(this); control->SetDescendantWindow(GetWindow()); OnAddChild(control); @@ -822,14 +200,14 @@ namespace cru::ui void MultiChildControl::AddChild(Control* control, const int position) { - AddChildCheck(control); + ControlAddChildCheck(control); if (position < 0 || static_cast(position) > this->children_.size()) throw std::invalid_argument("The position is out of range."); children_.insert(this->children_.cbegin() + position, control); - control->SetInternalParent(this); + control->SetParent(this); control->SetDescendantWindow(GetWindow()); OnAddChild(control); @@ -843,7 +221,7 @@ namespace cru::ui children_.erase(i); - child->SetInternalParent(nullptr); + child->SetParent(nullptr); child->SetDescendantWindow(nullptr); OnRemoveChild(child); @@ -859,7 +237,7 @@ namespace cru::ui children_.erase(i); - child->SetInternalParent(nullptr); + child->SetParent(nullptr); child->SetDescendantWindow(nullptr); OnRemoveChild(child); @@ -875,6 +253,17 @@ namespace cru::ui } + std::list GetAncestorList(Control* control) + { + std::list l; + while (control != nullptr) + { + l.push_front(control); + control = control->GetParent(); + } + return l; + } + Control* FindLowestCommonAncestor(Control * left, Control * right) { if (left == nullptr || right == nullptr) diff --git a/src/ui/control.hpp b/src/ui/control.hpp index 6abcc365..8e69fb07 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -12,49 +12,21 @@ #include "ui_base.hpp" #include "layout_base.hpp" #include "events/ui_event.hpp" -#include "border_property.hpp" #include "cursor.hpp" #include "any_map.hpp" +#include "input_util.hpp" namespace cru::ui { class Window; - struct AdditionalMeasureInfo - { - bool horizontal_stretchable = true; - bool vertical_stretchable = true; - }; - - struct AdditionalLayoutInfo - { - Point total_offset = Point::Zero(); - }; - - //the position cache - struct ControlPositionCache - { - //The lefttop relative to the ancestor. - Point lefttop_position_absolute = Point::Zero(); - }; - - class Control : public Object { friend class Window; protected: - struct GeometryInfo - { - Microsoft::WRL::ComPtr border_geometry = nullptr; - Microsoft::WRL::ComPtr padding_content_geometry = nullptr; - Microsoft::WRL::ComPtr content_geometry = nullptr; - }; - - - protected: - Control(); + Control() = default; public: Control(const Control& other) = delete; Control(Control&& other) = delete; @@ -63,53 +35,35 @@ namespace cru::ui ~Control() override = default; public: - - //*************** region: tree *************** virtual StringView GetControlType() const = 0; - virtual const std::vector& GetInternalChildren() const = 0; - - Control* GetParent() const - { - return parent_ == nullptr ? internal_parent_ : parent_; - } - - Control* GetInternalParent() const - { - return internal_parent_; - } + //*************** region: tree *************** + public: //Get the window if attached, otherwise, return nullptr. Window* GetWindow() const { return window_; } - void SetParent(Control* parent); + Control* GetParent() const + { + return parent_; + } - void SetInternalParent(Control* internal_parent); + void SetParent(Control* parent); void SetDescendantWindow(Window* window); - //Traverse the tree rooted the control including itself. void TraverseDescendants(const std::function& predicate); - //*************** region: position and size *************** - - //Get the lefttop relative to its parent. - virtual Point GetOffset(); - - //Get the actual size. - virtual Size GetSize(); - - // If offset changes, call RefreshDescendantPositionCache. - virtual void SetRect(const Rect& rect); + private: + static void TraverseDescendantsInternal(Control* control, const std::function& predicate); - //Get lefttop relative to ancestor. This is only valid when - //attached to window. Notice that the value is cached. - //You can invalidate and recalculate it by calling "InvalidatePositionCache". - Point GetPositionAbsolute() const; + //*************** region: position *************** + public: + virtual Point GetPositionInWindow() const = 0; //Local point to absolute point. Point ControlToWindow(const Point& point) const; @@ -117,122 +71,19 @@ namespace cru::ui //Absolute point to local point. Point WindowToControl(const Point& point) const; - void RefreshDescendantPositionCache(); - - private: - static void RefreshControlPositionCacheInternal(Control* control, const Point& parent_lefttop_absolute); - - public: - - // Default implement in Control is test point in border geometry's - // fill and stroke with width of border. - virtual bool IsPointInside(const Point& point); - - // Get the top control among all descendants (including self) in local coordinate. - virtual Control* HitTest(const Point& point); - - //*************** region: graphic *************** - - bool IsClipContent() const - { - return clip_content_; - } - - void SetClipContent(bool clip); - - //Draw this control and its child controls. - void Draw(ID2D1DeviceContext* device_context); - - virtual void InvalidateDraw(); - - Microsoft::WRL::ComPtr GetForegroundBrush() const - { - return foreground_brush_; - } - - void SetForegroundBrush(Microsoft::WRL::ComPtr foreground_brush) - { - foreground_brush_ = std::move(foreground_brush); - InvalidateDraw(); - } - - Microsoft::WRL::ComPtr GetBackgroundBrush() const - { - return background_brush_; - } - - void SetBackgroundBrush(Microsoft::WRL::ComPtr background_brush) - { - background_brush_ = std::move(background_brush); - InvalidateDraw(); - } //*************** region: focus *************** - + public: bool RequestFocus(); bool HasFocus(); - bool IsFocusOnPressed() const - { - return is_focus_on_pressed_; - } - - void SetFocusOnPressed(const bool value) - { - is_focus_on_pressed_ = value; - } - - //*************** region: layout *************** - - void InvalidateLayout(); - - void Measure(const Size& available_size, const AdditionalMeasureInfo& additional_info); - - void Layout(const Rect& rect, const AdditionalLayoutInfo& additional_info); - - Size GetDesiredSize() const; - - void SetDesiredSize(const Size& desired_size); - - BasicLayoutParams* GetLayoutParams() - { - return &layout_params_; - } - - Rect GetRect(RectRange range); - - Point TransformPoint(const Point& point, RectRange from = RectRange::Margin, RectRange to = RectRange::Content); - - //*************** region: border *************** - - BorderProperty& GetBorderProperty() - { - return border_property_; - } - - void UpdateBorder(); - - bool IsBordered() const - { - return is_bordered_; - } - - void SetBordered(bool bordered); - - - //*************** region: additional properties *************** - AnyMap* GetAdditionalPropertyMap() - { - return &additional_property_map_; - } - //*************** region: cursor *************** // If cursor is set to null, then it uses parent's cursor. // Window's cursor can't be null. - + public: Cursor::Ptr GetCursor() const { return cursor_; @@ -241,7 +92,16 @@ namespace cru::ui void SetCursor(const Cursor::Ptr& cursor); + //*************** region: additional properties *************** + public: + AnyMap* GetAdditionalPropertyMap() + { + return &additional_property_map_; + } + + //*************** region: events *************** + public: //Raised when mouse enter the control. events::RoutedEvent mouse_enter_event; //Raised when mouse is leave the control. @@ -264,95 +124,29 @@ namespace cru::ui events::RoutedEvent get_focus_event; events::RoutedEvent lose_focus_event; - Event draw_content_event; - Event draw_background_event; - Event draw_foreground_event; - - //*************** region: tree event *************** + //*************** region: tree *************** protected: - virtual void OnParentChanged(Control* old_parent, Control* new_parent); - - virtual void OnInternalParentChanged(Control* old_internal_parent, Control* new_internal_parent); + virtual const std::vector& GetInternalChildren() const = 0; - //Invoked when the control is attached to a window. Overrides should invoke base. + virtual void OnParentChanged(Control* old_parent, Control* new_parent); virtual void OnAttachToWindow(Window* window); - //Invoked when the control is detached to a window. Overrides should invoke base. virtual void OnDetachToWindow(Window* window); - //*************** region: graphic event *************** - private: - void OnDrawDecoration(ID2D1DeviceContext* device_context); - void OnDrawCore(ID2D1DeviceContext* device_context); - - - //*************** region: position and size event *************** - protected: - virtual void OnRectChange(const Rect& old_rect, const Rect& new_rect); - - void RegenerateGeometryInfo(); - - const GeometryInfo& GetGeometryInfo() const - { - return geometry_info_; - } - - - //*************** region: mouse event *************** + //*************** region: additional mouse event *************** protected: virtual void OnMouseClickBegin(MouseButton button); virtual void OnMouseClickEnd(MouseButton button); - //*************** region: layout *************** - private: - Size OnMeasureCore(const Size& available_size, const AdditionalMeasureInfo& additional_info); - void OnLayoutCore(const Rect& rect, const AdditionalLayoutInfo& additional_info); - - protected: - virtual Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) = 0; - virtual void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) = 0; - private: Window * window_ = nullptr; - Control* parent_ = nullptr; // when parent and internal parent are the same, parent_ is nullptr. - Control * internal_parent_ = nullptr; - - Rect rect_{}; - - ControlPositionCache position_cache_{}; - - std::unordered_map is_mouse_click_valid_map_ - { - { MouseButton::Left, true }, - { MouseButton::Middle, true }, - { MouseButton::Right, true } - }; // used for clicking determination - - BasicLayoutParams layout_params_{}; - Size desired_size_ = Size::Zero(); - - bool is_bordered_ = false; - BorderProperty border_property_; - - GeometryInfo geometry_info_{}; - - bool clip_content_ = false; + Control* parent_ = nullptr; - Microsoft::WRL::ComPtr foreground_brush_ = nullptr; - Microsoft::WRL::ComPtr background_brush_ = nullptr; + Cursor::Ptr cursor_{}; AnyMap additional_property_map_{}; - - bool is_focus_on_pressed_ = true; - -#ifdef CRU_DEBUG_LAYOUT - Microsoft::WRL::ComPtr margin_geometry_; - Microsoft::WRL::ComPtr padding_geometry_; -#endif - - Cursor::Ptr cursor_{}; }; @@ -372,31 +166,24 @@ namespace cru::ui NoChildControl& operator=(NoChildControl&& other) = delete; ~NoChildControl() override = default; + protected: const std::vector& GetInternalChildren() const override final { return empty_control_vector; } - - protected: - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override; }; - class SingleChildControl : public Control + class ContentControl : public Control { protected: - SingleChildControl(); + ContentControl(); public: - SingleChildControl(const SingleChildControl& other) = delete; - SingleChildControl(SingleChildControl&& other) = delete; - SingleChildControl& operator=(const SingleChildControl& other) = delete; - SingleChildControl& operator=(SingleChildControl&& other) = delete; - ~SingleChildControl() override; - - const std::vector& GetInternalChildren() const override final - { - return child_vector_; - } + ContentControl(const ContentControl& other) = delete; + ContentControl(ContentControl&& other) = delete; + ContentControl& operator=(const ContentControl& other) = delete; + ContentControl& operator=(ContentControl&& other) = delete; + ~ContentControl() override; Control* GetChild() const { @@ -406,12 +193,14 @@ namespace cru::ui void SetChild(Control* child); protected: + const std::vector& GetInternalChildren() const override final + { + return child_vector_; + } + // Override should call base. virtual void OnChildChanged(Control* old_child, Control* new_child); - Size OnMeasureContent(const Size& available_size, const AdditionalMeasureInfo& additional_info) override; - void OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) override; - private: std::vector child_vector_; Control*& child_; @@ -452,9 +241,7 @@ namespace cru::ui void RemoveChild(int position); protected: - //Invoked when a child is added. Overrides should invoke base. virtual void OnAddChild(Control* child); - //Invoked when a child is removed. Overrides should invoke base. virtual void OnRemoveChild(Control* child); private: diff --git a/src/ui/controls/button.hpp b/src/ui/controls/button.hpp index 82694fe8..6436f7c0 100644 --- a/src/ui/controls/button.hpp +++ b/src/ui/controls/button.hpp @@ -9,7 +9,7 @@ namespace cru::ui::controls { - class Button : public SingleChildControl + class Button : public ContentControl { public: static constexpr auto control_type = L"Button"; diff --git a/src/ui/controls/list_item.hpp b/src/ui/controls/list_item.hpp index a50b2496..bf8f8d8e 100644 --- a/src/ui/controls/list_item.hpp +++ b/src/ui/controls/list_item.hpp @@ -10,7 +10,7 @@ namespace cru::ui::controls { - class ListItem : public SingleChildControl + class ListItem : public ContentControl { public: static constexpr auto control_type = L"ListItem"; diff --git a/src/ui/controls/scroll_control.hpp b/src/ui/controls/scroll_control.hpp index 7138add6..84ebca30 100644 --- a/src/ui/controls/scroll_control.hpp +++ b/src/ui/controls/scroll_control.hpp @@ -17,7 +17,7 @@ namespace cru::ui::controls // Done: API // Done: ScrollBar // Done: MouseEvent - class ScrollControl : public SingleChildControl + class ScrollControl : public ContentControl { private: struct ScrollBarInfo diff --git a/src/ui/render/linear_layout_render_object.cpp b/src/ui/render/linear_layout_render_object.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/ui/render/linear_layout_render_object.hpp b/src/ui/render/linear_layout_render_object.hpp new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/src/ui/render/linear_layout_render_object.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 0828fc9c..c2aaeb62 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -3,96 +3,88 @@ namespace cru::ui::render { - void RenderObject::SetRenderHost(IRenderHost* new_render_host) - { - if (new_render_host == render_host_) - return; - - const auto old = render_host_; - render_host_ = new_render_host; - OnRenderHostChanged(old, new_render_host); - } - - void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, IRenderHost* new_render_host) - { - - } - - void RenderObject::InvalidateRenderHost() - { - if (render_host_ != nullptr) - render_host_->InvalidateRender(); - } - - void StrokeRenderObject::SetStrokeWidth(const float new_stroke_width) - { - if (stroke_width_ == new_stroke_width) - return; - - stroke_width_ = new_stroke_width; - InvalidateRenderHost(); - } - - void StrokeRenderObject::SetBrush(Microsoft::WRL::ComPtr new_brush) - { - if (brush_ == new_brush) - return; - - brush_ = std::move(new_brush); - InvalidateRenderHost(); - } - - void StrokeRenderObject::SetStrokeStyle(Microsoft::WRL::ComPtr new_stroke_style) - { - if (stroke_style_ == new_stroke_style) - return; - - stroke_style_ = std::move(new_stroke_style); - InvalidateRenderHost(); - } - - void FillRenderObject::SetBrush(Microsoft::WRL::ComPtr new_brush) - { - if (brush_ == new_brush) - return; - - brush_ = std::move(new_brush); - InvalidateRenderHost(); - } - - namespace details - { - template class ShapeRenderObject; - template class ShapeRenderObject; - template class ShapeRenderObject; - } - - namespace details - { - template ShapeStrokeRenderObject; - template ShapeStrokeRenderObject; - template ShapeStrokeRenderObject; - } - - namespace details - { - template ShapeFillRenderObject; - template ShapeFillRenderObject; - template ShapeFillRenderObject; - } - - void CustomDrawHandlerRenderObject::SetDrawHandler(DrawHandler new_draw_handler) - { - if (draw_handler_ == nullptr && new_draw_handler == nullptr) - return; - - draw_handler_ = std::move(new_draw_handler); - InvalidateRenderHost(); - } - - void CustomDrawHandlerRenderObject::Draw(ID2D1RenderTarget* render_target) - { - if (draw_handler_ != nullptr) - draw_handler_(render_target); - } +void RenderObject::SetRenderHost(IRenderHost* new_render_host) +{ + if (new_render_host == render_host_) return; + + const auto old = render_host_; + render_host_ = new_render_host; + OnRenderHostChanged(old, new_render_host); +} + +void RenderObject::AddChild(RenderObject* render_object, const int position) +{ + if (render_object->GetParent() != nullptr) + throw std::invalid_argument("Render object already has a parent."); + + if (position < 0) + throw std::invalid_argument("Position index is less than 0."); + + if (static_cast::size_type>(position) > + children_.size()) + throw std::invalid_argument("Position index is out of bound."); + + children_.insert(children_.cbegin() + position, render_object); + render_object->SetParent(this); + OnAddChild(render_object, position); +} + +void RenderObject::RemoveChild(const int position) +{ + if (position < 0) + throw std::invalid_argument("Position index is less than 0."); + + if (static_cast::size_type>(position) >= + children_.size()) + throw std::invalid_argument("Position index is out of bound."); + + const auto i = children_.cbegin() + position; + const auto removed_child = *i; + children_.erase(i); + removed_child->SetParent(nullptr); + OnRemoveChild(removed_child, position); +} + + +void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, + IRenderHost* new_render_host) +{ +} + +void RenderObject::InvalidateRenderHostPaint() const +{ + if (render_host_ != nullptr) render_host_->InvalidatePaint(); +} + +void RenderObject::InvalidateRenderHostLayout() const +{ + if (render_host_ != nullptr) render_host_->InvalidateLayout(); +} + +void RenderObject::OnParentChanged(RenderObject* old_parent, + RenderObject* new_parent) +{ +} + + +void RenderObject::OnAddChild(RenderObject* new_child, int position) +{ +} + +void RenderObject::OnRemoveChild(RenderObject* removed_child, int position) +{ +} + +void RenderObject::SetParent(RenderObject* new_parent) +{ + const auto old_parent = parent_; + parent_ = new_parent; + OnParentChanged(old_parent, new_parent); +} + + +void LinearLayoutRenderObject::Measure(const MeasureConstraint& constraint) +{ + } +} // namespace cru::ui::render diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 31745be5..00f761d1 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -2,256 +2,108 @@ #include "pre.hpp" +#include +#include #include "system_headers.hpp" -#include -#include #include "base.hpp" #include "ui/ui_base.hpp" -#include "ui/d2d_util.hpp" namespace cru::ui::render { - /* About Render Object - * - * Render object is a concrete subclass of RenderObject class. - * It represents a painting action on a d2d render target. By - * overriding "Draw" virtual method, it can customize its painting - * action. - */ - - - struct IRenderHost : Interface - { - virtual void InvalidateRender() = 0; - }; - - - class RenderObject : public Object - { - protected: - RenderObject() = default; - public: - RenderObject(const RenderObject& other) = delete; - RenderObject(RenderObject&& other) = delete; - RenderObject& operator=(const RenderObject& other) = delete; - RenderObject& operator=(RenderObject&& other) = delete; - ~RenderObject() override = default; - - virtual void Draw(ID2D1RenderTarget* render_target) = 0; - - IRenderHost* GetRenderHost() const - { - return render_host_; - } - - void SetRenderHost(IRenderHost* new_render_host); - - protected: - virtual void OnRenderHostChanged(IRenderHost* old_render_host, IRenderHost* new_render_host); - - void InvalidateRenderHost(); - - private: - IRenderHost* render_host_ = nullptr; - }; - - - class StrokeRenderObject : public virtual RenderObject - { - protected: - StrokeRenderObject() = default; - public: - StrokeRenderObject(const StrokeRenderObject& other) = delete; - StrokeRenderObject(StrokeRenderObject&& other) = delete; - StrokeRenderObject& operator=(const StrokeRenderObject& other) = delete; - StrokeRenderObject& operator=(StrokeRenderObject&& other) = delete; - ~StrokeRenderObject() override = default; - - float GetStrokeWidth() const - { - return stroke_width_; - } - - void SetStrokeWidth(float new_stroke_width); - - Microsoft::WRL::ComPtr GetBrush() const - { - return brush_; - } - - void SetBrush(Microsoft::WRL::ComPtr new_brush); - - Microsoft::WRL::ComPtr GetStrokeStyle() const - { - return stroke_style_; - } - - void SetStrokeStyle(Microsoft::WRL::ComPtr new_stroke_style); - - private: - float stroke_width_ = 1.0f; - Microsoft::WRL::ComPtr brush_ = nullptr; - Microsoft::WRL::ComPtr stroke_style_ = nullptr; - }; - - - class FillRenderObject : public virtual RenderObject - { - protected: - FillRenderObject() = default; - public: - FillRenderObject(const FillRenderObject& other) = delete; - FillRenderObject(FillRenderObject&& other) = delete; - FillRenderObject& operator=(const FillRenderObject& other) = delete; - FillRenderObject& operator=(FillRenderObject&& other) = delete; - ~FillRenderObject() override = default; - - Microsoft::WRL::ComPtr GetBrush() const - { - return brush_; - } - - void SetBrush(Microsoft::WRL::ComPtr new_brush); - - private: - Microsoft::WRL::ComPtr brush_ = nullptr; - }; - - - namespace details - { - template - class ShapeRenderObject : public virtual RenderObject - { - public: - using ShapeType = TShapeType; - protected: - ShapeRenderObject() = default; - public: - ShapeRenderObject(const ShapeRenderObject& other) = delete; - ShapeRenderObject& operator=(const ShapeRenderObject& other) = delete; - ShapeRenderObject(ShapeRenderObject&& other) = delete; - ShapeRenderObject& operator=(ShapeRenderObject&& other) = delete; - ~ShapeRenderObject() override = default; - - ShapeType GetShape() const - { - return shape_; - } - - void SetShape(const ShapeType& new_shape) - { - if (new_shape == shape_) - return; - - shape_ = new_shape; - InvalidateRenderHost(); - } - - private: - ShapeType shape_; - }; +struct MeasureConstraint +{ + std::optional min_width; + std::optional min_height; + std::optional max_width; + std::optional max_height; +}; - extern template class ShapeRenderObject; - extern template class ShapeRenderObject; - extern template class ShapeRenderObject; - } +struct LayoutConstraint +{ + float preferred_width; + float preferred_height; +}; - using RectangleRenderObject = details::ShapeRenderObject; - using RoundedRectangleRenderObject = details::ShapeRenderObject; - using EllipseRenderObject = details::ShapeRenderObject; +struct IRenderHost : Interface +{ + virtual void InvalidatePaint() = 0; + virtual void InvalidateLayout() = 0; +}; - namespace details - { - template - class ShapeStrokeRenderObject : public ShapeRenderObject, public StrokeRenderObject - { - public: - ShapeStrokeRenderObject() = default; - ShapeStrokeRenderObject(const ShapeStrokeRenderObject& other) = delete; - ShapeStrokeRenderObject& operator=(const ShapeStrokeRenderObject& other) = delete; - ShapeStrokeRenderObject(ShapeStrokeRenderObject&& other) = delete; - ShapeStrokeRenderObject& operator=(ShapeStrokeRenderObject&& other) = delete; - ~ShapeStrokeRenderObject() = default; +// features: +// 1. tree +// 2. layout +// 3. paint +// 3. hit test +class RenderObject : public Object +{ +protected: + RenderObject() = default; - protected: - void Draw(ID2D1RenderTarget* render_target) override - { - const auto brush = GetBrush(); - if (brush != nullptr) - (render_target->*draw_function)(Convert(GetShape()), brush.Get(), GetStrokeWidth(), GetStrokeStyle().Get()); - } - }; +public: + RenderObject(const RenderObject& other) = delete; + RenderObject(RenderObject&& other) = delete; + RenderObject& operator=(const RenderObject& other) = delete; + RenderObject& operator=(RenderObject&& other) = delete; + ~RenderObject() override = default; - extern template ShapeStrokeRenderObject; - extern template ShapeStrokeRenderObject; - extern template ShapeStrokeRenderObject; - } + IRenderHost* GetRenderHost() const { return render_host_; } + void SetRenderHost(IRenderHost* new_render_host); - using RectangleStrokeRenderObject = details::ShapeStrokeRenderObject; - using RoundedRectangleStrokeRenderObject = details::ShapeStrokeRenderObject; - using EllipseStrokeRenderObject = details::ShapeStrokeRenderObject; + RenderObject* GetParent() const { return parent_; } + const std::vector& GetChildren() const { return children_; } + void AddChild(RenderObject* render_object, int position); + void RemoveChild(int position); - namespace details - { - template - class ShapeFillRenderObject : public ShapeRenderObject, public StrokeRenderObject - { - public: - ShapeFillRenderObject() = default; - ShapeFillRenderObject(const ShapeFillRenderObject& other) = delete; - ShapeFillRenderObject& operator=(const ShapeFillRenderObject& other) = delete; - ShapeFillRenderObject(ShapeFillRenderObject&& other) = delete; - ShapeFillRenderObject& operator=(ShapeFillRenderObject&& other) = delete; - ~ShapeFillRenderObject() = default; + virtual void Measure(const MeasureConstraint& constraint) = 0; + virtual void Layout(const LayoutConstraint& constraint) = 0; - protected: - void Draw(ID2D1RenderTarget* render_target) override - { - const auto brush = GetBrush(); - if (brush != nullptr) - (render_target->*fill_function)(Convert(GetShape()), brush.Get()); - } - }; + virtual void Draw(ID2D1RenderTarget* render_target) = 0; - extern template ShapeFillRenderObject; - extern template ShapeFillRenderObject; - extern template ShapeFillRenderObject; - } + virtual void HitTest(const Point& point) = 0; - using RectangleFillRenderObject = details::ShapeFillRenderObject; - using RoundedRectangleFillRenderObject = details::ShapeFillRenderObject; - using EllipseFillRenderObject = details::ShapeFillRenderObject; +protected: + virtual void OnRenderHostChanged(IRenderHost* old_render_host, + IRenderHost* new_render_host); + void InvalidateRenderHostPaint() const; + void InvalidateRenderHostLayout() const; - class CustomDrawHandlerRenderObject : public RenderObject - { - public: - using DrawHandler = std::function; + virtual void OnParentChanged(RenderObject* old_parent, + RenderObject* new_parent); - CustomDrawHandlerRenderObject() = default; - CustomDrawHandlerRenderObject(const CustomDrawHandlerRenderObject& other) = delete; - CustomDrawHandlerRenderObject& operator=(const CustomDrawHandlerRenderObject& other) = delete; - CustomDrawHandlerRenderObject(CustomDrawHandlerRenderObject&& other) = delete; - CustomDrawHandlerRenderObject& operator=(CustomDrawHandlerRenderObject&& other) = delete; - ~CustomDrawHandlerRenderObject() override = default; + virtual void OnAddChild(RenderObject* new_child, int position); + virtual void OnRemoveChild(RenderObject* removed_child, int position); - DrawHandler GetDrawHandler() const - { - return draw_handler_; - } +private: + void SetParent(RenderObject* new_parent); - void SetDrawHandler(DrawHandler new_draw_handler); +private: + IRenderHost* render_host_ = nullptr; + RenderObject* parent_ = nullptr; + std::vector children_; +}; - protected: - void Draw(ID2D1RenderTarget* render_target) override; - private: - DrawHandler draw_handler_{}; - }; -} +class LinearLayoutRenderObject : public RenderObject +{ +public: + LinearLayoutRenderObject() = default; + LinearLayoutRenderObject(const LinearLayoutRenderObject& other) = delete; + LinearLayoutRenderObject& operator=(const LinearLayoutRenderObject& other) = + delete; + LinearLayoutRenderObject(LinearLayoutRenderObject&& other) = delete; + LinearLayoutRenderObject& operator=(LinearLayoutRenderObject&& other) = + delete; + ~LinearLayoutRenderObject() = default; + + void Measure(const MeasureConstraint& constraint) override; + +private: +}; +} // namespace cru::ui::render diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp index c26bfe0e..17cb9acb 100644 --- a/src/ui/ui_base.hpp +++ b/src/ui/ui_base.hpp @@ -8,7 +8,7 @@ namespace cru::ui { - struct Point + struct Point final { constexpr static Point Zero() { @@ -33,7 +33,7 @@ namespace cru::ui } - struct Size + struct Size final { constexpr static Size Zero() { @@ -68,7 +68,7 @@ namespace cru::ui } - struct Thickness + struct Thickness final { constexpr static Thickness Zero() { @@ -122,7 +122,7 @@ namespace cru::ui float bottom; }; - constexpr bool operator == (const Thickness& left, const Thickness& right) + constexpr bool operator==(const Thickness& left, const Thickness& right) { return left.left == right.left && left.top == right.top && @@ -130,13 +130,13 @@ namespace cru::ui left.bottom == right.bottom; } - constexpr bool operator != (const Thickness& left, const Thickness& right) + constexpr bool operator!=(const Thickness& left, const Thickness& right) { return !(left == right); } - struct Rect + struct Rect final { constexpr Rect() = default; constexpr Rect(const float left, const float top, const float width, const float height) @@ -228,7 +228,7 @@ namespace cru::ui } - struct RoundedRect + struct RoundedRect final { constexpr RoundedRect() = default; constexpr RoundedRect(const Rect& rect, const float radius_x, const float radius_y) @@ -239,17 +239,18 @@ namespace cru::ui float radius_y = 0.0f; }; - constexpr bool operator == (const RoundedRect& left, const RoundedRect& right) + constexpr bool operator==(const RoundedRect& left, const RoundedRect& right) { return left.rect == right.rect && left.radius_x == right.radius_x && left.radius_y == right.radius_y; } - constexpr bool operator != (const RoundedRect& left, const RoundedRect& right) + constexpr bool operator!=(const RoundedRect& left, const RoundedRect& right) { return !(left == right); } - struct Ellipse + + struct Ellipse final { constexpr Ellipse() = default; constexpr Ellipse(const Point& center, const float radius_x, const float radius_y) @@ -270,26 +271,18 @@ namespace cru::ui float radius_y = 0.0f; }; - constexpr bool operator == (const Ellipse& left, const Ellipse& right) + constexpr bool operator==(const Ellipse& left, const Ellipse& right) { return left.center == right.center && left.radius_x == right.radius_x && left.radius_y == right.radius_y; } - constexpr bool operator != (const Ellipse& left, const Ellipse& right) + constexpr bool operator!=(const Ellipse& left, const Ellipse& right) { return !(left == right); } - enum class MouseButton - { - Left, - Right, - Middle - }; - - - struct TextRange + struct TextRange final { constexpr static std::optional FromTwoSides(unsigned first, unsigned second) { @@ -317,8 +310,4 @@ namespace cru::ui unsigned position = 0; unsigned count = 0; }; - - bool IsKeyDown(int virtual_code); - bool IsKeyToggled(int virtual_code); - bool IsAnyMouseButtonDown(); } diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 86fa4436..51b3f628 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -7,29 +7,6 @@ namespace cru::ui { - WindowClass::WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance) - : name_(name) - { - WNDCLASSEX window_class; - window_class.cbSize = sizeof(WNDCLASSEX); - - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.lpfnWndProc = window_proc; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = h_instance; - window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); - window_class.hCursor = LoadCursor(NULL, IDC_ARROW); - window_class.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); - window_class.lpszMenuName = NULL; - window_class.lpszClassName = name.c_str(); - window_class.hIconSm = NULL; - - atom_ = RegisterClassEx(&window_class); - if (atom_ == 0) - throw std::runtime_error("Failed to create window class."); - } - LRESULT __stdcall GeneralWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { auto window = WindowManager::GetInstance()->FromHandle(hWnd); diff --git a/src/ui/window.hpp b/src/ui/window.hpp index e96d4d92..d3374684 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -17,32 +17,6 @@ namespace cru::graph namespace cru::ui { - class WindowClass : public Object - { - public: - WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance); - WindowClass(const WindowClass& other) = delete; - WindowClass(WindowClass&& other) = delete; - WindowClass& operator=(const WindowClass& other) = delete; - WindowClass& operator=(WindowClass&& other) = delete; - ~WindowClass() override = default; - - - const wchar_t* GetName() const - { - return name_.c_str(); - } - - ATOM GetAtom() const - { - return atom_; - } - - private: - String name_; - ATOM atom_; - }; - class WindowManager : public Object { public: @@ -85,7 +59,7 @@ namespace cru::ui - class Window final : public SingleChildControl + class Window final : public ContentControl { friend class WindowManager; public: diff --git a/src/ui/window_class.cpp b/src/ui/window_class.cpp new file mode 100644 index 00000000..456d9492 --- /dev/null +++ b/src/ui/window_class.cpp @@ -0,0 +1,25 @@ +#include "window_class.hpp" + +namespace cru::ui { +WindowClass::WindowClass(const String& name, WNDPROC window_proc, + HINSTANCE h_instance) + : name_(name) { + WNDCLASSEX window_class; + window_class.cbSize = sizeof(WNDCLASSEX); + + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.lpfnWndProc = window_proc; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = h_instance; + window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); + window_class.hCursor = LoadCursor(NULL, IDC_ARROW); + window_class.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); + window_class.lpszMenuName = NULL; + window_class.lpszClassName = name.c_str(); + window_class.hIconSm = NULL; + + atom_ = ::RegisterClassExW(&window_class); + if (atom_ == 0) throw std::runtime_error("Failed to create window class."); +} +} // namespace cru::ui diff --git a/src/ui/window_class.hpp b/src/ui/window_class.hpp new file mode 100644 index 00000000..66babd94 --- /dev/null +++ b/src/ui/window_class.hpp @@ -0,0 +1,26 @@ +#pragma once +#include "pre.hpp" + +#include "system_headers.hpp" + +#include "base.hpp" + +namespace cru::ui { +class WindowClass : public Object { + public: + WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance); + WindowClass(const WindowClass& other) = delete; + WindowClass(WindowClass&& other) = delete; + WindowClass& operator=(const WindowClass& other) = delete; + WindowClass& operator=(WindowClass&& other) = delete; + ~WindowClass() override = default; + + const wchar_t* GetName() const { return name_.c_str(); } + + ATOM GetAtom() const { return atom_; } + + private: + String name_; + ATOM atom_; +}; +} // namespace cru::ui diff --git a/src/util/any_map.cpp b/src/util/any_map.cpp new file mode 100644 index 00000000..c49464d3 --- /dev/null +++ b/src/util/any_map.cpp @@ -0,0 +1,30 @@ +#include "any_map.hpp" + +namespace cru::util { +AnyMap::ListenerToken AnyMap::RegisterValueChangeListener( + const String& key, const Listener& listener) { + const auto token = current_listener_token_++; + map_[key].second.push_back(token); + listeners_.emplace(token, listener); + return token; +} + +void AnyMap::UnregisterValueChangeListener(const ListenerToken token) { + const auto find_result = listeners_.find(token); + if (find_result != listeners_.cend()) listeners_.erase(find_result); +} + +void AnyMap::InvokeListeners(std::list& listener_list, + const std::any& value) { + auto i = listener_list.cbegin(); + while (i != listener_list.cend()) { + auto current_i = i++; + const auto find_result = listeners_.find(*current_i); + if (find_result != listeners_.cend()) + find_result->second(value); + else + listener_list.erase( + current_i); // otherwise remove the invalid listener token. + } +} +} // namespace cru::util diff --git a/src/util/any_map.hpp b/src/util/any_map.hpp new file mode 100644 index 00000000..d82167d2 --- /dev/null +++ b/src/util/any_map.hpp @@ -0,0 +1,94 @@ +#pragma once +#include "pre.hpp" + +#include +#include +#include +#include +#include + +#include "base.hpp" +#include "format.hpp" + +namespace cru::util { +// A map with String as key and any type as value. +// It also has notification when value with specified key changed. +class AnyMap : public Object { + public: + using ListenerToken = long; + using Listener = std::function; + + AnyMap() = default; + AnyMap(const AnyMap& other) = delete; + AnyMap(AnyMap&& other) = delete; + AnyMap& operator=(const AnyMap& other) = delete; + AnyMap& operator=(AnyMap&& other) = delete; + ~AnyMap() override = default; + + // return the value if the value exists and the type of value is T. + // return a null optional if value doesn't exists. + // throw std::runtime_error if type is mismatch. + template + std::optional GetOptionalValue(const String& key) const { + try { + const auto find_result = map_.find(key); + if (find_result != map_.cend()) { + const auto& value = find_result->second.first; + if (value.has_value()) return std::any_cast(value); + return std::nullopt; + } + return std::nullopt; + } catch (const std::bad_any_cast&) { + throw std::runtime_error( + Format("Value of key \"{}\" in AnyMap is not of the type {}.", + ToUtf8String(key), typeid(T).name())); + } + } + + // return the value if the value exists and the type of value is T. + // throw if value doesn't exists. (different from "GetOptionalValue"). + // throw std::runtime_error if type is mismatch. + template + T GetValue(const String& key) const { + const auto optional_value = GetOptionalValue(key); + if (optional_value.has_value()) + return optional_value.value(); + else + throw std::runtime_error( + Format("Key \"{}\" does not exists in AnyMap.", ToUtf8String(key))); + } + + // Set the value of key, and trigger all related listeners. + template + void SetValue(const String& key, T&& value) { + auto& p = map_[key]; + p.first = std::make_any(std::forward(value)); + InvokeListeners(p.second, p.first); + } + + // Remove the value of the key. + void ClearValue(const String& key) { + auto& p = map_[key]; + p.first = std::any{}; + InvokeListeners(p.second, std::any{}); + } + + // Add a listener which is called when value of key is changed. + // Return a token used to remove the listener. + ListenerToken RegisterValueChangeListener(const String& key, + const Listener& listener); + + // Remove a listener by token. + void UnregisterValueChangeListener(ListenerToken token); + + private: + void InvokeListeners(std::list& listener_list, + const std::any& value); + + private: + std::unordered_map>> + map_{}; + std::unordered_map listeners_{}; + ListenerToken current_listener_token_ = 0; +}; +} // namespace cru::util diff --git a/src/util/format.hpp b/src/util/format.hpp new file mode 100644 index 00000000..874c5b43 --- /dev/null +++ b/src/util/format.hpp @@ -0,0 +1,94 @@ +#pragma once +#include "pre.hpp" + +#include "base.hpp" + +namespace cru::util { +namespace details { +constexpr StringView PlaceHolder(type_tag) { return StringView(L"{}"); } + +constexpr MultiByteStringView PlaceHolder(type_tag) { + return MultiByteStringView("{}"); +} + +template +void FormatInternal(TString& string) { + const auto find_result = string.find(PlaceHolder(type_tag{})); + if (find_result != TString::npos) + throw std::invalid_argument("There is more placeholders than args."); +} + +template +void FormatInternal(TString& string, const T& arg, const TRest&... args) { + const auto find_result = string.find(PlaceHolder(type_tag{})); + if (find_result == TString::npos) + throw std::invalid_argument("There is less placeholders than args."); + + string.replace(find_result, 2, FormatToString(arg, type_tag{})); + FormatInternal(string, args...); +} +} // namespace details + +template +String Format(const StringView& format, const T&... args) { + String result(format); + details::FormatInternal(result, args...); + return result; +} + +template +MultiByteString Format(const MultiByteStringView& format, const T&... args) { + MultiByteString result(format); + details::FormatInternal(result, args...); + return result; +} + +#define CRU_FORMAT_NUMBER(type) \ + inline String FormatToString(const type number, type_tag) { \ + return std::to_wstring(number); \ + } \ + inline MultiByteString FormatToString(const type number, \ + type_tag) { \ + return std::to_string(number); \ + } + +CRU_FORMAT_NUMBER(int) +CRU_FORMAT_NUMBER(short) +CRU_FORMAT_NUMBER(long) +CRU_FORMAT_NUMBER(long long) +CRU_FORMAT_NUMBER(unsigned int) +CRU_FORMAT_NUMBER(unsigned short) +CRU_FORMAT_NUMBER(unsigned long) +CRU_FORMAT_NUMBER(unsigned long long) +CRU_FORMAT_NUMBER(float) +CRU_FORMAT_NUMBER(double) + +#undef CRU_FORMAT_NUMBER + +inline StringView FormatToString(const String& string, type_tag) { + return string; +} + +inline MultiByteString FormatToString(const MultiByteString& string, + type_tag) { + return string; +} + +inline StringView FormatToString(const StringView& string, type_tag) { + return string; +} + +inline MultiByteStringView FormatToString(const MultiByteStringView& string, + type_tag) { + return string; +} + +inline StringView FormatToString(const wchar_t* string, type_tag) { + return StringView(string); +} + +inline MultiByteStringView FormatToString(const char* string, + type_tag) { + return MultiByteString(string); +} +} // namespace cru::util diff --git a/src/util/math_util.hpp b/src/util/math_util.hpp new file mode 100644 index 00000000..01348641 --- /dev/null +++ b/src/util/math_util.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "pre.hpp" + +#include +#include + +namespace cru::util { +template >> +float Coerce(const T n, const std::optional min, + const std::optional max) { + if (min.has_value() && n < min.value()) return min.value(); + if (max.has_value() && n > max.value()) return max.value(); + return n; +} + +template >> +float Coerce(const T n, const T min, const T max) { + if (n < min) return min; + if (n > max) return max; + return n; +} + +template >> +float Coerce(const T n, const std::nullopt_t, const std::optional max) { + if (max.has_value() && n > max.value()) return max.value(); + return n; +} + +template >> +float Coerce(const T n, const std::optional min, const std::nullopt_t) { + if (min.has_value() && n < min.value()) return min.value(); + return n; +} + +template >> +float Coerce(const T n, const std::nullopt_t, const T max) { + if (n > max) return max; + return n; +} + +template >> +float Coerce(const T n, const T min, const std::nullopt_t) { + if (n < min) return min; + return n; +} + +template >> +T AtLeast0(const T value) { + return value < static_cast(0) ? static_cast(0) : value; +} +} // namespace cru::util diff --git a/src/util/string_util.cpp b/src/util/string_util.cpp new file mode 100644 index 00000000..3c765259 --- /dev/null +++ b/src/util/string_util.cpp @@ -0,0 +1,21 @@ +#include "string_util.hpp" + +#include "system_headers.hpp" +#include "exception.hpp" + +namespace cru::util { +MultiByteString ToUtf8String(const StringView& string) { + if (string.empty()) return MultiByteString(); + + const auto length = ::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, + nullptr, 0, nullptr, nullptr); + MultiByteString result; + result.reserve(length); + if (::WideCharToMultiByte(CP_UTF8, 0, string.data(), -1, result.data(), + static_cast(result.capacity()), nullptr, + nullptr) == 0) + throw Win32Error(::GetLastError(), + "Failed to convert wide string to UTF-8."); + return result; +} +} diff --git a/src/util/string_util.hpp b/src/util/string_util.hpp new file mode 100644 index 00000000..6d060089 --- /dev/null +++ b/src/util/string_util.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "pre.hpp" + +#include "base.hpp" + +namespace cru::util { +MultiByteString ToUtf8String(const StringView& string); +} -- cgit v1.2.3 From 1e3cad155a234d2e3e9b6aca650d4d1c4c9e8d4e Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 20 Mar 2019 17:08:57 +0800 Subject: ... --- CruUI.vcxproj | 5 +- CruUI.vcxproj.filters | 21 +- src/graph/graph.cpp | 367 ++++++++---------- src/graph/graph.hpp | 322 ++++++++------- src/ui/render/flex_layout_render_object.cpp | 212 ++++++++++ src/ui/render/flex_layout_render_object.hpp | 55 +++ src/ui/render/linear_layout_render_object.cpp | 0 src/ui/render/linear_layout_render_object.hpp | 1 - src/ui/render/render_object.cpp | 158 +++++--- src/ui/render/render_object.hpp | 133 +++---- src/ui/ui_base.cpp | 6 - src/ui/ui_base.hpp | 537 +++++++++++--------------- 12 files changed, 967 insertions(+), 850 deletions(-) create mode 100644 src/ui/render/flex_layout_render_object.cpp create mode 100644 src/ui/render/flex_layout_render_object.hpp delete mode 100644 src/ui/render/linear_layout_render_object.cpp delete mode 100644 src/ui/render/linear_layout_render_object.hpp delete mode 100644 src/ui/ui_base.cpp (limited to 'src/ui/render/render_object.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 9bedd3f8..bd321715 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -152,10 +152,9 @@ - + - @@ -182,7 +181,7 @@ - + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 9a85da3f..25923c76 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -36,9 +36,6 @@ Source Files - - Source Files - Source Files @@ -93,9 +90,6 @@ Source Files - - Source Files - Source Files @@ -105,6 +99,9 @@ Source Files + + Source Files + @@ -200,9 +197,6 @@ Header Files - - Header Files - Header Files @@ -218,6 +212,9 @@ Header Files + + Header Files + @@ -241,9 +238,6 @@ Source Files - - Source Files - Source Files @@ -262,9 +256,6 @@ Source Files - - Source Files - Source Files diff --git a/src/graph/graph.cpp b/src/graph/graph.cpp index eef95c8c..ed3fe5d5 100644 --- a/src/graph/graph.cpp +++ b/src/graph/graph.cpp @@ -3,224 +3,159 @@ #include "application.hpp" #include "exception.hpp" -namespace cru::graph -{ - using Microsoft::WRL::ComPtr; - - WindowRenderTarget::WindowRenderTarget(GraphManager* graph_manager, HWND hwnd) - { - this->graph_manager_ = graph_manager; - - const auto d3d11_device = graph_manager->GetD3D11Device(); - const auto dxgi_factory = graph_manager->GetDxgiFactory(); - - // Allocate a descriptor. - DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = { 0 }; - swap_chain_desc.Width = 0; // use automatic sizing - swap_chain_desc.Height = 0; - swap_chain_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format - swap_chain_desc.Stereo = false; - swap_chain_desc.SampleDesc.Count = 1; // don't use multi-sampling - swap_chain_desc.SampleDesc.Quality = 0; - swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swap_chain_desc.BufferCount = 2; // use double buffering to enable flip - swap_chain_desc.Scaling = DXGI_SCALING_NONE; - swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all apps must use this SwapEffect - swap_chain_desc.Flags = 0; - - - - // Get the final swap chain for this window from the DXGI factory. - ThrowIfFailed( - dxgi_factory->CreateSwapChainForHwnd( - d3d11_device.Get(), - hwnd, - &swap_chain_desc, - nullptr, - nullptr, - &dxgi_swap_chain_ - ) - ); - - CreateTargetBitmap(); - } - - WindowRenderTarget::~WindowRenderTarget() - { - - } - - void WindowRenderTarget::ResizeBuffer(const int width, const int height) - { - const auto graph_manager = graph_manager_; - const auto d2d1_device_context = graph_manager->GetD2D1DeviceContext(); - - ComPtr old_target; - d2d1_device_context->GetTarget(&old_target); - const auto target_this = old_target == this->target_bitmap_; - if (target_this) - d2d1_device_context->SetTarget(nullptr); - - old_target = nullptr; - target_bitmap_ = nullptr; - - ThrowIfFailed( - dxgi_swap_chain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0) - ); - - CreateTargetBitmap(); - - if (target_this) - d2d1_device_context->SetTarget(target_bitmap_.Get()); - } - - void WindowRenderTarget::SetAsTarget() - { - GetD2DDeviceContext()->SetTarget(target_bitmap_.Get()); - } - - void WindowRenderTarget::Present() - { - ThrowIfFailed( - dxgi_swap_chain_->Present(1, 0) - ); - } - - void WindowRenderTarget::CreateTargetBitmap() - { - // Direct2D needs the dxgi version of the backbuffer surface pointer. - ComPtr dxgiBackBuffer; - ThrowIfFailed( - dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer)) - ); - - const auto dpi = graph_manager_->GetDpi(); - - auto bitmap_properties = - D2D1::BitmapProperties1( - D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, - D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), - dpi.x, - dpi.y - ); - - // Get a D2D surface from the DXGI back buffer to use as the D2D render target. - ThrowIfFailed( - graph_manager_->GetD2D1DeviceContext()->CreateBitmapFromDxgiSurface( - dxgiBackBuffer.Get(), - &bitmap_properties, - &target_bitmap_ - ) - ); - } - - GraphManager* GraphManager::GetInstance() - { - return Application::GetInstance()->ResolveSingleton([](auto) - { - return new GraphManager{}; - }); - } - - GraphManager::GraphManager() - { - UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; +namespace cru::graph { +using Microsoft::WRL::ComPtr; + +WindowRenderTarget::WindowRenderTarget(GraphManager* graph_manager, HWND hwnd) { + this->graph_manager_ = graph_manager; + + const auto d3d11_device = graph_manager->GetD3D11Device(); + const auto dxgi_factory = graph_manager->GetDxgiFactory(); + + // Allocate a descriptor. + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {0}; + swap_chain_desc.Width = 0; // use automatic sizing + swap_chain_desc.Height = 0; + swap_chain_desc.Format = + DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format + swap_chain_desc.Stereo = false; + swap_chain_desc.SampleDesc.Count = 1; // don't use multi-sampling + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.BufferCount = 2; // use double buffering to enable flip + swap_chain_desc.Scaling = DXGI_SCALING_NONE; + swap_chain_desc.SwapEffect = + DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all apps must use this SwapEffect + swap_chain_desc.Flags = 0; + + // Get the final swap chain for this window from the DXGI factory. + ThrowIfFailed(dxgi_factory->CreateSwapChainForHwnd( + d3d11_device.Get(), hwnd, &swap_chain_desc, nullptr, nullptr, + &dxgi_swap_chain_)); + + CreateTargetBitmap(); +} + +WindowRenderTarget::~WindowRenderTarget() {} + +void WindowRenderTarget::ResizeBuffer(const int width, const int height) { + const auto graph_manager = graph_manager_; + const auto d2d1_device_context = graph_manager->GetD2D1DeviceContext(); + + ComPtr old_target; + d2d1_device_context->GetTarget(&old_target); + const auto target_this = old_target == this->target_bitmap_; + if (target_this) d2d1_device_context->SetTarget(nullptr); + + old_target = nullptr; + target_bitmap_ = nullptr; + + ThrowIfFailed(dxgi_swap_chain_->ResizeBuffers(0, width, height, + DXGI_FORMAT_UNKNOWN, 0)); + + CreateTargetBitmap(); + + if (target_this) d2d1_device_context->SetTarget(target_bitmap_.Get()); +} + +void WindowRenderTarget::SetAsTarget() { + GetD2DDeviceContext()->SetTarget(target_bitmap_.Get()); +} + +void WindowRenderTarget::Present() { + ThrowIfFailed(dxgi_swap_chain_->Present(1, 0)); +} + +void WindowRenderTarget::CreateTargetBitmap() { + // Direct2D needs the dxgi version of the backbuffer surface pointer. + ComPtr dxgiBackBuffer; + ThrowIfFailed(dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))); + + const auto dpi = graph_manager_->GetDpi(); + + auto bitmap_properties = D2D1::BitmapProperties1( + D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), + dpi.x, dpi.y); + + // Get a D2D surface from the DXGI back buffer to use as the D2D render + // target. + ThrowIfFailed( + graph_manager_->GetD2D1DeviceContext()->CreateBitmapFromDxgiSurface( + dxgiBackBuffer.Get(), &bitmap_properties, &target_bitmap_)); +} + +GraphManager* GraphManager::GetInstance() { + return Application::GetInstance()->ResolveSingleton( + [](auto) { return new GraphManager{}; }); +} + +GraphManager::GraphManager() { + UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; #ifdef CRU_DEBUG - creation_flags |= D3D11_CREATE_DEVICE_DEBUG; + creation_flags |= D3D11_CREATE_DEVICE_DEBUG; #endif - const D3D_FEATURE_LEVEL feature_levels[] = - { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; - - - ThrowIfFailed(D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_HARDWARE, - nullptr, - creation_flags, - feature_levels, - ARRAYSIZE(feature_levels), - D3D11_SDK_VERSION, - &d3d11_device_, - nullptr, - &d3d11_device_context_ - )); - - Microsoft::WRL::ComPtr dxgi_device; - - ThrowIfFailed(d3d11_device_.As(&dxgi_device)); - - ThrowIfFailed(D2D1CreateFactory( - D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory1), - &d2d1_factory_ - )); - - ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); - - ThrowIfFailed(d2d1_device_->CreateDeviceContext( - D2D1_DEVICE_CONTEXT_OPTIONS_NONE, - &d2d1_device_context_ - )); - - // Identify the physical adapter (GPU or card) this device is runs on. - ComPtr dxgi_adapter; - ThrowIfFailed( - dxgi_device->GetAdapter(&dxgi_adapter) - ); - - // Get the factory object that created the DXGI device. - ThrowIfFailed( - dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_)) - ); - - - ThrowIfFailed(DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast(dwrite_factory_.GetAddressOf()) - )); - - dwrite_factory_->GetSystemFontCollection(&dwrite_system_font_collection_); - } - - GraphManager::~GraphManager() - { - - } - - std::shared_ptr GraphManager::CreateWindowRenderTarget(HWND hwnd) - { - return std::make_shared(this, hwnd); - } - - Dpi GraphManager::GetDpi() const - { - Dpi dpi; - d2d1_factory_->GetDesktopDpi(&dpi.x, &dpi.y); - return dpi; - } - - void GraphManager::ReloadSystemMetrics() - { - ThrowIfFailed( - d2d1_factory_->ReloadSystemMetrics() - ); - } - - Microsoft::WRL::ComPtr CreateSolidColorBrush(const D2D1_COLOR_F& color) - { - Microsoft::WRL::ComPtr brush; - ThrowIfFailed(GraphManager::GetInstance()->GetD2D1DeviceContext()->CreateSolidColorBrush(color, &brush)); - return brush; - } + const D3D_FEATURE_LEVEL feature_levels[] = { + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; + + ThrowIfFailed(D3D11CreateDevice( + nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, creation_flags, + feature_levels, ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, + &d3d11_device_, nullptr, &d3d11_device_context_)); + + Microsoft::WRL::ComPtr dxgi_device; + + ThrowIfFailed(d3d11_device_.As(&dxgi_device)); + + ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory1), &d2d1_factory_)); + + ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); + + ThrowIfFailed(d2d1_device_->CreateDeviceContext( + D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1_device_context_)); + + // Identify the physical adapter (GPU or card) this device is runs on. + ComPtr dxgi_adapter; + ThrowIfFailed(dxgi_device->GetAdapter(&dxgi_adapter)); + + // Get the factory object that created the DXGI device. + ThrowIfFailed(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_))); + + ThrowIfFailed(DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), + reinterpret_cast(dwrite_factory_.GetAddressOf()))); + + dwrite_factory_->GetSystemFontCollection(&dwrite_system_font_collection_); +} + +GraphManager::~GraphManager() {} + +std::shared_ptr GraphManager::CreateWindowRenderTarget( + HWND hwnd) { + return std::make_shared(this, hwnd); +} + +Dpi GraphManager::GetDpi() const { + Dpi dpi; + d2d1_factory_->GetDesktopDpi(&dpi.x, &dpi.y); + return dpi; +} + +void GraphManager::ReloadSystemMetrics() { + ThrowIfFailed(d2d1_factory_->ReloadSystemMetrics()); +} + +Microsoft::WRL::ComPtr CreateSolidColorBrush( + const D2D1_COLOR_F& color) { + Microsoft::WRL::ComPtr brush; + ThrowIfFailed(GraphManager::GetInstance() + ->GetD2D1DeviceContext() + ->CreateSolidColorBrush(color, &brush)); + return brush; } +} // namespace cru::graph diff --git a/src/graph/graph.hpp b/src/graph/graph.hpp index 440b0594..bad5b6d0 100644 --- a/src/graph/graph.hpp +++ b/src/graph/graph.hpp @@ -1,180 +1,160 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" -#include #include +#include +#include "system_headers.hpp" #include "base.hpp" +namespace cru::graph { +class GraphManager; + +// Represents a window render target. +class WindowRenderTarget : public Object { + public: + WindowRenderTarget(GraphManager* graph_manager, HWND hwnd); + WindowRenderTarget(const WindowRenderTarget& other) = delete; + WindowRenderTarget(WindowRenderTarget&& other) = delete; + WindowRenderTarget& operator=(const WindowRenderTarget& other) = delete; + WindowRenderTarget& operator=(WindowRenderTarget&& other) = delete; + ~WindowRenderTarget() override; + + public: + // Get the graph manager that created the render target. + GraphManager* GetGraphManager() const { return graph_manager_; } + + // Get the d2d device context. + inline Microsoft::WRL::ComPtr GetD2DDeviceContext() const; + + // Get the target bitmap which can be set as the ID2D1DeviceContext's target. + Microsoft::WRL::ComPtr GetTargetBitmap() const { + return target_bitmap_; + } + + // Resize the underlying buffer. + void ResizeBuffer(int width, int height); + + // Set this render target as the d2d device context's target. + void SetAsTarget(); + + // Present the data of the underlying buffer to the window. + void Present(); + + private: + void CreateTargetBitmap(); + + private: + GraphManager* graph_manager_; + Microsoft::WRL::ComPtr dxgi_swap_chain_; + Microsoft::WRL::ComPtr target_bitmap_; +}; + +struct Dpi { + float x; + float y; +}; + +class GraphManager final : public Object { + public: + static GraphManager* GetInstance(); + + private: + GraphManager(); + + public: + GraphManager(const GraphManager& other) = delete; + GraphManager(GraphManager&& other) = delete; + GraphManager& operator=(const GraphManager& other) = delete; + GraphManager& operator=(GraphManager&& other) = delete; + ~GraphManager() override; + + public: + Microsoft::WRL::ComPtr GetD2D1Factory() const { + return d2d1_factory_; + } + + Microsoft::WRL::ComPtr GetD2D1DeviceContext() const { + return d2d1_device_context_; + } + + Microsoft::WRL::ComPtr GetD3D11Device() const { + return d3d11_device_; + } + + Microsoft::WRL::ComPtr GetDxgiFactory() const { + return dxgi_factory_; + } + + Microsoft::WRL::ComPtr GetDWriteFactory() const { + return dwrite_factory_; + } + + // Create a window render target with the HWND. + std::shared_ptr CreateWindowRenderTarget(HWND hwnd); + + // Get the desktop dpi. + Dpi GetDpi() const; + + // Reload system metrics including desktop dpi. + void ReloadSystemMetrics(); + + Microsoft::WRL::ComPtr GetSystemFontCollection() + const { + return dwrite_system_font_collection_.Get(); + } + + private: + Microsoft::WRL::ComPtr d3d11_device_; + Microsoft::WRL::ComPtr d3d11_device_context_; + Microsoft::WRL::ComPtr d2d1_factory_; + Microsoft::WRL::ComPtr d2d1_device_; + Microsoft::WRL::ComPtr d2d1_device_context_; + Microsoft::WRL::ComPtr dxgi_factory_; + + Microsoft::WRL::ComPtr dwrite_factory_; + Microsoft::WRL::ComPtr dwrite_system_font_collection_; +}; + +inline int DipToPixelInternal(const float dip, const float dpi) { + return static_cast(dip * dpi / 96.0f); +} + +inline int DipToPixelX(const float dip_x) { + return DipToPixelInternal(dip_x, GraphManager::GetInstance()->GetDpi().x); +} + +inline int DipToPixelY(const float dip_y) { + return DipToPixelInternal(dip_y, GraphManager::GetInstance()->GetDpi().y); +} -namespace cru::graph -{ - class GraphManager; - - //Represents a window render target. - class WindowRenderTarget : public Object - { - public: - WindowRenderTarget(GraphManager* graph_manager, HWND hwnd); - WindowRenderTarget(const WindowRenderTarget& other) = delete; - WindowRenderTarget(WindowRenderTarget&& other) = delete; - WindowRenderTarget& operator=(const WindowRenderTarget& other) = delete; - WindowRenderTarget& operator=(WindowRenderTarget&& other) = delete; - ~WindowRenderTarget() override; - - public: - //Get the graph manager that created the render target. - GraphManager* GetGraphManager() const - { - return graph_manager_; - } - - //Get the d2d device context. - inline Microsoft::WRL::ComPtr GetD2DDeviceContext() const; - - //Get the target bitmap which can be set as the ID2D1DeviceContext's target. - Microsoft::WRL::ComPtr GetTargetBitmap() const - { - return target_bitmap_; - } - - //Resize the underlying buffer. - void ResizeBuffer(int width, int height); - - //Set this render target as the d2d device context's target. - void SetAsTarget(); - - //Present the data of the underlying buffer to the window. - void Present(); - - private: - void CreateTargetBitmap(); - - private: - GraphManager* graph_manager_; - Microsoft::WRL::ComPtr dxgi_swap_chain_; - Microsoft::WRL::ComPtr target_bitmap_; - }; - - struct Dpi - { - float x; - float y; - }; - - class GraphManager final : public Object - { - public: - static GraphManager* GetInstance(); - - private: - GraphManager(); - public: - GraphManager(const GraphManager& other) = delete; - GraphManager(GraphManager&& other) = delete; - GraphManager& operator=(const GraphManager& other) = delete; - GraphManager& operator=(GraphManager&& other) = delete; - ~GraphManager() override; - - public: - Microsoft::WRL::ComPtr GetD2D1Factory() const - { - return d2d1_factory_; - } - - Microsoft::WRL::ComPtr GetD2D1DeviceContext() const - { - return d2d1_device_context_; - } - - Microsoft::WRL::ComPtr GetD3D11Device() const - { - return d3d11_device_; - } - - Microsoft::WRL::ComPtr GetDxgiFactory() const - { - return dxgi_factory_; - } - - Microsoft::WRL::ComPtr GetDWriteFactory() const - { - return dwrite_factory_; - } - - - //Create a window render target with the HWND. - std::shared_ptr CreateWindowRenderTarget(HWND hwnd); - - //Get the desktop dpi. - Dpi GetDpi() const; - - //Reload system metrics including desktop dpi. - void ReloadSystemMetrics(); - - Microsoft::WRL::ComPtr GetSystemFontCollection() const - { - return dwrite_system_font_collection_.Get(); - } - - private: - Microsoft::WRL::ComPtr d3d11_device_; - Microsoft::WRL::ComPtr d3d11_device_context_; - Microsoft::WRL::ComPtr d2d1_factory_; - Microsoft::WRL::ComPtr d2d1_device_; - Microsoft::WRL::ComPtr d2d1_device_context_; - Microsoft::WRL::ComPtr dxgi_factory_; - - Microsoft::WRL::ComPtr dwrite_factory_; - Microsoft::WRL::ComPtr dwrite_system_font_collection_; - }; - - inline int DipToPixelInternal(const float dip, const float dpi) - { - return static_cast(dip * dpi / 96.0f); - } - - inline int DipToPixelX(const float dip_x) - { - return DipToPixelInternal(dip_x, GraphManager::GetInstance()->GetDpi().x); - } - - inline int DipToPixelY(const float dip_y) - { - return DipToPixelInternal(dip_y, GraphManager::GetInstance()->GetDpi().y); - } - - inline float DipToPixelInternal(const int pixel, const float dpi) - { - return static_cast(pixel) * 96.0f / dpi; - } - - inline float PixelToDipX(const int pixel_x) - { - return DipToPixelInternal(pixel_x, GraphManager::GetInstance()->GetDpi().x); - } - - inline float PixelToDipY(const int pixel_y) - { - return DipToPixelInternal(pixel_y, GraphManager::GetInstance()->GetDpi().y); - } - - Microsoft::WRL::ComPtr WindowRenderTarget::GetD2DDeviceContext() const - { - return graph_manager_->GetD2D1DeviceContext(); - } - - inline void WithTransform(ID2D1DeviceContext* device_context, const D2D1_MATRIX_3X2_F matrix, const std::function& action) - { - D2D1_MATRIX_3X2_F old_transform; - device_context->GetTransform(&old_transform); - device_context->SetTransform(old_transform * matrix); - action(device_context); - device_context->SetTransform(old_transform); - } - - Microsoft::WRL::ComPtr CreateSolidColorBrush(const D2D1_COLOR_F& color); +inline float DipToPixelInternal(const int pixel, const float dpi) { + return static_cast(pixel) * 96.0f / dpi; } + +inline float PixelToDipX(const int pixel_x) { + return DipToPixelInternal(pixel_x, GraphManager::GetInstance()->GetDpi().x); +} + +inline float PixelToDipY(const int pixel_y) { + return DipToPixelInternal(pixel_y, GraphManager::GetInstance()->GetDpi().y); +} + +Microsoft::WRL::ComPtr +WindowRenderTarget::GetD2DDeviceContext() const { + return graph_manager_->GetD2D1DeviceContext(); +} + +inline void WithTransform( + ID2D1RenderTarget* device_context, const D2D1_MATRIX_3X2_F matrix, + const std::function& action) { + D2D1_MATRIX_3X2_F old_transform; + device_context->GetTransform(&old_transform); + device_context->SetTransform(old_transform * matrix); + action(device_context); + device_context->SetTransform(old_transform); +} + +Microsoft::WRL::ComPtr CreateSolidColorBrush( + const D2D1_COLOR_F& color); +} // namespace cru::graph diff --git a/src/ui/render/flex_layout_render_object.cpp b/src/ui/render/flex_layout_render_object.cpp new file mode 100644 index 00000000..4e5171a2 --- /dev/null +++ b/src/ui/render/flex_layout_render_object.cpp @@ -0,0 +1,212 @@ +#include "flex_layout_render_object.hpp" + +#include +#include + +#include "cru_debug.hpp" +#include "graph/graph.hpp" + +namespace cru::ui::render { +FlexChildLayoutData* FlexLayoutRenderObject::GetChildLayoutData(int position) { + if (position < 0 || position >= child_layout_data_.size()) + throw std::invalid_argument("Position out of bound."); + + return &child_layout_data_[position]; +} + +void FlexLayoutRenderObject::Draw(ID2D1RenderTarget* render_target) { + for (const auto child : GetChildren()) { + auto offset = child->GetOffset(); + graph::WithTransform(render_target, + D2D1::Matrix3x2F::Translation(offset.x, offset.y), + [child](auto rt) { child->Draw(rt); }); + } +} + +RenderObject* FlexLayoutRenderObject::HitTest(const Point& point) { + const auto& children = GetChildren(); + for (auto i = children.crbegin(); i != children.crend(); ++i) { + auto offset = (*i)->GetOffset(); + Point p{point.x - offset.x, point.y - offset.y}; + const auto result = (*i)->HitTest(point); + if (result != nullptr) { + return result; + } + } + return Rect{Point::Zero(), GetSize()}.IsPointInside(point) ? this : nullptr; +} + +void FlexLayoutRenderObject::OnAddChild(RenderObject* new_child, int position) { + child_layout_data_.emplace(child_layout_data_.cbegin() + position); +} + +void FlexLayoutRenderObject::OnRemoveChild(RenderObject* removed_child, + int position) { + child_layout_data_.erase(child_layout_data_.cbegin() + position); +} + +Size FlexLayoutRenderObject::OnMeasureContent(const Size& available_size) { + std::vector has_basis_children; + std::vector no_basis_children; + std::vector grow_children; + std::vector shrink_chilren; + for (int i = 0; i < child_layout_data_.size(); i++) { + const auto& layout_data = child_layout_data_[i]; + if (layout_data.flex_basis.has_value()) + has_basis_children.push_back(i); + else + no_basis_children.push_back(i); + if (layout_data.flex_grow > 0) grow_children.push_back(i); + if (layout_data.flex_shrink > 0) shrink_chilren.push_back(i); + } + + std::function get_main_length; + std::function get_cross_length; + std::function create_size; + + if (direction_ == FlexDirection::Horizontal || + direction_ == FlexDirection::HorizontalReverse) { + get_main_length = [](const Size& size) { return size.width; }; + get_cross_length = [](const Size& size) { return size.height; }; + create_size = [](float main, float cross) { return Size(main, cross); }; + } else { + get_main_length = [](const Size& size) { return size.height; }; + get_cross_length = [](const Size& size) { return size.width; }; + create_size = [](float main, float cross) { return Size(cross, main); }; + } + + const auto& children = GetChildren(); + + float remain_main_length = get_main_length(available_size); + float max_cross_length = 0; + + for (const int i : has_basis_children) { + const auto child = children[i]; + const float basis = child_layout_data_[i].flex_basis.value(); + child->Measure(create_size(basis, get_cross_length(available_size))); + remain_main_length -= basis; + const float child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize(create_size(basis, child_preferred_cross_length)); + max_cross_length = std::max(max_cross_length, child_preferred_cross_length); + } + + for (const int i : no_basis_children) { + const auto child = children[i]; + child->Measure(create_size(remain_main_length > 0 ? remain_main_length : 0, + get_cross_length(available_size))); + remain_main_length -= get_main_length(child->GetPreferredSize()); + max_cross_length = + std::max(max_cross_length, get_cross_length(child->GetPreferredSize())); + } + + if (remain_main_length > 0) { + float total_grow = 0; + for (const int i : grow_children) + total_grow += child_layout_data_[i].flex_grow; + + for (const int i : grow_children) { + const float distributed_grow_length = + remain_main_length * (child_layout_data_[i].flex_grow / total_grow); + const auto child = children[i]; + const float new_main_length = + get_main_length(child->GetPreferredSize()) + distributed_grow_length; + child->Measure( + create_size(new_main_length, get_cross_length(available_size))); + const float new_child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize( + create_size(new_main_length, new_child_preferred_cross_length)); + max_cross_length = + std::max(max_cross_length, new_child_preferred_cross_length); + } + } + + if (remain_main_length < 0) { + float total_shrink = 0; + for (const int i : shrink_chilren) + total_shrink += child_layout_data_[i].flex_shrink; + + for (const int i : shrink_chilren) { + const float distributed_shrink_length = // negative + remain_main_length * + (child_layout_data_[i].flex_shrink / total_shrink); + const auto child = children[i]; + float new_main_length = get_main_length(child->GetPreferredSize()) + + distributed_shrink_length; + new_main_length = new_main_length > 0 ? new_main_length : 0; + child->Measure( + create_size(new_main_length, get_cross_length(available_size))); + const float new_child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize( + create_size(new_main_length, new_child_preferred_cross_length)); + max_cross_length = + std::max(max_cross_length, new_child_preferred_cross_length); + } + } + + return create_size(get_main_length(available_size) - + (remain_main_length > 0 ? remain_main_length : 0), + max_cross_length); +} + +void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { + auto calculate_cross_anchor = [](Alignment alignment, float start_point, + float content_length, + float preferred_length) -> float { + switch (alignment) { + case Alignment::Start: + return start_point; + case Alignment::Center: + return start_point + (content_length - preferred_length) / 2.0f; + case Alignment::End: + return start_point + content_length - preferred_length; + } + }; + + const auto& children = GetChildren(); + if (direction_ == FlexDirection::Horizontal || + direction_ == FlexDirection::HorizontalReverse) { + auto anchor_x = 0; + for (int i = 0; i < children.size(); i++) { + const auto child = children[i]; + const auto size = child->GetPreferredSize(); + + float real_anchor_x; + if (direction_ == FlexDirection::Horizontal) { + real_anchor_x = anchor_x + content_rect.left; + } else { + real_anchor_x = content_rect.GetRight() - anchor_x; + } + child->Layout(Rect{real_anchor_x, + calculate_cross_anchor( + child_layout_data_[i].alignment, content_rect.top, + content_rect.height, size.height), + size.width, size.height}); + + anchor_x += size.width; + } + } else { + auto anchor_y = 0; + for (int i = 0; i < children.size(); i++) { + const auto child = children[i]; + const auto size = child->GetPreferredSize(); + + float real_anchor_y; + if (direction_ == FlexDirection::Vertical) { + real_anchor_y = anchor_y + content_rect.top; + } else { + real_anchor_y = content_rect.GetBottom() - anchor_y; + } + child->Layout(Rect{real_anchor_y, + calculate_cross_anchor(child_layout_data_[i].alignment, + content_rect.left, + content_rect.width, size.width), + size.width, size.height}); + + anchor_y += size.height; + } + } +} +} // namespace cru::ui::render diff --git a/src/ui/render/flex_layout_render_object.hpp b/src/ui/render/flex_layout_render_object.hpp new file mode 100644 index 00000000..7172f0c0 --- /dev/null +++ b/src/ui/render/flex_layout_render_object.hpp @@ -0,0 +1,55 @@ +#pragma once +#include "pre.hpp" + +#include + +#include "render_object.hpp" + +namespace cru::ui::render { +enum class FlexDirection { + Horizontal, + HorizontalReverse, + Vertical, + VertivalReverse +}; + +enum class Alignment { Start, End, Center }; + +struct FlexChildLayoutData { + std::optional flex_basis; + float flex_grow = 0; + float flex_shrink = 0; + Alignment alignment = Alignment::Center; +}; + +class FlexLayoutRenderObject : public RenderObject { + public: + FlexLayoutRenderObject() = default; + FlexLayoutRenderObject(const FlexLayoutRenderObject& other) = delete; + FlexLayoutRenderObject& operator=(const FlexLayoutRenderObject& other) = + delete; + FlexLayoutRenderObject(FlexLayoutRenderObject&& other) = delete; + FlexLayoutRenderObject& operator=(FlexLayoutRenderObject&& other) = delete; + ~FlexLayoutRenderObject() override = default; + + FlexDirection GetFlexDirection() const { return direction_; } + void SetFlexDirection(FlexDirection direction) { direction_ = direction; } + + FlexChildLayoutData* GetChildLayoutData(int position); + + void Draw(ID2D1RenderTarget* render_target) override; + + RenderObject* HitTest(const Point& point) override; + + protected: + void OnAddChild(RenderObject* new_child, int position) override; + void OnRemoveChild(RenderObject* removed_child, int position) override; + + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + private: + FlexDirection direction_ = FlexDirection::Horizontal; + std::vector child_layout_data_{}; +}; +} // namespace cru::ui::render diff --git a/src/ui/render/linear_layout_render_object.cpp b/src/ui/render/linear_layout_render_object.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/ui/render/linear_layout_render_object.hpp b/src/ui/render/linear_layout_render_object.hpp deleted file mode 100644 index 6f70f09b..00000000 --- a/src/ui/render/linear_layout_render_object.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index c2aaeb62..0035d1be 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -1,90 +1,130 @@ #include "render_object.hpp" -#include -namespace cru::ui::render -{ -void RenderObject::SetRenderHost(IRenderHost* new_render_host) -{ - if (new_render_host == render_host_) return; +#include "cru_debug.hpp" - const auto old = render_host_; - render_host_ = new_render_host; - OnRenderHostChanged(old, new_render_host); +namespace cru::ui::render { +void RenderObject::SetRenderHost(IRenderHost* new_render_host) { + if (new_render_host == render_host_) return; + + const auto old = render_host_; + render_host_ = new_render_host; + OnRenderHostChanged(old, new_render_host); } -void RenderObject::AddChild(RenderObject* render_object, const int position) -{ - if (render_object->GetParent() != nullptr) - throw std::invalid_argument("Render object already has a parent."); +void RenderObject::AddChild(RenderObject* render_object, const int position) { + if (render_object->GetParent() != nullptr) + throw std::invalid_argument("Render object already has a parent."); - if (position < 0) - throw std::invalid_argument("Position index is less than 0."); + if (position < 0) + throw std::invalid_argument("Position index is less than 0."); - if (static_cast::size_type>(position) > - children_.size()) - throw std::invalid_argument("Position index is out of bound."); + if (static_cast::size_type>(position) > + children_.size()) + throw std::invalid_argument("Position index is out of bound."); - children_.insert(children_.cbegin() + position, render_object); - render_object->SetParent(this); - OnAddChild(render_object, position); + children_.insert(children_.cbegin() + position, render_object); + render_object->SetParent(this); + OnAddChild(render_object, position); } -void RenderObject::RemoveChild(const int position) -{ - if (position < 0) - throw std::invalid_argument("Position index is less than 0."); +void RenderObject::RemoveChild(const int position) { + if (position < 0) + throw std::invalid_argument("Position index is less than 0."); - if (static_cast::size_type>(position) >= - children_.size()) - throw std::invalid_argument("Position index is out of bound."); + if (static_cast::size_type>(position) >= + children_.size()) + throw std::invalid_argument("Position index is out of bound."); - const auto i = children_.cbegin() + position; - const auto removed_child = *i; - children_.erase(i); - removed_child->SetParent(nullptr); - OnRemoveChild(removed_child, position); + const auto i = children_.cbegin() + position; + const auto removed_child = *i; + children_.erase(i); + removed_child->SetParent(nullptr); + OnRemoveChild(removed_child, position); } +void RenderObject::Measure(const Size& available_size) { + OnMeasureCore(available_size); +} -void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, - IRenderHost* new_render_host) -{ +void RenderObject::Layout(const Rect& rect) { + SetOffset(rect.GetLeftTop()); + SetSize(rect.GetSize()); + OnLayoutCore(Rect{Point::Zero(), rect.GetSize()}); } -void RenderObject::InvalidateRenderHostPaint() const -{ - if (render_host_ != nullptr) render_host_->InvalidatePaint(); +void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, + IRenderHost* new_render_host) {} + +void RenderObject::InvalidateRenderHostPaint() const { + if (render_host_ != nullptr) render_host_->InvalidatePaint(); } -void RenderObject::InvalidateRenderHostLayout() const -{ - if (render_host_ != nullptr) render_host_->InvalidateLayout(); +void RenderObject::InvalidateRenderHostLayout() const { + if (render_host_ != nullptr) render_host_->InvalidateLayout(); } void RenderObject::OnParentChanged(RenderObject* old_parent, - RenderObject* new_parent) -{ -} + RenderObject* new_parent) {} +void RenderObject::OnAddChild(RenderObject* new_child, int position) {} -void RenderObject::OnAddChild(RenderObject* new_child, int position) -{ -} +void RenderObject::OnRemoveChild(RenderObject* removed_child, int position) {} -void RenderObject::OnRemoveChild(RenderObject* removed_child, int position) -{ +void RenderObject::SetParent(RenderObject* new_parent) { + const auto old_parent = parent_; + parent_ = new_parent; + OnParentChanged(old_parent, new_parent); } -void RenderObject::SetParent(RenderObject* new_parent) -{ - const auto old_parent = parent_; - parent_ = new_parent; - OnParentChanged(old_parent, new_parent); +void RenderObject::OnMeasureCore(const Size& available_size) { + Size margin_padding_size{ + margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), + margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; + const auto content_available_size = available_size - margin_padding_size; + auto coerced_content_available_size = content_available_size; + + if (coerced_content_available_size.width < 0) { + debug::DebugMessage( + L"Measure: horizontal length of padding and margin is bigger than " + L"available length."); + coerced_content_available_size.width = 0; + } + if (coerced_content_available_size.height < 0) { + debug::DebugMessage( + L"Measure: vertical length of padding and margin is bigger than " + L"available length."); + coerced_content_available_size.height = 0; + } + + const auto actual_content_size = + OnMeasureContent(coerced_content_available_size); + + SetPreferredSize(margin_padding_size + content_available_size + + actual_content_size); } - -void LinearLayoutRenderObject::Measure(const MeasureConstraint& constraint) -{ - +void RenderObject::OnLayoutCore(const Rect& rect) { + Size margin_padding_size{ + margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), + margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; + const auto content_available_size = rect.GetSize() - margin_padding_size; + auto coerced_content_available_size = content_available_size; + + if (coerced_content_available_size.width < 0) { + debug::DebugMessage( + L"Layout: horizontal length of padding and margin is bigger than " + L"available length."); + coerced_content_available_size.width = 0; + } + if (coerced_content_available_size.height < 0) { + debug::DebugMessage( + L"Layout: vertical length of padding and margin is bigger than " + L"available length."); + coerced_content_available_size.height = 0; + } + + OnLayoutContent(Rect{margin_.left + padding_.left, margin_.top + padding_.top, + coerced_content_available_size.width, + coerced_content_available_size.height}); } } // namespace cru::ui::render diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 00f761d1..aeba1457 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -1,5 +1,4 @@ #pragma once - #include "pre.hpp" #include @@ -9,101 +8,89 @@ #include "base.hpp" #include "ui/ui_base.hpp" -namespace cru::ui::render -{ -struct MeasureConstraint -{ - std::optional min_width; - std::optional min_height; - std::optional max_width; - std::optional max_height; +namespace cru::ui::render { +struct IRenderHost : Interface { + virtual void InvalidatePaint() = 0; + virtual void InvalidateLayout() = 0; }; +class RenderObject : public Object { + protected: + RenderObject() = default; -struct LayoutConstraint -{ - float preferred_width; - float preferred_height; -}; + public: + RenderObject(const RenderObject& other) = delete; + RenderObject(RenderObject&& other) = delete; + RenderObject& operator=(const RenderObject& other) = delete; + RenderObject& operator=(RenderObject&& other) = delete; + ~RenderObject() override = default; + IRenderHost* GetRenderHost() const { return render_host_; } + void SetRenderHost(IRenderHost* new_render_host); -struct IRenderHost : Interface -{ - virtual void InvalidatePaint() = 0; - virtual void InvalidateLayout() = 0; -}; + RenderObject* GetParent() const { return parent_; } + const std::vector& GetChildren() const { return children_; } + void AddChild(RenderObject* render_object, int position); + void RemoveChild(int position); -// features: -// 1. tree -// 2. layout -// 3. paint -// 3. hit test -class RenderObject : public Object -{ -protected: - RenderObject() = default; + Point GetOffset() const { return offset_; } + void SetOffset(const Point& offset) { offset_ = offset; } + Size GetSize() const { return size_; } + void SetSize(const Size& size) { size_ = size; } -public: - RenderObject(const RenderObject& other) = delete; - RenderObject(RenderObject&& other) = delete; - RenderObject& operator=(const RenderObject& other) = delete; - RenderObject& operator=(RenderObject&& other) = delete; - ~RenderObject() override = default; + Thickness GetMargin() const { return margin_; } + void SetMargin(const Thickness& margin) { margin_ = margin; } - IRenderHost* GetRenderHost() const { return render_host_; } - void SetRenderHost(IRenderHost* new_render_host); + Thickness GetPadding() const { return padding_; } + void SetPadding(const Thickness& padding) { padding_ = padding; } - RenderObject* GetParent() const { return parent_; } + Size GetPreferredSize() const { return preferred_size_; } + void SetPreferredSize(const Size& preferred_size) { + preferred_size_ = preferred_size; + } - const std::vector& GetChildren() const { return children_; } - void AddChild(RenderObject* render_object, int position); - void RemoveChild(int position); + void Measure(const Size& available_size); + void Layout(const Rect& rect); - virtual void Measure(const MeasureConstraint& constraint) = 0; - virtual void Layout(const LayoutConstraint& constraint) = 0; + virtual void Draw(ID2D1RenderTarget* render_target) = 0; - virtual void Draw(ID2D1RenderTarget* render_target) = 0; + virtual RenderObject* HitTest(const Point& point) = 0; - virtual void HitTest(const Point& point) = 0; + protected: + virtual void OnRenderHostChanged(IRenderHost* old_render_host, + IRenderHost* new_render_host); -protected: - virtual void OnRenderHostChanged(IRenderHost* old_render_host, - IRenderHost* new_render_host); + void InvalidateRenderHostPaint() const; + void InvalidateRenderHostLayout() const; - void InvalidateRenderHostPaint() const; - void InvalidateRenderHostLayout() const; + virtual void OnParentChanged(RenderObject* old_parent, + RenderObject* new_parent); - virtual void OnParentChanged(RenderObject* old_parent, - RenderObject* new_parent); + virtual void OnAddChild(RenderObject* new_child, int position); + virtual void OnRemoveChild(RenderObject* removed_child, int position); - virtual void OnAddChild(RenderObject* new_child, int position); - virtual void OnRemoveChild(RenderObject* removed_child, int position); + virtual Size OnMeasureContent(const Size& available_size) = 0; + virtual void OnLayoutContent(const Rect& content_rect) = 0; -private: - void SetParent(RenderObject* new_parent); + private: + void SetParent(RenderObject* new_parent); -private: - IRenderHost* render_host_ = nullptr; - RenderObject* parent_ = nullptr; - std::vector children_; -}; + void OnMeasureCore(const Size& available_size); + void OnLayoutCore(const Rect& rect); + + private: + IRenderHost* render_host_ = nullptr; + RenderObject* parent_ = nullptr; + std::vector children_{}; -class LinearLayoutRenderObject : public RenderObject -{ -public: - LinearLayoutRenderObject() = default; - LinearLayoutRenderObject(const LinearLayoutRenderObject& other) = delete; - LinearLayoutRenderObject& operator=(const LinearLayoutRenderObject& other) = - delete; - LinearLayoutRenderObject(LinearLayoutRenderObject&& other) = delete; - LinearLayoutRenderObject& operator=(LinearLayoutRenderObject&& other) = - delete; - ~LinearLayoutRenderObject() = default; + Point offset_ = Point::Zero(); + Size size_ = Size::Zero(); - void Measure(const MeasureConstraint& constraint) override; + Thickness margin_ = Thickness::Zero(); + Thickness padding_ = Thickness::Zero(); -private: + Size preferred_size_ = Size::Zero(); }; } // namespace cru::ui::render diff --git a/src/ui/ui_base.cpp b/src/ui/ui_base.cpp deleted file mode 100644 index 2853011d..00000000 --- a/src/ui/ui_base.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "ui_base.hpp" - -namespace cru::ui -{ - -} diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp index 17cb9acb..ba6c8b9a 100644 --- a/src/ui/ui_base.hpp +++ b/src/ui/ui_base.hpp @@ -1,313 +1,238 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" #include +namespace cru::ui { +struct Point final { + constexpr static Point Zero() { return Point(0, 0); } + + constexpr Point() = default; + constexpr Point(const float x, const float y) : x(x), y(y) {} + + float x = 0; + float y = 0; +}; + +constexpr bool operator==(const Point& left, const Point& right) { + return left.x == right.x && left.y == right.y; +} -namespace cru::ui -{ - struct Point final - { - constexpr static Point Zero() - { - return Point(0, 0); - } - - constexpr Point() = default; - constexpr Point(const float x, const float y) : x(x), y(y) { } - - float x = 0; - float y = 0; - }; - - constexpr bool operator==(const Point& left, const Point& right) - { - return left.x == right.x && left.y == right.y; - } - - constexpr bool operator!=(const Point& left, const Point& right) - { - return !(left == right); - } - - - struct Size final - { - constexpr static Size Zero() - { - return Size(0, 0); - } - - constexpr Size() = default; - constexpr Size(const float width, const float height) : width(width), height(height) { } - - float width = 0; - float height = 0; - }; - - constexpr Size operator + (const Size& left, const Size& right) - { - return Size(left.width + right.width, left.height + right.height); - } - - constexpr Size operator - (const Size& left, const Size& right) - { - return Size(left.width - right.width, left.height - right.height); - } - - constexpr bool operator==(const Size& left, const Size& right) - { - return left.width == right.width && left.height == right.height; - } - - constexpr bool operator!=(const Size& left, const Size& right) - { - return !(left == right); - } - - - struct Thickness final - { - constexpr static Thickness Zero() - { - return Thickness(0); - } - - constexpr Thickness() : Thickness(0) { } - - constexpr explicit Thickness(const float width) - : left(width), top(width), right(width), bottom(width) { } - - constexpr explicit Thickness(const float horizontal, const float vertical) - : left(horizontal), top(vertical), right(horizontal), bottom(vertical) { } - - constexpr Thickness(const float left, const float top, const float right, const float bottom) - : left(left), top(top), right(right), bottom(bottom) { } - - float GetHorizontalTotal() const - { - return left + right; - } - - float GetVerticalTotal() const - { - return top + bottom; - } - - void SetLeftRight(const float value) - { - left = right = value; - } - - void SetTopBottom(const float value) - { - top = bottom = value; - } - - void SetAll(const float value) - { - left = top = right = bottom = value; - } - - float Validate() const - { - return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; - } - - float left; - float top; - float right; - float bottom; - }; - - constexpr bool operator==(const Thickness& left, const Thickness& right) - { - return left.left == right.left && - left.top == right.top && - left.right == right.right && - left.bottom == right.bottom; - } - - constexpr bool operator!=(const Thickness& left, const Thickness& right) - { - return !(left == right); - } - - - struct Rect final - { - constexpr Rect() = default; - constexpr Rect(const float left, const float top, const float width, const float height) - : left(left), top(top), width(width), height(height) { } - constexpr Rect(const Point& lefttop, const Size& size) - : left(lefttop.x), top(lefttop.y), width(size.width), height(size.height) { } - - constexpr static Rect FromVertices(const float left, const float top, const float right, const float bottom) - { - return Rect(left, top, right - left, bottom - top); - } - - constexpr static Rect FromCenter(const Point& center, const float width, const float height) - { - return Rect(center.x - width / 2.0f, center.y - height / 2.0f, width, height); - } - - constexpr float GetRight() const - { - return left + width; - } - - constexpr float GetBottom() const - { - return top + height; - } - - constexpr Point GetLeftTop() const - { - return Point(left, top); - } - - constexpr Point GetRightBottom() const - { - return Point(left + width, top + height); - } - - constexpr Point GetLeftBottom() const - { - return Point(left, top + height); - } - - constexpr Point GetRightTop() const - { - return Point(left + width, top); - } - - constexpr Point GetCenter() const - { - return Point(left + width / 2.0f, top + height / 2.0f); - } - - constexpr Size GetSize() const - { - return Size(width, height); - } - - constexpr Rect Shrink(const Thickness& thickness) const - { - return Rect(left + thickness.left, top + thickness.top, width - thickness.GetHorizontalTotal(), height - thickness.GetVerticalTotal()); - } - - constexpr bool IsPointInside(const Point& point) const - { - return - point.x >= left && - point.x < GetRight() && - point.y >= top && - point.y < GetBottom(); - } - - float left = 0.0f; - float top = 0.0f; - float width = 0.0f; - float height = 0.0f; - }; - - constexpr bool operator==(const Rect& left, const Rect& right) - { - return left.left == right.left && - left.top == right.top && - left.width == right.width && - left.height == right.height; - } - - constexpr bool operator!=(const Rect& left, const Rect& right) - { - return !(left == right); - } - - - struct RoundedRect final - { - constexpr RoundedRect() = default; - constexpr RoundedRect(const Rect& rect, const float radius_x, const float radius_y) - : rect(rect), radius_x(radius_x), radius_y(radius_y) { } - - Rect rect{}; - float radius_x = 0.0f; - float radius_y = 0.0f; - }; - - constexpr bool operator==(const RoundedRect& left, const RoundedRect& right) - { - return left.rect == right.rect && left.radius_x == right.radius_x && left.radius_y == right.radius_y; - } - - constexpr bool operator!=(const RoundedRect& left, const RoundedRect& right) - { - return !(left == right); - } - - - struct Ellipse final - { - constexpr Ellipse() = default; - constexpr Ellipse(const Point& center, const float radius_x, const float radius_y) - : center(center), radius_x(radius_x), radius_y(radius_y) { } - - constexpr static Ellipse FromRect(const Rect& rect) - { - return Ellipse(rect.GetCenter(), rect.width / 2.0f, rect.height / 2.0f); - } - - constexpr Rect GetBoundRect() const - { - return Rect::FromCenter(center, radius_x * 2.0f, radius_y * 2.0f); - } - - Point center{}; - float radius_x = 0.0f; - float radius_y = 0.0f; - }; - - constexpr bool operator==(const Ellipse& left, const Ellipse& right) - { - return left.center == right.center && left.radius_x == right.radius_x && left.radius_y == right.radius_y; - } - - constexpr bool operator!=(const Ellipse& left, const Ellipse& right) - { - return !(left == right); - } - - - struct TextRange final - { - constexpr static std::optional FromTwoSides(unsigned first, unsigned second) - { - if (first > second) - return std::make_optional(second, first - second); - if (first < second) - return std::make_optional(first, second - first); - return std::nullopt; - } - - constexpr static std::pair ToTwoSides(std::optional text_range, unsigned default_position = 0) - { - if (text_range.has_value()) - return std::make_pair(text_range.value().position, text_range.value().position + text_range.value().count); - return std::make_pair(default_position, default_position); - } - - constexpr TextRange() = default; - constexpr TextRange(const unsigned position, const unsigned count) - : position(position), count(count) - { - - } - - unsigned position = 0; - unsigned count = 0; - }; +constexpr bool operator!=(const Point& left, const Point& right) { + return !(left == right); } + +struct Size final { + constexpr static Size Zero() { return Size(0, 0); } + + constexpr Size() = default; + constexpr Size(const float width, const float height) + : width(width), height(height) {} + + float width = 0; + float height = 0; +}; + +constexpr Size operator+(const Size& left, const Size& right) { + return Size(left.width + right.width, left.height + right.height); +} + +constexpr Size operator-(const Size& left, const Size& right) { + return Size(left.width - right.width, left.height - right.height); +} + +constexpr bool operator==(const Size& left, const Size& right) { + return left.width == right.width && left.height == right.height; +} + +constexpr bool operator!=(const Size& left, const Size& right) { + return !(left == right); +} + +struct Thickness final { + constexpr static Thickness Zero() { return Thickness(0); } + + constexpr Thickness() : Thickness(0) {} + + constexpr explicit Thickness(const float width) + : left(width), top(width), right(width), bottom(width) {} + + constexpr explicit Thickness(const float horizontal, const float vertical) + : left(horizontal), top(vertical), right(horizontal), bottom(vertical) {} + + constexpr Thickness(const float left, const float top, const float right, + const float bottom) + : left(left), top(top), right(right), bottom(bottom) {} + + float GetHorizontalTotal() const { return left + right; } + + float GetVerticalTotal() const { return top + bottom; } + + void SetLeftRight(const float value) { left = right = value; } + + void SetTopBottom(const float value) { top = bottom = value; } + + void SetAll(const float value) { left = top = right = bottom = value; } + + float Validate() const { + return left >= 0.0 && top >= 0.0 && right >= 0.0 && bottom >= 0.0; + } + + float left; + float top; + float right; + float bottom; +}; + +constexpr bool operator==(const Thickness& left, const Thickness& right) { + return left.left == right.left && left.top == right.top && + left.right == right.right && left.bottom == right.bottom; +} + +constexpr bool operator!=(const Thickness& left, const Thickness& right) { + return !(left == right); +} + +struct Rect final { + constexpr Rect() = default; + constexpr Rect(const float left, const float top, const float width, + const float height) + : left(left), top(top), width(width), height(height) {} + constexpr Rect(const Point& lefttop, const Size& size) + : left(lefttop.x), + top(lefttop.y), + width(size.width), + height(size.height) {} + + constexpr static Rect FromVertices(const float left, const float top, + const float right, const float bottom) { + return Rect(left, top, right - left, bottom - top); + } + + constexpr static Rect FromCenter(const Point& center, const float width, + const float height) { + return Rect(center.x - width / 2.0f, center.y - height / 2.0f, width, + height); + } + + constexpr float GetRight() const { return left + width; } + + constexpr float GetBottom() const { return top + height; } + + constexpr Point GetLeftTop() const { return Point(left, top); } + + constexpr Point GetRightBottom() const { + return Point(left + width, top + height); + } + + constexpr Point GetLeftBottom() const { return Point(left, top + height); } + + constexpr Point GetRightTop() const { return Point(left + width, top); } + + constexpr Point GetCenter() const { + return Point(left + width / 2.0f, top + height / 2.0f); + } + + constexpr Size GetSize() const { return Size(width, height); } + + constexpr Rect Shrink(const Thickness& thickness) const { + return Rect(left + thickness.left, top + thickness.top, + width - thickness.GetHorizontalTotal(), + height - thickness.GetVerticalTotal()); + } + + constexpr bool IsPointInside(const Point& point) const { + return point.x >= left && point.x < GetRight() && point.y >= top && + point.y < GetBottom(); + } + + float left = 0.0f; + float top = 0.0f; + float width = 0.0f; + float height = 0.0f; +}; + +constexpr bool operator==(const Rect& left, const Rect& right) { + return left.left == right.left && left.top == right.top && + left.width == right.width && left.height == right.height; +} + +constexpr bool operator!=(const Rect& left, const Rect& right) { + return !(left == right); +} + +struct RoundedRect final { + constexpr RoundedRect() = default; + constexpr RoundedRect(const Rect& rect, const float radius_x, + const float radius_y) + : rect(rect), radius_x(radius_x), radius_y(radius_y) {} + + Rect rect{}; + float radius_x = 0.0f; + float radius_y = 0.0f; +}; + +constexpr bool operator==(const RoundedRect& left, const RoundedRect& right) { + return left.rect == right.rect && left.radius_x == right.radius_x && + left.radius_y == right.radius_y; +} + +constexpr bool operator!=(const RoundedRect& left, const RoundedRect& right) { + return !(left == right); +} + +struct Ellipse final { + constexpr Ellipse() = default; + constexpr Ellipse(const Point& center, const float radius_x, + const float radius_y) + : center(center), radius_x(radius_x), radius_y(radius_y) {} + + constexpr static Ellipse FromRect(const Rect& rect) { + return Ellipse(rect.GetCenter(), rect.width / 2.0f, rect.height / 2.0f); + } + + constexpr Rect GetBoundRect() const { + return Rect::FromCenter(center, radius_x * 2.0f, radius_y * 2.0f); + } + + Point center{}; + float radius_x = 0.0f; + float radius_y = 0.0f; +}; + +constexpr bool operator==(const Ellipse& left, const Ellipse& right) { + return left.center == right.center && left.radius_x == right.radius_x && + left.radius_y == right.radius_y; +} + +constexpr bool operator!=(const Ellipse& left, const Ellipse& right) { + return !(left == right); +} + +struct TextRange final { + constexpr static std::optional FromTwoSides(unsigned first, + unsigned second) { + if (first > second) + return std::make_optional(second, first - second); + if (first < second) + return std::make_optional(first, second - first); + return std::nullopt; + } + + constexpr static std::pair ToTwoSides( + std::optional text_range, unsigned default_position = 0) { + if (text_range.has_value()) + return std::make_pair( + text_range.value().position, + text_range.value().position + text_range.value().count); + return std::make_pair(default_position, default_position); + } + + constexpr TextRange() = default; + constexpr TextRange(const unsigned position, const unsigned count) + : position(position), count(count) {} + + unsigned position = 0; + unsigned count = 0; +}; +} // namespace cru::ui -- cgit v1.2.3 From 62080f78c900aa41e456aa37a1825310121de881 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 20 Mar 2019 17:31:22 +0800 Subject: ... --- CruUI.vcxproj | 4 - CruUI.vcxproj.filters | 12 - src/ui/border_property.cpp | 23 -- src/ui/border_property.hpp | 87 ------ src/ui/control.cpp | 402 ++++++++++----------------- src/ui/control.hpp | 564 +++++++++++++++----------------------- src/ui/controls/linear_layout.cpp | 10 +- src/ui/d2d_util.hpp | 2 - src/ui/layout_base.cpp | 6 - src/ui/layout_base.hpp | 102 ------- src/ui/render/render_object.cpp | 19 -- src/ui/render/render_object.hpp | 20 +- src/ui/window.hpp | 377 ++++++++++++------------- 13 files changed, 560 insertions(+), 1068 deletions(-) delete mode 100644 src/ui/border_property.cpp delete mode 100644 src/ui/border_property.hpp delete mode 100644 src/ui/layout_base.cpp delete mode 100644 src/ui/layout_base.hpp (limited to 'src/ui/render/render_object.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index bd321715..f4fada4d 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -127,7 +127,6 @@ - @@ -141,7 +140,6 @@ - @@ -151,7 +149,6 @@ - @@ -180,7 +177,6 @@ - diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 25923c76..1b7038d2 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -33,9 +33,6 @@ Source Files - - Source Files - Source Files @@ -60,9 +57,6 @@ Source Files - - Source Files - Source Files @@ -131,18 +125,12 @@ Header Files - - Header Files - Header Files Header Files - - Header Files - Header Files diff --git a/src/ui/border_property.cpp b/src/ui/border_property.cpp deleted file mode 100644 index b79bb482..00000000 --- a/src/ui/border_property.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "border_property.hpp" - -#include "ui_manager.hpp" - -namespace cru::ui -{ - BorderProperty::BorderProperty(): BorderProperty(UiManager::GetInstance()->GetPredefineResources()->border_property_brush) - { - - } - - BorderProperty::BorderProperty(Microsoft::WRL::ComPtr brush): brush_(std::move(brush)) - { - - } - - BorderProperty::BorderProperty(Microsoft::WRL::ComPtr brush, const float width, const float radius_x, - const float radius_y, Microsoft::WRL::ComPtr stroke_style) : - brush_(std::move(brush)), stroke_width_(width), radius_x_(radius_x), radius_y_(radius_y), stroke_style_(std::move(stroke_style)) - { - - } -} diff --git a/src/ui/border_property.hpp b/src/ui/border_property.hpp deleted file mode 100644 index 4dee0e0f..00000000 --- a/src/ui/border_property.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "system_headers.hpp" - -#include "base.hpp" - - -namespace cru::ui -{ - class BorderProperty final - { - public: - BorderProperty(); - explicit BorderProperty(Microsoft::WRL::ComPtr brush); - BorderProperty(Microsoft::WRL::ComPtr brush, float width, float radius_x, float radius_y, Microsoft::WRL::ComPtr stroke_style = nullptr); - BorderProperty(const BorderProperty& other) = default; - BorderProperty(BorderProperty&& other) = default; - BorderProperty& operator=(const BorderProperty& other) = default; - BorderProperty& operator=(BorderProperty&& other) = default; - ~BorderProperty() = default; - - - Microsoft::WRL::ComPtr GetBrush() const - { - return brush_; - } - - float GetStrokeWidth() const - { - return stroke_width_; - } - - Microsoft::WRL::ComPtr GetStrokeStyle() const - { - return stroke_style_; - } - - float GetRadiusX() const - { - return radius_x_; - } - - float GetRadiusY() const - { - return radius_y_; - } - - void SetBrush(Microsoft::WRL::ComPtr brush) - { - Require(brush == nullptr, "Brush of BorderProperty mustn't be null."); - brush_ = std::move(brush); - } - - void SetStrokeWidth(const float stroke_width) - { - Require(stroke_width >= 0.0f, "Stroke width must be no less than 0."); - stroke_width_ = stroke_width; - } - - void SetStrokeStyle(Microsoft::WRL::ComPtr stroke_style) - { - stroke_style_ = std::move(stroke_style); - } - - void SetRadiusX(const float radius_x) - { - Require(radius_x >= 0.0f, "Radius-x must be no less than 0."); - radius_x_ = radius_x; - } - - void SetRadiusY(const float radius_y) - { - Require(radius_y >= 0.0f, "Radius-y must be no less than 0."); - radius_y_ = radius_y; - } - - private: - Microsoft::WRL::ComPtr brush_; - float stroke_width_ = 1.0f; - float radius_x_ = 0.0f; - float radius_y_ = 0.0f; - Microsoft::WRL::ComPtr stroke_style_ = nullptr; - }; -} diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 617c50c7..ee2abad0 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -3,292 +3,192 @@ #include #include -#include "window.hpp" #include "application.hpp" -#include "graph/graph.hpp" -#include "exception.hpp" #include "cru_debug.hpp" #include "d2d_util.hpp" +#include "exception.hpp" +#include "graph/graph.hpp" #include "math_util.hpp" +#include "window.hpp" #ifdef CRU_DEBUG_LAYOUT #include "ui_manager.hpp" #endif -namespace cru::ui -{ - - void Control::SetParent(Control* parent) - { - const auto old_parent = GetParent(); - parent_ = parent; - const auto new_parent = GetParent(); - if (old_parent != new_parent) - OnParentChanged(old_parent, new_parent); - } - - void Control::SetDescendantWindow(Window* window) - { - if (window == nullptr && window_ == nullptr) - return; - - //You can only attach or detach window. - assert((window != nullptr && window_ == nullptr) || (window == nullptr && window_ != nullptr)); - - if (window == nullptr) - { - const auto old = window_; - TraverseDescendants([old](Control* control) - { - control->window_ = nullptr; - control->OnDetachToWindow(old); - }); - } - else - TraverseDescendants([window](Control* control) - { - control->window_ = window; - control->OnAttachToWindow(window); - }); - } - - void Control::TraverseDescendants(const std::function& predicate) - { - TraverseDescendantsInternal(this, predicate); - } - - void Control::TraverseDescendantsInternal(Control * control, const std::function& predicate) - { - predicate(control); - for (auto c: control->GetInternalChildren()) - TraverseDescendantsInternal(c, predicate); - } - - - Point Control::ControlToWindow(const Point& point) const - { - const auto position = GetPositionInWindow(); - return Point(point.x + position.x, point.y + position.y); - } - - Point Control::WindowToControl(const Point & point) const - { - const auto position = GetPositionInWindow(); - return Point(point.x - position.x, point.y - position.y); - } - - bool Control::RequestFocus() - { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->RequestFocusFor(this); - } - - bool Control::HasFocus() - { - auto window = GetWindow(); - if (window == nullptr) - return false; - - return window->GetFocusControl() == this; - } - - void Control::SetCursor(const Cursor::Ptr& cursor) - { - if (cursor != cursor_) - { - cursor_ = cursor; - const auto window = GetWindow(); - if (window && window->GetMouseHoverControl() == this) - window->UpdateCursor(); - } - } - - void Control::OnParentChanged(Control* old_parent, Control* new_parent) - { - - } - - void Control::OnAttachToWindow(Window* window) - { - - } - - void Control::OnDetachToWindow(Window * window) - { - - } - - void Control::OnMouseClickBegin(MouseButton button) - { - - } - - void Control::OnMouseClickEnd(MouseButton button) - { - - } - - - const std::vector NoChildControl::empty_control_vector{}; +namespace cru::ui { +void Control::_SetParent(Control* parent) { + const auto old_parent = GetParent(); + parent_ = parent; + const auto new_parent = GetParent(); + if (old_parent != new_parent) OnParentChanged(old_parent, new_parent); +} - ContentControl::ContentControl() : child_vector_{nullptr}, child_(child_vector_[0]) - { +void Control::_SetDescendantWindow(Window* window) { + if (window == nullptr && window_ == nullptr) return; + + // You can only attach or detach window. + assert((window != nullptr && window_ == nullptr) || + (window == nullptr && window_ != nullptr)); + + if (window == nullptr) { + const auto old = window_; + TraverseDescendants([old](Control* control) { + control->window_ = nullptr; + control->OnDetachToWindow(old); + }); + } else + TraverseDescendants([window](Control* control) { + control->window_ = window; + control->OnAttachToWindow(window); + }); +} - } +void Control::TraverseDescendants( + const std::function& predicate) { + TraverseDescendantsInternal(this, predicate); +} - ContentControl::~ContentControl() - { - delete child_; - } +void Control::TraverseDescendantsInternal( + Control* control, const std::function& predicate) { + predicate(control); + for (auto c : control->GetChildren()) + TraverseDescendantsInternal(c, predicate); +} +bool Control::RequestFocus() { + auto window = GetWindow(); + if (window == nullptr) return false; - void ContentControl::SetChild(Control* child) - { - if (child == child_) - return; + return window->RequestFocusFor(this); +} - const auto window = GetWindow(); - const auto old_child = child_; - child_ = child; - if (old_child) - { - old_child->SetParent(nullptr); - old_child->SetDescendantWindow(nullptr); - } - if (child) - { - child->SetParent(this); - child->SetDescendantWindow(window); - } - OnChildChanged(old_child, child); - } +bool Control::HasFocus() { + auto window = GetWindow(); + if (window == nullptr) return false; - void ContentControl::OnChildChanged(Control* old_child, Control* new_child) - { + return window->GetFocusControl() == this; +} - } +void Control::SetCursor(const Cursor::Ptr& cursor) { + if (cursor != cursor_) { + cursor_ = cursor; + const auto window = GetWindow(); + if (window && window->GetMouseHoverControl() == this) + window->UpdateCursor(); + } +} - void ControlAddChildCheck(Control* control) - { - if (control->GetParent() != nullptr) - throw std::invalid_argument("The control already has a parent."); +void Control::OnParentChanged(Control* old_parent, Control* new_parent) {} - if (dynamic_cast(control)) - throw std::invalid_argument("Can't add a window as child."); - } +void Control::OnAttachToWindow(Window* window) {} - MultiChildControl::~MultiChildControl() - { - for (const auto child : children_) - delete child; - } +void Control::OnDetachToWindow(Window* window) {} - void MultiChildControl::AddChild(Control* control) - { - ControlAddChildCheck(control); +void Control::OnMouseClickBegin(MouseButton button) {} - children_.push_back(control); +void Control::OnMouseClickEnd(MouseButton button) {} - control->SetParent(this); - control->SetDescendantWindow(GetWindow()); +const std::vector NoChildControl::empty_control_vector{}; - OnAddChild(control); - } +ContentControl::ContentControl() + : child_vector_{nullptr}, child_(child_vector_[0]) {} - void MultiChildControl::AddChild(Control* control, const int position) - { - ControlAddChildCheck(control); +ContentControl::~ContentControl() { delete child_; } - if (position < 0 || static_cast(position) > this->children_.size()) - throw std::invalid_argument("The position is out of range."); +void ContentControl::SetChild(Control* child) { + if (child == child_) return; - children_.insert(this->children_.cbegin() + position, control); + const auto window = GetWindow(); + const auto old_child = child_; + child_ = child; + if (old_child) { + old_child->_SetParent(nullptr); + old_child->_SetDescendantWindow(nullptr); + } + if (child) { + child->_SetParent(this); + child->_SetDescendantWindow(window); + } + OnChildChanged(old_child, child); +} - control->SetParent(this); - control->SetDescendantWindow(GetWindow()); +void ContentControl::OnChildChanged(Control* old_child, Control* new_child) {} - OnAddChild(control); - } +void ControlAddChildCheck(Control* control) { + if (control->GetParent() != nullptr) + throw std::invalid_argument("The control already has a parent."); - void MultiChildControl::RemoveChild(Control* child) - { - const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child); - if (i == this->children_.cend()) - throw std::invalid_argument("The argument child is not a child of this control."); + if (dynamic_cast(control)) + throw std::invalid_argument("Can't add a window as child."); +} + +MultiChildControl::~MultiChildControl() { + for (const auto child : children_) delete child; +} + +void MultiChildControl::AddChild(Control* control, const int position) { + ControlAddChildCheck(control); - children_.erase(i); + if (position < 0 || static_cast(position) > + this->children_.size()) + throw std::invalid_argument("The position is out of range."); - child->SetParent(nullptr); - child->SetDescendantWindow(nullptr); + children_.insert(this->children_.cbegin() + position, control); + + control->_SetParent(this); + control->_SetDescendantWindow(GetWindow()); + + OnAddChild(control); +} - OnRemoveChild(child); - } +void MultiChildControl::RemoveChild(const int position) { + if (position < 0 || static_castchildren_.size())>(position) >= + this->children_.size()) + throw std::invalid_argument("The position is out of range."); - void MultiChildControl::RemoveChild(const int position) - { - if (position < 0 || static_castchildren_.size())>(position) >= this->children_.size()) - throw std::invalid_argument("The position is out of range."); - - const auto i = children_.cbegin() + position; - const auto child = *i; - - children_.erase(i); - - child->SetParent(nullptr); - child->SetDescendantWindow(nullptr); - - OnRemoveChild(child); - } - - void MultiChildControl::OnAddChild(Control* child) - { - - } - - void MultiChildControl::OnRemoveChild(Control* child) - { - - } - - std::list GetAncestorList(Control* control) - { - std::list l; - while (control != nullptr) - { - l.push_front(control); - control = control->GetParent(); - } - return l; - } + const auto i = children_.cbegin() + position; + const auto child = *i; - Control* FindLowestCommonAncestor(Control * left, Control * right) - { - if (left == nullptr || right == nullptr) - return nullptr; + children_.erase(i); - auto&& left_list = GetAncestorList(left); - auto&& right_list = GetAncestorList(right); - - // the root is different - if (left_list.front() != right_list.front()) - return nullptr; + child->_SetParent(nullptr); + child->_SetDescendantWindow(nullptr); + + OnRemoveChild(child); +} + +void MultiChildControl::OnAddChild(Control* child) {} + +void MultiChildControl::OnRemoveChild(Control* child) {} + +std::list GetAncestorList(Control* control) { + std::list l; + while (control != nullptr) { + l.push_front(control); + control = control->GetParent(); + } + return l; +} - // find the last same control or the last control (one is ancestor of the other) - auto left_i = left_list.cbegin(); - auto right_i = right_list.cbegin(); - while (true) - { - if (left_i == left_list.cend()) - return *(--left_i); - if (right_i == right_list.cend()) - return *(--right_i); - if (*left_i != *right_i) - return *(--left_i); - ++left_i; - ++right_i; - } - } +Control* FindLowestCommonAncestor(Control* left, Control* right) { + if (left == nullptr || right == nullptr) return nullptr; + + auto&& left_list = GetAncestorList(left); + auto&& right_list = GetAncestorList(right); + + // the root is different + if (left_list.front() != right_list.front()) return nullptr; + + // find the last same control or the last control (one is ancestor of the + // other) + auto left_i = left_list.cbegin(); + auto right_i = right_list.cbegin(); + while (true) { + if (left_i == left_list.cend()) return *(--left_i); + if (right_i == right_list.cend()) return *(--right_i); + if (*left_i != *right_i) return *(--left_i); + ++left_i; + ++right_i; + } } +} // namespace cru::ui diff --git a/src/ui/control.hpp b/src/ui/control.hpp index 8e69fb07..e85d0e6d 100644 --- a/src/ui/control.hpp +++ b/src/ui/control.hpp @@ -1,357 +1,243 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" -#include #include +#include #include +#include "system_headers.hpp" #include "base.hpp" -#include "ui_base.hpp" -#include "layout_base.hpp" -#include "events/ui_event.hpp" #include "cursor.hpp" -#include "any_map.hpp" +#include "events/ui_event.hpp" #include "input_util.hpp" +#include "ui_base.hpp" -namespace cru::ui -{ - class Window; - - - class Control : public Object - { - friend class Window; - - protected: - Control() = default; - public: - Control(const Control& other) = delete; - Control(Control&& other) = delete; - Control& operator=(const Control& other) = delete; - Control& operator=(Control&& other) = delete; - ~Control() override = default; - - public: - virtual StringView GetControlType() const = 0; - - - //*************** region: tree *************** - public: - //Get the window if attached, otherwise, return nullptr. - Window* GetWindow() const - { - return window_; - } - - Control* GetParent() const - { - return parent_; - } - - void SetParent(Control* parent); - - void SetDescendantWindow(Window* window); - - //Traverse the tree rooted the control including itself. - void TraverseDescendants(const std::function& predicate); - - private: - static void TraverseDescendantsInternal(Control* control, const std::function& predicate); - - //*************** region: position *************** - public: - virtual Point GetPositionInWindow() const = 0; - - //Local point to absolute point. - Point ControlToWindow(const Point& point) const; - - //Absolute point to local point. - Point WindowToControl(const Point& point) const; - - - - //*************** region: focus *************** - public: - bool RequestFocus(); - - bool HasFocus(); - - - //*************** region: cursor *************** - // If cursor is set to null, then it uses parent's cursor. - // Window's cursor can't be null. - public: - Cursor::Ptr GetCursor() const - { - return cursor_; - } - - void SetCursor(const Cursor::Ptr& cursor); - - - //*************** region: additional properties *************** - public: - AnyMap* GetAdditionalPropertyMap() - { - return &additional_property_map_; - } - - - //*************** region: events *************** - public: - //Raised when mouse enter the control. - events::RoutedEvent mouse_enter_event; - //Raised when mouse is leave the control. - events::RoutedEvent mouse_leave_event; - //Raised when mouse is move in the control. - events::RoutedEvent mouse_move_event; - //Raised when a mouse button is pressed in the control. - events::RoutedEvent mouse_down_event; - //Raised when a mouse button is released in the control. - events::RoutedEvent mouse_up_event; - //Raised when a mouse button is pressed in the control and released in the control with mouse not leaving it between two operations. - events::RoutedEvent mouse_click_event; - - events::RoutedEvent mouse_wheel_event; - - events::RoutedEvent key_down_event; - events::RoutedEvent key_up_event; - events::RoutedEvent char_event; - - events::RoutedEvent get_focus_event; - events::RoutedEvent lose_focus_event; - - - //*************** region: tree *************** - protected: - virtual const std::vector& GetInternalChildren() const = 0; - - virtual void OnParentChanged(Control* old_parent, Control* new_parent); - virtual void OnAttachToWindow(Window* window); - virtual void OnDetachToWindow(Window* window); - - - //*************** region: additional mouse event *************** - protected: - virtual void OnMouseClickBegin(MouseButton button); - virtual void OnMouseClickEnd(MouseButton button); - - - private: - Window * window_ = nullptr; - Control* parent_ = nullptr; - - Cursor::Ptr cursor_{}; - - AnyMap additional_property_map_{}; - }; +namespace cru::ui { +class Window; +class Control : public Object { + friend class Window; - - class NoChildControl : public Control - { - private: - // used in GetInternalChildren. - static const std::vector empty_control_vector; + protected: + Control() = default; - protected: - NoChildControl() = default; - public: - NoChildControl(const NoChildControl& other) = delete; - NoChildControl(NoChildControl&& other) = delete; - NoChildControl& operator=(const NoChildControl& other) = delete; - NoChildControl& operator=(NoChildControl&& other) = delete; - ~NoChildControl() override = default; - - protected: - const std::vector& GetInternalChildren() const override final - { - return empty_control_vector; - } - }; - - - class ContentControl : public Control - { - protected: - ContentControl(); - public: - ContentControl(const ContentControl& other) = delete; - ContentControl(ContentControl&& other) = delete; - ContentControl& operator=(const ContentControl& other) = delete; - ContentControl& operator=(ContentControl&& other) = delete; - ~ContentControl() override; - - Control* GetChild() const - { - return child_; - } - - void SetChild(Control* child); - - protected: - const std::vector& GetInternalChildren() const override final - { - return child_vector_; - } - - // Override should call base. - virtual void OnChildChanged(Control* old_child, Control* new_child); - - private: - std::vector child_vector_; - Control*& child_; - }; - - - class MultiChildControl : public Control - { - protected: - MultiChildControl() = default; - public: - MultiChildControl(const MultiChildControl& other) = delete; - MultiChildControl(MultiChildControl&& other) = delete; - MultiChildControl& operator=(const MultiChildControl& other) = delete; - MultiChildControl& operator=(MultiChildControl&& other) = delete; - ~MultiChildControl() override; - - const std::vector& GetInternalChildren() const override final - { - return children_; - } - - const std::vector& GetChildren() const - { - return children_; - } - - //Add a child at tail. - void AddChild(Control* control); - - //Add a child before the position. - void AddChild(Control* control, int position); - - //Remove a child. - void RemoveChild(Control* child); - - //Remove a child at specified position. - void RemoveChild(int position); - - protected: - virtual void OnAddChild(Control* child); - virtual void OnRemoveChild(Control* child); - - private: - std::vector children_; - }; - - - - //*************** region: event dispatcher helper *************** - - // Dispatch the event. - // - // This will raise routed event of the control and its parent and parent's - // parent ... (until "last_receiver" if it's not nullptr) with appropriate args. - // - // First tunnel from top to bottom possibly stopped by "handled" flag in EventArgs. - // Second bubble from bottom to top possibly stopped by "handled" flag in EventArgs. - // Last direct to each control. - // - // Args is of type "EventArgs". The first init argument is "sender", which is - // automatically bound to each receiving control. The second init argument is - // "original_sender", which is unchanged. And "args" will be perfectly forwarded - // as the rest arguments. - template - void DispatchEvent(Control* const original_sender, events::RoutedEvent Control::* event_ptr, Control* const last_receiver, Args&&... args) - { - std::list receive_list; - - auto parent = original_sender; - while (parent != last_receiver) - { - receive_list.push_back(parent); - parent = parent->GetInternalParent(); - } - - auto handled = false; - - //tunnel - for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) - { - EventArgs event_args(*i, original_sender, std::forward(args)...); - (*i->*event_ptr).tunnel.Raise(event_args); - if (event_args.IsHandled()) - { - handled = true; - break; - } - } - - //bubble - if (!handled) - { - for (auto i : receive_list) - { - EventArgs event_args(i, original_sender, std::forward(args)...); - (i->*event_ptr).bubble.Raise(event_args); - if (event_args.IsHandled()) - break; - } - } - - //direct - for (auto i : receive_list) - { - EventArgs event_args(i, original_sender, std::forward(args)...); - (i->*event_ptr).direct.Raise(event_args); - } + public: + Control(const Control& other) = delete; + Control(Control&& other) = delete; + Control& operator=(const Control& other) = delete; + Control& operator=(Control&& other) = delete; + ~Control() override = default; + + public: + virtual StringView GetControlType() const = 0; + + //*************** region: tree *************** + public: + // Get the window if attached, otherwise, return nullptr. + Window* GetWindow() const { return window_; } + + Control* GetParent() const { return parent_; } + + virtual const std::vector& GetChildren() const = 0; + + // Traverse the tree rooted the control including itself. + void TraverseDescendants(const std::function& predicate); + + void _SetParent(Control* parent); + void _SetDescendantWindow(Window* window); + + private: + static void TraverseDescendantsInternal( + Control* control, const std::function& predicate); + + //*************** region: focus *************** + public: + bool RequestFocus(); + + bool HasFocus(); + + //*************** region: cursor *************** + // If cursor is set to null, then it uses parent's cursor. + // Window's cursor can't be null. + public: + Cursor::Ptr GetCursor() const { return cursor_; } + + void SetCursor(const Cursor::Ptr& cursor); + + //*************** region: events *************** + public: + // Raised when mouse enter the control. + events::RoutedEvent mouse_enter_event; + // Raised when mouse is leave the control. + events::RoutedEvent mouse_leave_event; + // Raised when mouse is move in the control. + events::RoutedEvent mouse_move_event; + // Raised when a mouse button is pressed in the control. + events::RoutedEvent mouse_down_event; + // Raised when a mouse button is released in the control. + events::RoutedEvent mouse_up_event; + // Raised when a mouse button is pressed in the control and released in the + // control with mouse not leaving it between two operations. + events::RoutedEvent mouse_click_event; + + events::RoutedEvent mouse_wheel_event; + + events::RoutedEvent key_down_event; + events::RoutedEvent key_up_event; + events::RoutedEvent char_event; + + events::RoutedEvent get_focus_event; + events::RoutedEvent lose_focus_event; + + //*************** region: tree *************** + protected: + virtual void OnParentChanged(Control* old_parent, Control* new_parent); + virtual void OnAttachToWindow(Window* window); + virtual void OnDetachToWindow(Window* window); + + //*************** region: additional mouse event *************** + protected: + virtual void OnMouseClickBegin(MouseButton button); + virtual void OnMouseClickEnd(MouseButton button); + + private: + Window* window_ = nullptr; + Control* parent_ = nullptr; + + Cursor::Ptr cursor_{}; +}; + +class NoChildControl : public Control { + private: + static const std::vector empty_control_vector; + + protected: + NoChildControl() = default; + + public: + NoChildControl(const NoChildControl& other) = delete; + NoChildControl(NoChildControl&& other) = delete; + NoChildControl& operator=(const NoChildControl& other) = delete; + NoChildControl& operator=(NoChildControl&& other) = delete; + ~NoChildControl() override = default; + + protected: + const std::vector& GetChildren() const override final { + return empty_control_vector; + } +}; + +class ContentControl : public Control { + protected: + ContentControl(); + + public: + ContentControl(const ContentControl& other) = delete; + ContentControl(ContentControl&& other) = delete; + ContentControl& operator=(const ContentControl& other) = delete; + ContentControl& operator=(ContentControl&& other) = delete; + ~ContentControl() override; + + const std::vector& GetChildren() const override final { + return child_vector_; + } + Control* GetChild() const { return child_; } + void SetChild(Control* child); + + protected: + virtual void OnChildChanged(Control* old_child, Control* new_child); + + private: + std::vector child_vector_; + Control*& child_; +}; + +class MultiChildControl : public Control { + protected: + MultiChildControl() = default; + + public: + MultiChildControl(const MultiChildControl& other) = delete; + MultiChildControl(MultiChildControl&& other) = delete; + MultiChildControl& operator=(const MultiChildControl& other) = delete; + MultiChildControl& operator=(MultiChildControl&& other) = delete; + ~MultiChildControl() override; + + const std::vector& GetChildren() const override final { + return children_; + } + + void AddChild(Control* control, int position); + + void RemoveChild(int position); + + protected: + virtual void OnAddChild(Control* child); + virtual void OnRemoveChild(Control* child); + + private: + std::vector children_; +}; + +//*************** region: event dispatcher helper *************** + +// Dispatch the event. +// +// This will raise routed event of the control and its parent and parent's +// parent ... (until "last_receiver" if it's not nullptr) with appropriate args. +// +// First tunnel from top to bottom possibly stopped by "handled" flag in +// EventArgs. Second bubble from bottom to top possibly stopped by "handled" +// flag in EventArgs. Last direct to each control. +// +// Args is of type "EventArgs". The first init argument is "sender", which is +// automatically bound to each receiving control. The second init argument is +// "original_sender", which is unchanged. And "args" will be perfectly forwarded +// as the rest arguments. +template +void DispatchEvent(Control* const original_sender, + events::RoutedEvent Control::*event_ptr, + Control* const last_receiver, Args&&... args) { + std::list receive_list; + + auto parent = original_sender; + while (parent != last_receiver) { + receive_list.push_back(parent); + parent = parent->GetInternalParent(); + } + + auto handled = false; + + // tunnel + for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) { + EventArgs event_args(*i, original_sender, std::forward(args)...); + (*i->*event_ptr).tunnel.Raise(event_args); + if (event_args.IsHandled()) { + handled = true; + break; } - - - //*************** region: tree helper *************** - - // Find the lowest common ancestor. - // Return nullptr if "left" and "right" are not in the same tree. - Control* FindLowestCommonAncestor(Control* left, Control* right); - - - //*************** region: create helper *************** - - template - TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, Args&&... args) - { - static_assert(std::is_base_of_v, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward(args)...); - control->GetLayoutParams()->width = width; - control->GetLayoutParams()->height = height; - return control; + } + + // bubble + if (!handled) { + for (auto i : receive_list) { + EventArgs event_args(i, original_sender, std::forward(args)...); + (i->*event_ptr).bubble.Raise(event_args); + if (event_args.IsHandled()) break; } + } - template - TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args) - { - static_assert(std::is_base_of_v, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward(args)...); - control->GetLayoutParams()->padding = padding; - control->GetLayoutParams()->margin = margin; - return control; - } + // direct + for (auto i : receive_list) { + EventArgs event_args(i, original_sender, std::forward(args)...); + (i->*event_ptr).direct.Raise(event_args); + } +} - template - TControl* CreateWithLayout(const LayoutSideParams& width, const LayoutSideParams& height, const Thickness& padding, const Thickness& margin, Args&&... args) - { - static_assert(std::is_base_of_v, "TControl is not a control class."); - TControl* control = TControl::Create(std::forward(args)...); - control->GetLayoutParams()->width = width; - control->GetLayoutParams()->height = height; - control->GetLayoutParams()->padding = padding; - control->GetLayoutParams()->margin = margin; - return control; - } +//*************** region: tree helper *************** - using ControlList = std::initializer_list; -} +// Find the lowest common ancestor. +// Return nullptr if "left" and "right" are not in the same tree. +Control* FindLowestCommonAncestor(Control* left, Control* right); + +} // namespace cru::ui diff --git a/src/ui/controls/linear_layout.cpp b/src/ui/controls/linear_layout.cpp index d3fdc9b5..c3de7ca3 100644 --- a/src/ui/controls/linear_layout.cpp +++ b/src/ui/controls/linear_layout.cpp @@ -27,7 +27,7 @@ namespace cru::ui::controls // First measure Content and Exactly and count Stretch. if (orientation_ == Orientation::Horizontal) - for(auto control: GetInternalChildren()) + for(auto control: GetChildren()) { const auto mode = control->GetLayoutParams()->width.mode; if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) @@ -42,7 +42,7 @@ namespace cru::ui::controls stretch_control_list.push_back(control); } else - for(auto control: GetInternalChildren()) + for(auto control: GetChildren()) { const auto mode = control->GetLayoutParams()->height.mode; if (mode == MeasureMode::Content || mode == MeasureMode::Exactly) @@ -82,7 +82,7 @@ namespace cru::ui::controls if (orientation_ == Orientation::Horizontal) { - for (auto control : GetInternalChildren()) + for (auto control : GetChildren()) { if (control->GetLayoutParams()->height.mode == MeasureMode::Stretch) { @@ -93,7 +93,7 @@ namespace cru::ui::controls } else { - for (auto control : GetInternalChildren()) + for (auto control : GetChildren()) { if (control->GetLayoutParams()->width.mode == MeasureMode::Stretch) { @@ -110,7 +110,7 @@ namespace cru::ui::controls void LinearLayout::OnLayoutContent(const Rect& rect, const AdditionalLayoutInfo& additional_info) { float current_main_side_anchor = 0; - for(auto control: GetInternalChildren()) + for(auto control: GetChildren()) { const auto layout_params = control->GetLayoutParams(); const auto size = control->GetDesiredSize(); diff --git a/src/ui/d2d_util.hpp b/src/ui/d2d_util.hpp index 5f2e10b2..94ef6223 100644 --- a/src/ui/d2d_util.hpp +++ b/src/ui/d2d_util.hpp @@ -1,6 +1,4 @@ #pragma once - -// ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" #include "system_headers.hpp" diff --git a/src/ui/layout_base.cpp b/src/ui/layout_base.cpp deleted file mode 100644 index 5898a623..00000000 --- a/src/ui/layout_base.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "layout_base.hpp" - -namespace cru::ui -{ - -} diff --git a/src/ui/layout_base.hpp b/src/ui/layout_base.hpp deleted file mode 100644 index 527d9f98..00000000 --- a/src/ui/layout_base.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -// ReSharper disable once CppUnusedIncludeDirective -#include "pre.hpp" - -#include "ui_base.hpp" - -namespace cru::ui -{ - enum class Alignment - { - Center, - Start, - End - }; - - enum class MeasureMode - { - Exactly, - Content, - Stretch - }; - - enum class RectRange - { - Content, // content excluding padding, border and margin - Padding, // only including content and padding - HalfBorder, // including content, padding and half border - FullBorder, // including content, padding and full border - Margin // including content, padding, border and margin - }; - - struct LayoutSideParams final - { - constexpr static LayoutSideParams Exactly(const float length, const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Exactly, length, alignment); - } - - constexpr static LayoutSideParams Content(const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Content, 0, alignment); - } - - constexpr static LayoutSideParams Stretch(const Alignment alignment = Alignment::Center) - { - return LayoutSideParams(MeasureMode::Stretch, 0, alignment); - } - - constexpr LayoutSideParams() = default; - - constexpr explicit LayoutSideParams(const MeasureMode mode, const float length, const Alignment alignment) - : length(length), mode(mode), alignment(alignment) - { - - } - - constexpr bool Validate() const - { - if (length < 0.0) - return false; - if (min.has_value() && min.value() < 0.0) - return false; - if (max.has_value() && max.value() < 0.0) - return false; - if (min.has_value() && max.has_value() && min.value() > max.value()) - return false; - return true; - } - - // only used in exactly mode, specify the exactly side length of content. - float length = 0.0; - MeasureMode mode = MeasureMode::Content; - Alignment alignment = Alignment::Center; - - // min and max specify the min/max side length of content. - // they are used as hint and respect the actual size that content needs. - // when mode is exactly, length is coerced into the min-max range. - std::optional min = std::nullopt; - std::optional max = std::nullopt; - }; - - struct BasicLayoutParams final - { - BasicLayoutParams() = default; - BasicLayoutParams(const BasicLayoutParams&) = default; - BasicLayoutParams(BasicLayoutParams&&) = default; - BasicLayoutParams& operator = (const BasicLayoutParams&) = default; - BasicLayoutParams& operator = (BasicLayoutParams&&) = default; - ~BasicLayoutParams() = default; - - bool Validate() const - { - return width.Validate() && height.Validate() && margin.Validate() && padding.Validate(); - } - - LayoutSideParams width; - LayoutSideParams height; - Thickness padding; - Thickness margin; - }; -} diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 0035d1be..0a0e693c 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -3,14 +3,6 @@ #include "cru_debug.hpp" namespace cru::ui::render { -void RenderObject::SetRenderHost(IRenderHost* new_render_host) { - if (new_render_host == render_host_) return; - - const auto old = render_host_; - render_host_ = new_render_host; - OnRenderHostChanged(old, new_render_host); -} - void RenderObject::AddChild(RenderObject* render_object, const int position) { if (render_object->GetParent() != nullptr) throw std::invalid_argument("Render object already has a parent."); @@ -52,17 +44,6 @@ void RenderObject::Layout(const Rect& rect) { OnLayoutCore(Rect{Point::Zero(), rect.GetSize()}); } -void RenderObject::OnRenderHostChanged(IRenderHost* old_render_host, - IRenderHost* new_render_host) {} - -void RenderObject::InvalidateRenderHostPaint() const { - if (render_host_ != nullptr) render_host_->InvalidatePaint(); -} - -void RenderObject::InvalidateRenderHostLayout() const { - if (render_host_ != nullptr) render_host_->InvalidateLayout(); -} - void RenderObject::OnParentChanged(RenderObject* old_parent, RenderObject* new_parent) {} diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index aeba1457..34f5dcee 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -8,11 +8,11 @@ #include "base.hpp" #include "ui/ui_base.hpp" +namespace cru::ui { +class Control; +} + namespace cru::ui::render { -struct IRenderHost : Interface { - virtual void InvalidatePaint() = 0; - virtual void InvalidateLayout() = 0; -}; class RenderObject : public Object { protected: @@ -25,8 +25,8 @@ class RenderObject : public Object { RenderObject& operator=(RenderObject&& other) = delete; ~RenderObject() override = default; - IRenderHost* GetRenderHost() const { return render_host_; } - void SetRenderHost(IRenderHost* new_render_host); + Control* GetAttachedControl() const { return control_; } + void SetAttachedControl(Control* new_control) { control_ = new_control; } RenderObject* GetParent() const { return parent_; } @@ -58,12 +58,6 @@ class RenderObject : public Object { virtual RenderObject* HitTest(const Point& point) = 0; protected: - virtual void OnRenderHostChanged(IRenderHost* old_render_host, - IRenderHost* new_render_host); - - void InvalidateRenderHostPaint() const; - void InvalidateRenderHostLayout() const; - virtual void OnParentChanged(RenderObject* old_parent, RenderObject* new_parent); @@ -80,7 +74,7 @@ class RenderObject : public Object { void OnLayoutCore(const Rect& rect); private: - IRenderHost* render_host_ = nullptr; + Control* control_ = nullptr; RenderObject* parent_ = nullptr; std::vector children_{}; diff --git a/src/ui/window.hpp b/src/ui/window.hpp index d3374684..ac26de22 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -3,276 +3,243 @@ // ReSharper disable once CppUnusedIncludeDirective #include "pre.hpp" -#include "system_headers.hpp" #include #include +#include "system_headers.hpp" #include "control.hpp" #include "events/ui_event.hpp" -namespace cru::graph -{ - class WindowRenderTarget; +namespace cru::graph { +class WindowRenderTarget; } -namespace cru::ui -{ - class WindowManager : public Object - { - public: - static WindowManager* GetInstance(); - private: - WindowManager(); - public: - WindowManager(const WindowManager& other) = delete; - WindowManager(WindowManager&& other) = delete; - WindowManager& operator=(const WindowManager& other) = delete; - WindowManager& operator=(WindowManager&& other) = delete; - ~WindowManager() override = default; +namespace cru::ui { +class WindowManager : public Object { + public: + static WindowManager* GetInstance(); + private: + WindowManager(); - //Get the general window class for creating ordinary window. - WindowClass* GetGeneralWindowClass() const - { - return general_window_class_.get(); - } + public: + WindowManager(const WindowManager& other) = delete; + WindowManager(WindowManager&& other) = delete; + WindowManager& operator=(const WindowManager& other) = delete; + WindowManager& operator=(WindowManager&& other) = delete; + ~WindowManager() override = default; - //Register a window newly created. - //This function adds the hwnd to hwnd-window map. - //It should be called immediately after a window was created. - void RegisterWindow(HWND hwnd, Window* window); + // Get the general window class for creating ordinary window. + WindowClass* GetGeneralWindowClass() const { + return general_window_class_.get(); + } - //Unregister a window that is going to be destroyed. - //This function removes the hwnd from the hwnd-window map. - //It should be called immediately before a window is going to be destroyed, - void UnregisterWindow(HWND hwnd); + // Register a window newly created. + // This function adds the hwnd to hwnd-window map. + // It should be called immediately after a window was created. + void RegisterWindow(HWND hwnd, Window* window); - //Return a pointer to the Window object related to the HWND or nullptr if the hwnd is not in the map. - Window* FromHandle(HWND hwnd); + // Unregister a window that is going to be destroyed. + // This function removes the hwnd from the hwnd-window map. + // It should be called immediately before a window is going to be destroyed, + void UnregisterWindow(HWND hwnd); - std::vector GetAllWindows() const; + // Return a pointer to the Window object related to the HWND or nullptr if the + // hwnd is not in the map. + Window* FromHandle(HWND hwnd); - private: - std::unique_ptr general_window_class_; - std::map window_map_; - }; + std::vector GetAllWindows() const; + private: + std::unique_ptr general_window_class_; + std::map window_map_; +}; +class Window final : public ContentControl { + friend class WindowManager; - class Window final : public ContentControl - { - friend class WindowManager; - public: - static constexpr auto control_type = L"Window"; + public: + static constexpr auto control_type = L"Window"; - public: - static Window* CreateOverlapped(); - static Window* CreatePopup(Window* parent, bool caption = false); + public: + static Window* CreateOverlapped(); + static Window* CreatePopup(Window* parent, bool caption = false); - private: - struct tag_overlapped_constructor {}; - struct tag_popup_constructor {}; + private: + struct tag_overlapped_constructor {}; + struct tag_popup_constructor {}; - explicit Window(tag_overlapped_constructor); - Window(tag_popup_constructor, Window* parent, bool caption); + explicit Window(tag_overlapped_constructor); + Window(tag_popup_constructor, Window* parent, bool caption); - void BeforeCreateHwnd(); - void AfterCreateHwnd(WindowManager* window_manager); + void BeforeCreateHwnd(); + void AfterCreateHwnd(WindowManager* window_manager); - public: - Window(const Window& other) = delete; - Window(Window&& other) = delete; - Window& operator=(const Window& other) = delete; - Window& operator=(Window&& other) = delete; - ~Window() override; + public: + Window(const Window& other) = delete; + Window(Window&& other) = delete; + Window& operator=(const Window& other) = delete; + Window& operator=(Window&& other) = delete; + ~Window() override; - public: - StringView GetControlType() const override final; + public: + StringView GetControlType() const override final; - void SetDeleteThisOnDestroy(bool value); + void SetDeleteThisOnDestroy(bool value); - //*************** region: handle *************** + //*************** region: handle *************** - //Get the handle of the window. Return null if window is invalid. - HWND GetWindowHandle() const - { - return hwnd_; - } + // Get the handle of the window. Return null if window is invalid. + HWND GetWindowHandle() const { return hwnd_; } - //Return if the window is still valid, that is, hasn't been closed or destroyed. - bool IsWindowValid() const - { - return hwnd_ != nullptr; - } + // Return if the window is still valid, that is, hasn't been closed or + // destroyed. + bool IsWindowValid() const { return hwnd_ != nullptr; } + //*************** region: window operations *************** - //*************** region: window operations *************** + Window* GetParentWindow() const { return parent_window_; } - Window* GetParentWindow() const - { - return parent_window_; - } + // Close and destroy the window if the window is valid. + void Close(); - //Close and destroy the window if the window is valid. - void Close(); + // Send a repaint message to the window's message queue which may make the + // window repaint. + void InvalidateDraw() override final; - //Send a repaint message to the window's message queue which may make the window repaint. - void InvalidateDraw() override final; + // Show the window. + void Show(); - //Show the window. - void Show(); + // Hide thw window. + void Hide(); - //Hide thw window. - void Hide(); + // Get the client size. + Size GetClientSize(); - //Get the client size. - Size GetClientSize(); + // Set the client size and repaint. + void SetClientSize(const Size& size); - //Set the client size and repaint. - void SetClientSize(const Size& size); + // Get the rect of the window containing frame. + // The lefttop of the rect is relative to screen lefttop. + Rect GetWindowRect(); - //Get the rect of the window containing frame. - //The lefttop of the rect is relative to screen lefttop. - Rect GetWindowRect(); + // Set the rect of the window containing frame. + // The lefttop of the rect is relative to screen lefttop. + void SetWindowRect(const Rect& rect); - //Set the rect of the window containing frame. - //The lefttop of the rect is relative to screen lefttop. - void SetWindowRect(const Rect& rect); + // Set the lefttop of the window relative to screen. + void SetWindowPosition(const Point& position); - //Set the lefttop of the window relative to screen. - void SetWindowPosition(const Point& position); + Point PointToScreen(const Point& point); - Point PointToScreen(const Point& point); + Point PointFromScreen(const Point& point); - Point PointFromScreen(const Point& point); + // Handle the raw window message. + // Return true if the message is handled and get the result through "result" + // argument. Return false if the message is not handled. + bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, + LRESULT& result); - //Handle the raw window message. - //Return true if the message is handled and get the result through "result" argument. - //Return false if the message is not handled. - bool HandleWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param, LRESULT& result); + //*************** region: mouse *************** - //*************** region: mouse *************** + Point GetMousePosition(); - Point GetMousePosition(); + Control* GetMouseHoverControl() const { return mouse_hover_control_; } - Control* GetMouseHoverControl() const - { - return mouse_hover_control_; - } + //*************** region: layout *************** - //*************** region: position and size *************** + void WindowInvalidateLayout(); - //Always return (0, 0) for a window. - Point GetOffset() override final; + void Relayout(); - //Get the size of client area for a window. - Size GetSize() override final; + void SetSizeFitContent(const Size& max_size = Size(1000, 1000)); - //This method has no effect for a window. Use SetClientSize instead. - void SetRect(const Rect& size) override final; + //*************** region: focus *************** - //Override. If point is in client area, it is in window. - bool IsPointInside(const Point& point) override final; + // Request focus for specified control. + bool RequestFocusFor(Control* control); - //*************** region: layout *************** + // Get the control that has focus. + Control* GetFocusControl(); - void WindowInvalidateLayout(); + //*************** region: mouse capture *************** - void Relayout(); + Control* CaptureMouseFor(Control* control); + Control* ReleaseCurrentMouseCapture(); - void SetSizeFitContent(const Size& max_size = Size(1000, 1000)); + //*************** region: cursor *************** + void UpdateCursor(); + //*************** region: debug *************** +#ifdef CRU_DEBUG_LAYOUT + bool IsDebugLayout() const { return debug_layout_; } - //*************** region: focus *************** + void SetDebugLayout(bool value); +#endif - //Request focus for specified control. - bool RequestFocusFor(Control* control); + public: + //*************** region: events *************** + Event activated_event; + Event deactivated_event; - //Get the control that has focus. - Control* GetFocusControl(); + Event native_message_event; + private: + //*************** region: native operations *************** - //*************** region: mouse capture *************** + // Get the client rect in pixel. + RECT GetClientRectPixel(); - Control* CaptureMouseFor(Control* control); - Control* ReleaseCurrentMouseCapture(); + bool IsMessageInQueue(UINT message); - - //*************** region: cursor *************** - void UpdateCursor(); + void SetCursorInternal(HCURSOR cursor); - //*************** region: debug *************** -#ifdef CRU_DEBUG_LAYOUT - bool IsDebugLayout() const - { - return debug_layout_; - } + //*************** region: native messages *************** - void SetDebugLayout(bool value); -#endif - - public: - //*************** region: events *************** - Event activated_event; - Event deactivated_event; - - Event native_message_event; - - private: - //*************** region: native operations *************** - - //Get the client rect in pixel. - RECT GetClientRectPixel(); - - bool IsMessageInQueue(UINT message); - - void SetCursorInternal(HCURSOR cursor); - - - //*************** region: native messages *************** - - void OnDestroyInternal(); - void OnPaintInternal(); - void OnResizeInternal(int new_width, int new_height); - - void OnSetFocusInternal(); - void OnKillFocusInternal(); - - void OnMouseMoveInternal(POINT point); - void OnMouseLeaveInternal(); - void OnMouseDownInternal(MouseButton button, POINT point); - void OnMouseUpInternal(MouseButton button, POINT point); - - void OnMouseWheelInternal(short delta, POINT point); - void OnKeyDownInternal(int virtual_code); - void OnKeyUpInternal(int virtual_code); - void OnCharInternal(wchar_t c); - - void OnActivatedInternal(); - void OnDeactivatedInternal(); - - //*************** region: event dispatcher helper *************** - - void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point); - - private: - bool delete_this_on_destroy_ = true; - - HWND hwnd_ = nullptr; - Window* parent_window_ = nullptr; - std::shared_ptr render_target_{}; - - Control* mouse_hover_control_ = nullptr; - - bool window_focus_ = false; - Control* focus_control_ = this; // "focus_control_" can't be nullptr - - Control* mouse_capture_control_ = nullptr; - - bool is_layout_invalid_ = false; + void OnDestroyInternal(); + void OnPaintInternal(); + void OnResizeInternal(int new_width, int new_height); + + void OnSetFocusInternal(); + void OnKillFocusInternal(); + + void OnMouseMoveInternal(POINT point); + void OnMouseLeaveInternal(); + void OnMouseDownInternal(MouseButton button, POINT point); + void OnMouseUpInternal(MouseButton button, POINT point); + + void OnMouseWheelInternal(short delta, POINT point); + void OnKeyDownInternal(int virtual_code); + void OnKeyUpInternal(int virtual_code); + void OnCharInternal(wchar_t c); + + void OnActivatedInternal(); + void OnDeactivatedInternal(); + + //*************** region: event dispatcher helper *************** + + void DispatchMouseHoverControlChangeEvent(Control* old_control, + Control* new_control, + const Point& point); + + private: + bool delete_this_on_destroy_ = true; + + HWND hwnd_ = nullptr; + Window* parent_window_ = nullptr; + std::shared_ptr render_target_{}; + + Control* mouse_hover_control_ = nullptr; + + bool window_focus_ = false; + Control* focus_control_ = this; // "focus_control_" can't be nullptr + + Control* mouse_capture_control_ = nullptr; + + bool is_layout_invalid_ = false; #ifdef CRU_DEBUG_LAYOUT - bool debug_layout_ = false; + bool debug_layout_ = false; #endif - }; -} +}; +} // namespace cru::ui -- cgit v1.2.3 From fdbb02b2cbdd4e4069005d0535a343229f7c4d32 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 21 Mar 2019 21:42:28 +0800 Subject: ... --- CruUI.vcxproj | 2 + CruUI.vcxproj.filters | 6 + src/main.cpp | 239 +----- src/pre.hpp | 4 - src/ui/control.hpp | 2 +- src/ui/controls/flex_layout.cpp | 4 + src/ui/controls/flex_layout.hpp | 10 +- src/ui/controls/text_block.cpp | 4 + src/ui/controls/text_block.hpp | 10 +- src/ui/d2d_util.hpp | 158 ++-- src/ui/events/ui_event.cpp | 6 - src/ui/events/ui_event.hpp | 507 +++++------- src/ui/input_util.cpp | 29 +- src/ui/input_util.hpp | 20 +- src/ui/render/flex_layout_render_object.cpp | 2 + src/ui/render/render_object.cpp | 5 +- src/ui/render/render_object.hpp | 8 +- src/ui/render/text_render_object.cpp | 6 + src/ui/render/text_render_object.hpp | 2 + src/ui/render/window_render_object.cpp | 48 ++ src/ui/render/window_render_object.hpp | 40 + src/ui/window.cpp | 1194 ++++++++++++--------------- src/ui/window.hpp | 33 +- 23 files changed, 1018 insertions(+), 1321 deletions(-) create mode 100644 src/ui/render/window_render_object.cpp create mode 100644 src/ui/render/window_render_object.hpp (limited to 'src/ui/render/render_object.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 2954ecc8..2e1359f9 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -118,6 +118,7 @@ + @@ -130,6 +131,7 @@ + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 35a97194..3bd5d30a 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -78,6 +78,9 @@ Source Files + + Source Files + @@ -164,6 +167,9 @@ Header Files + + Header Files + 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 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 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(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(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 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(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(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(LayoutSideParams::Content(Alignment::Start), LayoutSideParams::Content(), L"This is a little short sentence!!!")); - layout->AddChild(CreateWithLayout(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(LayoutSideParams::Exactly(200), LayoutSideParams::Content(), - std::initializer_list{ - CreateWithLayout(LayoutSideParams::Stretch(), LayoutSideParams::Content()) - } - )); - */ + window->SetChild(flex_layout); - /* test 3 - const auto linear_layout = CreateWithLayout(Thickness(50, 50), Thickness(50, 50), LinearLayout::Orientation::Vertical, ControlList{ - Button::Create({ - TextBlock::Create(L"Button") - }), - CreateWithLayout(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 @@ -4,10 +4,6 @@ #define CRU_DEBUG #endif -#ifdef CRU_DEBUG -#define CRU_DEBUG_LAYOUT -#endif - #ifdef CRU_DEBUG #define _CRTDBG_MAP_ALLOC #include 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 +#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 - class RoutedEvent - { - public: - static_assert(std::is_base_of_v, "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 direct; - Event bubble; - Event tunnel; - }; - - class MouseEventArgs : public UiEventArgs - { - public: - MouseEventArgs(Object* sender, Object* original_sender, const std::optional& 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_; - }; - - - 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 GetResult() const - { - return result_; - } - - void SetResult(const std::optional result) - { - result_ = result; - } - - private: - WindowNativeMessage message_; - std::optional result_; - }; - - class KeyEventArgs : public UiEventArgs - { - public: - KeyEventArgs(Object* sender, Object* original_sender, int virtual_code) - : 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 +class RoutedEvent { + public: + static_assert(std::is_base_of_v, + "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 direct; + Event bubble; + Event tunnel; +}; + +class MouseEventArgs : public UiEventArgs { + public: + MouseEventArgs(Object* sender, Object* original_sender, + const std::optional& 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_; +}; + +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 GetResult() const { return result_; } + + void SetResult(const std::optional result) { result_ = result; } + + private: + WindowNativeMessage message_; + std::optional result_; +}; + +class KeyEventArgs : public UiEventArgs { + public: + KeyEventArgs(Object* sender, Object* original_sender, int virtual_code) + : 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(result) & 0x8000) != 0; - } +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 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); - } +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 + +#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([](auto) - { - return new WindowManager{}; - }); - } - - WindowManager::WindowManager() { - general_window_class_ = std::make_unique( - 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 WindowManager::GetAllWindows() const - { - std::vector 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( + [](auto) { return new WindowManager{}; }); +} +WindowManager::WindowManager() { + general_window_class_ = std::make_unique( + 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 WindowManager::GetAllWindows() const { + std::vector 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(GetWindowLongPtr(hwnd_, GWL_STYLE)); - const auto window_ex_style = static_cast(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(GetWindowLongPtr(hwnd_, GWL_STYLE)); + const auto window_ex_style = + static_cast(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(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; - } - } +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(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; + } +} - 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(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(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(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(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 @@ -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 activated_event; @@ -184,7 +174,12 @@ class Window final : public ContentControl { 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. @@ -229,6 +224,8 @@ class Window final : public ContentControl { Window* parent_window_ = nullptr; std::shared_ptr render_target_{}; + render::WindowRenderObject* render_object_; + Control* mouse_hover_control_ = nullptr; bool window_focus_ = false; -- cgit v1.2.3 From afdac77a66143375b5bebd4ff128b0fba87c5d21 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 21 Mar 2019 22:44:31 +0800 Subject: Change all error handling. --- src/base.hpp | 1 + src/ui/content_control.cpp | 4 +--- src/ui/control.cpp | 3 --- src/ui/layout_control.cpp | 21 +++++---------------- src/ui/render/render_object.cpp | 21 ++++++--------------- src/ui/render/text_render_object.cpp | 2 -- src/ui/render/window_render_object.cpp | 2 -- src/ui/window.cpp | 25 ++++++++++--------------- src/ui/window_class.cpp | 5 ++++- 9 files changed, 27 insertions(+), 57 deletions(-) (limited to 'src/ui/render/render_object.cpp') diff --git a/src/base.hpp b/src/base.hpp index 2c511c4b..e3dfc1ee 100644 --- a/src/base.hpp +++ b/src/base.hpp @@ -1,6 +1,7 @@ #pragma once #include "pre.hpp" +#include #include #include #include diff --git a/src/ui/content_control.cpp b/src/ui/content_control.cpp index 960867b2..d5abca1c 100644 --- a/src/ui/content_control.cpp +++ b/src/ui/content_control.cpp @@ -9,9 +9,7 @@ ContentControl::ContentControl() ContentControl::~ContentControl() { delete child_; } void ContentControl::SetChild(Control* child) { - if (dynamic_cast(child)) - throw std::invalid_argument("Can't add a window as child."); - + assert(!dynamic_cast(child)); // Can't add a window as child. if (child == child_) return; const auto window = GetWindow(); diff --git a/src/ui/control.cpp b/src/ui/control.cpp index e19754dc..5c629fd6 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -1,11 +1,8 @@ #include "control.hpp" -#include - #include "window.hpp" namespace cru::ui { - void Control::_SetParent(Control* parent) { const auto old_parent = GetParent(); parent_ = parent; diff --git a/src/ui/layout_control.cpp b/src/ui/layout_control.cpp index d2b430dd..c0c4a7fe 100644 --- a/src/ui/layout_control.cpp +++ b/src/ui/layout_control.cpp @@ -3,24 +3,14 @@ #include "window.hpp" namespace cru::ui { -void ControlAddChildCheck(Control* control) { - if (control->GetParent() != nullptr) - throw std::invalid_argument("The control already has a parent."); - - if (dynamic_cast(control)) - throw std::invalid_argument("Can't add a window as child."); -} - LayoutControl::~LayoutControl() { for (const auto child : children_) delete child; } void LayoutControl::AddChild(Control* control, const int position) { - ControlAddChildCheck(control); - - if (position < 0 || static_cast(position) > - this->children_.size()) - throw std::invalid_argument("The position is out of range."); + assert(control->GetParent() == nullptr); // The control already has a parent. + assert(!dynamic_cast(control)); // Can't add a window as child. + assert(position >= 0 || position <= this->children_.size()); // The position is out of range. children_.insert(this->children_.cbegin() + position, control); @@ -31,9 +21,8 @@ void LayoutControl::AddChild(Control* control, const int position) { } void LayoutControl::RemoveChild(const int position) { - if (position < 0 || static_castchildren_.size())>(position) >= - this->children_.size()) - throw std::invalid_argument("The position is out of range."); + assert(position >= 0 && + position < this->children_.size()); // The position is out of range. const auto i = children_.cbegin() + position; const auto child = *i; diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 0be44c1e..6380c2fe 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -4,15 +4,10 @@ namespace cru::ui::render { void RenderObject::AddChild(RenderObject* render_object, const int position) { - if (render_object->GetParent() != nullptr) - throw std::invalid_argument("Render object already has a parent."); - - if (position < 0) - throw std::invalid_argument("Position index is less than 0."); - - if (static_cast::size_type>(position) > - children_.size()) - throw std::invalid_argument("Position index is out of bound."); + assert(render_object->GetParent() == + nullptr); // Render object already has a parent. + assert(position >= 0); // Position index is less than 0. + assert(position <= children_.size()); // Position index is out of bound. children_.insert(children_.cbegin() + position, render_object); render_object->SetParent(this); @@ -20,12 +15,8 @@ void RenderObject::AddChild(RenderObject* render_object, const int position) { } void RenderObject::RemoveChild(const int position) { - if (position < 0) - throw std::invalid_argument("Position index is less than 0."); - - if (static_cast::size_type>(position) >= - children_.size()) - throw std::invalid_argument("Position index is out of bound."); + assert(position >= 0); // Position index is less than 0. + assert(position < children_.size()); // Position index is out of bound. const auto i = children_.cbegin() + position; const auto removed_child = *i; diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp index e8032f78..b90dae71 100644 --- a/src/ui/render/text_render_object.cpp +++ b/src/ui/render/text_render_object.cpp @@ -1,7 +1,5 @@ #include "text_render_object.hpp" -#include - #include "exception.hpp" #include "graph/graph.hpp" diff --git a/src/ui/render/window_render_object.cpp b/src/ui/render/window_render_object.cpp index 52452bd4..f198c2fa 100644 --- a/src/ui/render/window_render_object.cpp +++ b/src/ui/render/window_render_object.cpp @@ -1,7 +1,5 @@ #include "window_render_object.hpp" -#include - #include "graph/graph.hpp" #include "ui/window.hpp" diff --git a/src/ui/window.cpp b/src/ui/window.cpp index b976ca6a..ca3356ff 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -117,19 +117,14 @@ WindowManager::WindowManager() { } void WindowManager::RegisterWindow(HWND hwnd, Window* window) { - const auto find_result = window_map_.find(hwnd); - if (find_result != window_map_.end()) - throw std::runtime_error("The hwnd is already in the map."); - + assert(window_map_.count(hwnd) == 0); // The hwnd is already in the map. window_map_.emplace(hwnd, window); } void WindowManager::UnregisterWindow(HWND hwnd) { const auto find_result = window_map_.find(hwnd); - if (find_result == window_map_.end()) - throw std::runtime_error("The hwnd is not in the map."); + assert(find_result != window_map_.end()); // The hwnd is not in the map. window_map_.erase(find_result); - if (window_map_.empty()) Application::GetInstance()->Quit(0); } @@ -188,14 +183,15 @@ Window::Window(tag_overlapped_constructor) { CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr); - if (hwnd_ == nullptr) throw std::runtime_error("Failed to create window."); + if (hwnd_ == nullptr) + throw Win32Error(::GetLastError(), "Failed to create window."); AfterCreateHwnd(window_manager); } Window::Window(tag_popup_constructor, Window* parent, const bool caption) { - if (parent != nullptr && !parent->IsWindowValid()) - throw std::runtime_error("Parent window is not valid."); + assert(parent == nullptr || + parent->IsWindowValid()); // Parent window is not valid. BeforeCreateHwnd(); @@ -210,7 +206,8 @@ Window::Window(tag_popup_constructor, Window* parent, const bool caption) { parent == nullptr ? nullptr : parent->GetWindowHandle(), nullptr, Application::GetInstance()->GetInstanceHandle(), nullptr); - if (hwnd_ == nullptr) throw std::runtime_error("Failed to create window."); + if (hwnd_ == nullptr) + throw Win32Error(::GetLastError(), "Failed to create window."); AfterCreateHwnd(window_manager); } @@ -474,10 +471,8 @@ Point Window::GetMousePosition() { } bool Window::RequestFocusFor(Control* control) { - if (control == nullptr) - throw std::invalid_argument( - "The control to request focus can't be null. You can set it as the " - "window."); + assert(control != nullptr); // The control to request focus can't be null. + // You can set it as the window. if (!IsWindowValid()) return false; diff --git a/src/ui/window_class.cpp b/src/ui/window_class.cpp index 456d9492..5e8b3454 100644 --- a/src/ui/window_class.cpp +++ b/src/ui/window_class.cpp @@ -1,5 +1,7 @@ #include "window_class.hpp" +#include "exception.hpp" + namespace cru::ui { WindowClass::WindowClass(const String& name, WNDPROC window_proc, HINSTANCE h_instance) @@ -20,6 +22,7 @@ WindowClass::WindowClass(const String& name, WNDPROC window_proc, window_class.hIconSm = NULL; atom_ = ::RegisterClassExW(&window_class); - if (atom_ == 0) throw std::runtime_error("Failed to create window class."); + if (atom_ == 0) + throw Win32Error(::GetLastError(), "Failed to create window class."); } } // namespace cru::ui -- cgit v1.2.3 From 39d2c8b3d957e75c3a2a1c738c1628e3eb5c0173 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 23 Mar 2019 00:15:18 +0800 Subject: ... --- CruUI.vcxproj | 2 + CruUI.vcxproj.filters | 6 ++ src/ui/render/border_render_object.cpp | 96 +++++++++++++++++++++++++++++ src/ui/render/border_render_object.hpp | 71 +++++++++++++++++++++ src/ui/render/flex_layout_render_object.hpp | 10 +-- src/ui/render/render_object.cpp | 30 +++++---- src/ui/render/render_object.hpp | 9 +-- src/ui/render/text_render_object.hpp | 7 ++- 8 files changed, 206 insertions(+), 25 deletions(-) create mode 100644 src/ui/render/border_render_object.cpp create mode 100644 src/ui/render/border_render_object.hpp (limited to 'src/ui/render/render_object.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index a6daa3c4..374a882c 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -120,6 +120,7 @@ + @@ -136,6 +137,7 @@ + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 36ae998d..5554927b 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -87,6 +87,9 @@ Source Files + + Source Files + @@ -182,6 +185,9 @@ Header Files + + Header Files + diff --git a/src/ui/render/border_render_object.cpp b/src/ui/render/border_render_object.cpp new file mode 100644 index 00000000..c98d6c29 --- /dev/null +++ b/src/ui/render/border_render_object.cpp @@ -0,0 +1,96 @@ +#include "border_render_object.hpp" + +#include "cru_debug.hpp" + +namespace cru::ui::render { +BorderRenderObject::BorderRenderObject(Microsoft::WRL::ComPtr brush) + : border_brush_(std::move(brush)) {} + +void BorderRenderObject::OnMeasureCore(const Size& available_size) { + const auto margin = GetMargin(); + const auto padding = GetPadding(); + Size margin_border_padding_size{ + margin.GetHorizontalTotal() + padding.GetHorizontalTotal(), + margin.GetVerticalTotal() + padding.GetVerticalTotal()}; + + if (is_enabled_) { + margin_border_padding_size.width += border_width_.GetHorizontalTotal(); + margin_border_padding_size.height += border_width_.GetVerticalTotal(); + } + + auto coerced_margin_border_padding_size = margin_border_padding_size; + if (coerced_margin_border_padding_size.width > available_size.width) { + debug::DebugMessage( + L"Measure: horizontal length of padding, border and margin is bigger " + L"than available length."); + coerced_margin_border_padding_size.width = available_size.width; + } + if (coerced_margin_border_padding_size.height > available_size.height) { + debug::DebugMessage( + L"Measure: vertical length of padding, border and margin is bigger " + L"than available length."); + coerced_margin_border_padding_size.height = available_size.height; + } + + const auto coerced_content_available_size = + available_size - coerced_margin_border_padding_size; + + const auto actual_content_size = + OnMeasureContent(coerced_content_available_size); + + SetPreferredSize(coerced_margin_border_padding_size + actual_content_size); +} + +void BorderRenderObject::OnLayoutCore(const Rect& rect) { + const auto margin = GetMargin(); + const auto padding = GetPadding(); + Size margin_border_padding_size{ + margin.GetHorizontalTotal() + padding.GetHorizontalTotal(), + margin.GetVerticalTotal() + padding.GetVerticalTotal()}; + + if (is_enabled_) { + margin_border_padding_size.width += border_width_.GetHorizontalTotal(); + margin_border_padding_size.height += border_width_.GetVerticalTotal(); + } + + const auto content_available_size = + rect.GetSize() - margin_border_padding_size; + auto coerced_content_available_size = content_available_size; + + if (coerced_content_available_size.width < 0) { + debug::DebugMessage( + L"Layout: horizontal length of padding, border and margin is bigger " + L"than available length."); + coerced_content_available_size.width = 0; + } + if (coerced_content_available_size.height < 0) { + debug::DebugMessage( + L"Layout: vertical length of padding, border and margin is bigger than " + L"available length."); + coerced_content_available_size.height = 0; + } + + OnLayoutContent( + Rect{margin.left + (is_enabled_ ? border_width_.left : 0) + padding.left, + margin.top + (is_enabled_ ? border_width_.top : 0) + padding.top, + coerced_content_available_size.width, + coerced_content_available_size.height}); +} + +Size BorderRenderObject::OnMeasureContent(const Size& available_size) { + const auto child = GetChild(); + if (child) { + child->Measure(available_size); + return child->GetPreferredSize(); + } else { + return Size::Zero(); + } +} + +void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { + const auto child = GetChild(); + if (child) { + child->Layout(content_rect); + } +} +} // namespace cru::ui::render diff --git a/src/ui/render/border_render_object.hpp b/src/ui/render/border_render_object.hpp new file mode 100644 index 00000000..d7a595af --- /dev/null +++ b/src/ui/render/border_render_object.hpp @@ -0,0 +1,71 @@ +#pragma once +#include "pre.hpp" + +#include // for ComPtr + +#include "render_object.hpp" + +// forward declarations +struct ID2D1Brush; +struct ID2D1Geometry; + +struct CornerRadius { + float left_top; + float right_top; + float left_bottom; + float right_bottom; +}; + +namespace cru::ui::render { +class BorderRenderObject : public RenderObject { + public: + explicit BorderRenderObject(Microsoft::WRL::ComPtr brush); + BorderRenderObject(const BorderRenderObject& other) = delete; + BorderRenderObject(BorderRenderObject&& other) = delete; + BorderRenderObject& operator=(const BorderRenderObject& other) = delete; + BorderRenderObject& operator=(BorderRenderObject&& other) = delete; + ~BorderRenderObject() override = default; + + bool IsEnabled() const { return is_enabled_; } + void SetEnabled(bool enabled) { is_enabled_ = enabled; } + + Microsoft::WRL::ComPtr GetBrush() const { return border_brush_; } + void SetBrush(const Microsoft::WRL::ComPtr new_brush) { + border_brush_ = std::move(new_brush); + } + + Thickness GetBorderWidth() const { return border_width_; } + void SetBorderWidth(const Thickness& thickness) { border_width_ = thickness; } + + CornerRadius GetCornerRadius() const { return corner_radius_; } + void SetCornerRadius(const CornerRadius& new_corner_radius) { + corner_radius_ = new_corner_radius; + } + + void RecreateGeometry(); // TODO + + void Draw(ID2D1RenderTarget* render_target) override; // TODO + + RenderObject* HitTest(const Point& point) override; // TODO + + protected: + void OnMeasureCore(const Size& available_size) override; + void OnLayoutCore(const Rect& rect) 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: + bool is_enabled_; + + Microsoft::WRL::ComPtr border_brush_; + Thickness border_width_; + CornerRadius corner_radius_; + + Microsoft::WRL::ComPtr geometry_; +}; +} // namespace cru::ui::render diff --git a/src/ui/render/flex_layout_render_object.hpp b/src/ui/render/flex_layout_render_object.hpp index b12362e8..ac4c2c0f 100644 --- a/src/ui/render/flex_layout_render_object.hpp +++ b/src/ui/render/flex_layout_render_object.hpp @@ -16,7 +16,7 @@ enum class FlexDirection { enum class Alignment { Start, End, Center }; struct FlexChildLayoutData { - std::optional flex_basis; + std::optional flex_basis; // nullopt stands for content float flex_grow = 0; float flex_shrink = 0; Alignment alignment = Alignment::Center; @@ -35,12 +35,8 @@ class FlexLayoutRenderObject : public RenderObject { FlexDirection GetFlexDirection() const { return direction_; } void SetFlexDirection(FlexDirection direction) { direction_ = direction; } - Alignment GetContentMainAlign() const { - return content_main_align_; - } - void SetContentMainAlign(Alignment align) { - content_main_align_ = align; - } + Alignment GetContentMainAlign() const { return content_main_align_; } + void SetContentMainAlign(Alignment align) { content_main_align_ = align; } FlexChildLayoutData* GetChildLayoutData(int position); diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 6380c2fe..f56baa8f 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -1,5 +1,8 @@ #include "render_object.hpp" +#include +#include + #include "cru_debug.hpp" namespace cru::ui::render { @@ -44,36 +47,31 @@ 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; - OnParentChanged(old_parent, new_parent); -} - void RenderObject::OnMeasureCore(const Size& available_size) { Size margin_padding_size{ margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; - const auto content_available_size = available_size - margin_padding_size; - auto coerced_content_available_size = content_available_size; - if (coerced_content_available_size.width < 0) { + auto coerced_margin_padding_size = margin_padding_size; + if (coerced_margin_padding_size.width > available_size.width) { debug::DebugMessage( L"Measure: horizontal length of padding and margin is bigger than " L"available length."); - coerced_content_available_size.width = 0; + coerced_margin_padding_size.width = available_size.width; } - if (coerced_content_available_size.height < 0) { + if (coerced_margin_padding_size.height > available_size.height) { debug::DebugMessage( L"Measure: vertical length of padding and margin is bigger than " L"available length."); - coerced_content_available_size.height = 0; + coerced_margin_padding_size.height = available_size.height; } + const auto coerced_content_available_size = + available_size - coerced_margin_padding_size; const auto actual_content_size = OnMeasureContent(coerced_content_available_size); - SetPreferredSize(margin_padding_size + actual_content_size); + SetPreferredSize(coerced_margin_padding_size + actual_content_size); } void RenderObject::OnLayoutCore(const Rect& rect) { @@ -100,4 +98,10 @@ void RenderObject::OnLayoutCore(const Rect& rect) { coerced_content_available_size.width, coerced_content_available_size.height}); } + +void RenderObject::SetParent(RenderObject* new_parent) { + const auto old_parent = parent_; + parent_ = new_parent; + OnParentChanged(old_parent, new_parent); +} } // namespace cru::ui::render diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index a9950198..51b0c3ae 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -3,11 +3,13 @@ #include #include -#include "system_headers.hpp" #include "base.hpp" #include "ui/ui_base.hpp" +// forward declarations +struct ID2D1RenderTarget; + namespace cru::ui { class Control; } @@ -70,15 +72,14 @@ class RenderObject : public Object { virtual void OnSizeChanged(const Size& old_size, const Size& new_size); + virtual void OnMeasureCore(const Size& available_size); + virtual void OnLayoutCore(const Rect& rect); virtual Size OnMeasureContent(const Size& available_size) = 0; virtual void OnLayoutContent(const Rect& content_rect) = 0; private: void SetParent(RenderObject* new_parent); - void OnMeasureCore(const Size& available_size); - void OnLayoutCore(const Rect& rect); - private: Control* control_ = nullptr; diff --git a/src/ui/render/text_render_object.hpp b/src/ui/render/text_render_object.hpp index 4361b9c0..ac874b75 100644 --- a/src/ui/render/text_render_object.hpp +++ b/src/ui/render/text_render_object.hpp @@ -1,10 +1,15 @@ #pragma once #include "pre.hpp" -#include "system_headers.hpp" +#include // for ComPtr #include "render_object.hpp" +// forward declarations +struct ID2D1Brush; +struct IDWriteTextFormat; +struct IDWriteTextLayout; + namespace cru::ui::render { class TextRenderObject : public RenderObject { public: -- cgit v1.2.3 From 79d1d76509dbf6cf9c79f8eb55968535982975aa Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 24 Mar 2019 19:06:17 +0800 Subject: ... --- CruUI.vcxproj | 8 +- CruUI.vcxproj.filters | 24 +++- src/graph/graph.cpp | 161 --------------------------- src/graph/graph.hpp | 164 ---------------------------- src/graph/graph_manager.cpp | 79 ++++++++++++++ src/graph/graph_manager.hpp | 60 ++++++++++ src/graph/graph_util.hpp | 63 +++++++++++ src/graph/window_render_target.cpp | 99 +++++++++++++++++ src/graph/window_render_target.hpp | 49 +++++++++ src/ui/controls/button.cpp | 13 +++ src/ui/controls/button.hpp | 31 +++++- src/ui/controls/text_block.cpp | 2 - src/ui/render/border_render_object.cpp | 52 +++++++-- src/ui/render/border_render_object.hpp | 22 ++-- src/ui/render/flex_layout_render_object.cpp | 2 +- src/ui/render/render_object.cpp | 3 - src/ui/render/render_object.hpp | 2 - src/ui/render/text_render_object.cpp | 67 +++++++++--- src/ui/render/text_render_object.hpp | 39 +++---- src/ui/render/window_render_object.cpp | 2 +- src/ui/ui_manager.cpp | 57 +++++----- src/ui/ui_manager.hpp | 15 +-- src/ui/window.cpp | 22 ++-- src/ui/window.hpp | 3 +- src/util/com_util.hpp | 22 ++++ 25 files changed, 602 insertions(+), 459 deletions(-) delete mode 100644 src/graph/graph.cpp delete mode 100644 src/graph/graph.hpp create mode 100644 src/graph/graph_manager.cpp create mode 100644 src/graph/graph_manager.hpp create mode 100644 src/graph/graph_util.hpp create mode 100644 src/graph/window_render_target.cpp create mode 100644 src/graph/window_render_target.hpp create mode 100644 src/util/com_util.hpp (limited to 'src/ui/render/render_object.cpp') diff --git a/CruUI.vcxproj b/CruUI.vcxproj index 4143bb43..24b6957e 100644 --- a/CruUI.vcxproj +++ b/CruUI.vcxproj @@ -125,6 +125,7 @@ + @@ -137,13 +138,15 @@ - + + + @@ -153,6 +156,7 @@ + @@ -171,7 +175,7 @@ - + diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters index 01575ce2..68dc97ba 100644 --- a/CruUI.vcxproj.filters +++ b/CruUI.vcxproj.filters @@ -27,9 +27,6 @@ Source Files - - Source Files - Source Files @@ -93,11 +90,14 @@ Source Files + + Source Files + + + Source Files + - - Header Files - Header Files @@ -191,6 +191,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/src/graph/graph.cpp b/src/graph/graph.cpp deleted file mode 100644 index ed3fe5d5..00000000 --- a/src/graph/graph.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "graph.hpp" - -#include "application.hpp" -#include "exception.hpp" - -namespace cru::graph { -using Microsoft::WRL::ComPtr; - -WindowRenderTarget::WindowRenderTarget(GraphManager* graph_manager, HWND hwnd) { - this->graph_manager_ = graph_manager; - - const auto d3d11_device = graph_manager->GetD3D11Device(); - const auto dxgi_factory = graph_manager->GetDxgiFactory(); - - // Allocate a descriptor. - DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {0}; - swap_chain_desc.Width = 0; // use automatic sizing - swap_chain_desc.Height = 0; - swap_chain_desc.Format = - DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format - swap_chain_desc.Stereo = false; - swap_chain_desc.SampleDesc.Count = 1; // don't use multi-sampling - swap_chain_desc.SampleDesc.Quality = 0; - swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swap_chain_desc.BufferCount = 2; // use double buffering to enable flip - swap_chain_desc.Scaling = DXGI_SCALING_NONE; - swap_chain_desc.SwapEffect = - DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all apps must use this SwapEffect - swap_chain_desc.Flags = 0; - - // Get the final swap chain for this window from the DXGI factory. - ThrowIfFailed(dxgi_factory->CreateSwapChainForHwnd( - d3d11_device.Get(), hwnd, &swap_chain_desc, nullptr, nullptr, - &dxgi_swap_chain_)); - - CreateTargetBitmap(); -} - -WindowRenderTarget::~WindowRenderTarget() {} - -void WindowRenderTarget::ResizeBuffer(const int width, const int height) { - const auto graph_manager = graph_manager_; - const auto d2d1_device_context = graph_manager->GetD2D1DeviceContext(); - - ComPtr old_target; - d2d1_device_context->GetTarget(&old_target); - const auto target_this = old_target == this->target_bitmap_; - if (target_this) d2d1_device_context->SetTarget(nullptr); - - old_target = nullptr; - target_bitmap_ = nullptr; - - ThrowIfFailed(dxgi_swap_chain_->ResizeBuffers(0, width, height, - DXGI_FORMAT_UNKNOWN, 0)); - - CreateTargetBitmap(); - - if (target_this) d2d1_device_context->SetTarget(target_bitmap_.Get()); -} - -void WindowRenderTarget::SetAsTarget() { - GetD2DDeviceContext()->SetTarget(target_bitmap_.Get()); -} - -void WindowRenderTarget::Present() { - ThrowIfFailed(dxgi_swap_chain_->Present(1, 0)); -} - -void WindowRenderTarget::CreateTargetBitmap() { - // Direct2D needs the dxgi version of the backbuffer surface pointer. - ComPtr dxgiBackBuffer; - ThrowIfFailed(dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))); - - const auto dpi = graph_manager_->GetDpi(); - - auto bitmap_properties = D2D1::BitmapProperties1( - D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, - D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), - dpi.x, dpi.y); - - // Get a D2D surface from the DXGI back buffer to use as the D2D render - // target. - ThrowIfFailed( - graph_manager_->GetD2D1DeviceContext()->CreateBitmapFromDxgiSurface( - dxgiBackBuffer.Get(), &bitmap_properties, &target_bitmap_)); -} - -GraphManager* GraphManager::GetInstance() { - return Application::GetInstance()->ResolveSingleton( - [](auto) { return new GraphManager{}; }); -} - -GraphManager::GraphManager() { - UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - -#ifdef CRU_DEBUG - creation_flags |= D3D11_CREATE_DEVICE_DEBUG; -#endif - - const D3D_FEATURE_LEVEL feature_levels[] = { - D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1}; - - ThrowIfFailed(D3D11CreateDevice( - nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, creation_flags, - feature_levels, ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, - &d3d11_device_, nullptr, &d3d11_device_context_)); - - Microsoft::WRL::ComPtr dxgi_device; - - ThrowIfFailed(d3d11_device_.As(&dxgi_device)); - - ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory1), &d2d1_factory_)); - - ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); - - ThrowIfFailed(d2d1_device_->CreateDeviceContext( - D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1_device_context_)); - - // Identify the physical adapter (GPU or card) this device is runs on. - ComPtr dxgi_adapter; - ThrowIfFailed(dxgi_device->GetAdapter(&dxgi_adapter)); - - // Get the factory object that created the DXGI device. - ThrowIfFailed(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_))); - - ThrowIfFailed(DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), - reinterpret_cast(dwrite_factory_.GetAddressOf()))); - - dwrite_factory_->GetSystemFontCollection(&dwrite_system_font_collection_); -} - -GraphManager::~GraphManager() {} - -std::shared_ptr GraphManager::CreateWindowRenderTarget( - HWND hwnd) { - return std::make_shared(this, hwnd); -} - -Dpi GraphManager::GetDpi() const { - Dpi dpi; - d2d1_factory_->GetDesktopDpi(&dpi.x, &dpi.y); - return dpi; -} - -void GraphManager::ReloadSystemMetrics() { - ThrowIfFailed(d2d1_factory_->ReloadSystemMetrics()); -} - -Microsoft::WRL::ComPtr CreateSolidColorBrush( - const D2D1_COLOR_F& color) { - Microsoft::WRL::ComPtr brush; - ThrowIfFailed(GraphManager::GetInstance() - ->GetD2D1DeviceContext() - ->CreateSolidColorBrush(color, &brush)); - return brush; -} -} // namespace cru::graph diff --git a/src/graph/graph.hpp b/src/graph/graph.hpp deleted file mode 100644 index af14cc50..00000000 --- a/src/graph/graph.hpp +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once -#include "pre.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include "base.hpp" - -namespace cru::graph { -class GraphManager; - -// Represents a window render target. -class WindowRenderTarget : public Object { - public: - WindowRenderTarget(GraphManager* graph_manager, HWND hwnd); - WindowRenderTarget(const WindowRenderTarget& other) = delete; - WindowRenderTarget(WindowRenderTarget&& other) = delete; - WindowRenderTarget& operator=(const WindowRenderTarget& other) = delete; - WindowRenderTarget& operator=(WindowRenderTarget&& other) = delete; - ~WindowRenderTarget() override; - - public: - // Get the graph manager that created the render target. - GraphManager* GetGraphManager() const { return graph_manager_; } - - // Get the d2d device context. - inline Microsoft::WRL::ComPtr GetD2DDeviceContext() const; - - // Get the target bitmap which can be set as the ID2D1DeviceContext's target. - Microsoft::WRL::ComPtr GetTargetBitmap() const { - return target_bitmap_; - } - - // Resize the underlying buffer. - void ResizeBuffer(int width, int height); - - // Set this render target as the d2d device context's target. - void SetAsTarget(); - - // Present the data of the underlying buffer to the window. - void Present(); - - private: - void CreateTargetBitmap(); - - private: - GraphManager* graph_manager_; - Microsoft::WRL::ComPtr dxgi_swap_chain_; - Microsoft::WRL::ComPtr target_bitmap_; -}; - -struct Dpi { - float x; - float y; -}; - -class GraphManager final : public Object { - public: - static GraphManager* GetInstance(); - - private: - GraphManager(); - - public: - GraphManager(const GraphManager& other) = delete; - GraphManager(GraphManager&& other) = delete; - GraphManager& operator=(const GraphManager& other) = delete; - GraphManager& operator=(GraphManager&& other) = delete; - ~GraphManager() override; - - public: - Microsoft::WRL::ComPtr GetD2D1Factory() const { - return d2d1_factory_; - } - - Microsoft::WRL::ComPtr GetD2D1DeviceContext() const { - return d2d1_device_context_; - } - - Microsoft::WRL::ComPtr GetD3D11Device() const { - return d3d11_device_; - } - - Microsoft::WRL::ComPtr GetDxgiFactory() const { - return dxgi_factory_; - } - - Microsoft::WRL::ComPtr GetDWriteFactory() const { - return dwrite_factory_; - } - - // Create a window render target with the HWND. - std::shared_ptr CreateWindowRenderTarget(HWND hwnd); - - // Get the desktop dpi. - Dpi GetDpi() const; - - // Reload system metrics including desktop dpi. - void ReloadSystemMetrics(); - - Microsoft::WRL::ComPtr GetSystemFontCollection() - const { - return dwrite_system_font_collection_; - } - - private: - Microsoft::WRL::ComPtr d3d11_device_; - Microsoft::WRL::ComPtr d3d11_device_context_; - Microsoft::WRL::ComPtr d2d1_factory_; - Microsoft::WRL::ComPtr d2d1_device_; - Microsoft::WRL::ComPtr d2d1_device_context_; - Microsoft::WRL::ComPtr dxgi_factory_; - - Microsoft::WRL::ComPtr dwrite_factory_; - Microsoft::WRL::ComPtr dwrite_system_font_collection_; -}; - -inline int DipToPixelInternal(const float dip, const float dpi) { - return static_cast(dip * dpi / 96.0f); -} - -inline int DipToPixelX(const float dip_x) { - return DipToPixelInternal(dip_x, GraphManager::GetInstance()->GetDpi().x); -} - -inline int DipToPixelY(const float dip_y) { - return DipToPixelInternal(dip_y, GraphManager::GetInstance()->GetDpi().y); -} - -inline float DipToPixelInternal(const int pixel, const float dpi) { - return static_cast(pixel) * 96.0f / dpi; -} - -inline float PixelToDipX(const int pixel_x) { - return DipToPixelInternal(pixel_x, GraphManager::GetInstance()->GetDpi().x); -} - -inline float PixelToDipY(const int pixel_y) { - return DipToPixelInternal(pixel_y, GraphManager::GetInstance()->GetDpi().y); -} - -Microsoft::WRL::ComPtr -WindowRenderTarget::GetD2DDeviceContext() const { - return graph_manager_->GetD2D1DeviceContext(); -} - -inline void WithTransform( - ID2D1RenderTarget* device_context, const D2D1_MATRIX_3X2_F matrix, - const std::function& action) { - D2D1_MATRIX_3X2_F old_transform; - device_context->GetTransform(&old_transform); - device_context->SetTransform(old_transform * matrix); - action(device_context); - device_context->SetTransform(old_transform); -} - -Microsoft::WRL::ComPtr CreateSolidColorBrush( - const D2D1_COLOR_F& color); -} // namespace cru::graph diff --git a/src/graph/graph_manager.cpp b/src/graph/graph_manager.cpp new file mode 100644 index 00000000..ecc60915 --- /dev/null +++ b/src/graph/graph_manager.cpp @@ -0,0 +1,79 @@ +#include "graph_manager.hpp" + +#include +#include +#include +#include +#include + +#include "application.hpp" +#include "exception.hpp" +#include "util/com_util.hpp" +#include "window_render_target.hpp" + +namespace cru::graph { + +GraphManager* GraphManager::GetInstance() { + return Application::GetInstance()->ResolveSingleton( + [](auto) { return new GraphManager{}; }); +} + +GraphManager::GraphManager() { + UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + +#ifdef CRU_DEBUG + creation_flags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + const D3D_FEATURE_LEVEL feature_levels[] = { + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; + + Microsoft::WRL::ComPtr d3d11_device_context; + ID3D11Device* d3d11_device; + + ThrowIfFailed(D3D11CreateDevice( + nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, creation_flags, + feature_levels, ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, + &d3d11_device, nullptr, &d3d11_device_context)); + this->d3d11_device_ = util::CreateComSharedPtr(d3d11_device); + + Microsoft::WRL::ComPtr dxgi_device; + ThrowIfFailed(d3d11_device_->QueryInterface(dxgi_device.GetAddressOf())); + + ID2D1Factory1* d2d1_factory; + ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + IID_PPV_ARGS(&d2d1_factory))); + this->d2d1_factory_ = util::CreateComSharedPtr(d2d1_factory); + + Microsoft::WRL::ComPtr d2d1_device; + + ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device)); + + ID2D1DeviceContext* d2d1_device_context; + ThrowIfFailed(d2d1_device->CreateDeviceContext( + D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1_device_context)); + this->d2d1_device_context_ = util::CreateComSharedPtr(d2d1_device_context); + + // Identify the physical adapter (GPU or card) this device is runs on. + Microsoft::WRL::ComPtr dxgi_adapter; + ThrowIfFailed(dxgi_device->GetAdapter(&dxgi_adapter)); + + IDXGIFactory2* dxgi_factory; + // Get the factory object that created the DXGI device. + ThrowIfFailed(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory))); + this->dxgi_factory_ = util::CreateComSharedPtr(dxgi_factory); + + IDWriteFactory* dwrite_factory; + ThrowIfFailed( + DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), + reinterpret_cast(&dwrite_factory))); + this->dwrite_factory_ = util::CreateComSharedPtr(dwrite_factory); + + IDWriteFontCollection* font_collection; + ThrowIfFailed(dwrite_factory_->GetSystemFontCollection(&font_collection)); + this->dwrite_system_font_collection_ = + util::CreateComSharedPtr(font_collection); +} +} // namespace cru::graph diff --git a/src/graph/graph_manager.hpp b/src/graph/graph_manager.hpp new file mode 100644 index 00000000..4a1e7153 --- /dev/null +++ b/src/graph/graph_manager.hpp @@ -0,0 +1,60 @@ +#pragma once +#include "pre.hpp" + +#include + +#include "base.hpp" + +struct ID3D11Device; +struct ID3D11DeviceContext; +struct ID2D1Factory1; +struct ID2D1DeviceContext; +struct IDXGIFactory2; +struct IDWriteFontCollection; +struct IDWriteFactory; + +struct ID2D1RenderTarget; + +namespace cru::graph { +class WindowRenderTarget; + +class GraphManager final : public Object { + public: + static GraphManager* GetInstance(); + + private: + GraphManager(); + + public: + GraphManager(const GraphManager& other) = delete; + GraphManager(GraphManager&& other) = delete; + GraphManager& operator=(const GraphManager& other) = delete; + GraphManager& operator=(GraphManager&& other) = delete; + ~GraphManager() override = default; + + public: + ID2D1Factory1* GetD2D1Factory() const { return d2d1_factory_.get(); } + + ID2D1DeviceContext* GetD2D1DeviceContext() const { + return d2d1_device_context_.get(); + } + + ID3D11Device* GetD3D11Device() const { return d3d11_device_.get(); } + + IDXGIFactory2* GetDxgiFactory() const { return dxgi_factory_.get(); } + + IDWriteFactory* GetDWriteFactory() const { return dwrite_factory_.get(); } + + IDWriteFontCollection* GetSystemFontCollection() const { + return dwrite_system_font_collection_.get(); + } + + private: + std::shared_ptr d3d11_device_; + std::shared_ptr d2d1_factory_; + std::shared_ptr d2d1_device_context_; + std::shared_ptr dxgi_factory_; + std::shared_ptr dwrite_factory_; + std::shared_ptr dwrite_system_font_collection_; +}; +} // namespace cru::graph diff --git a/src/graph/graph_util.hpp b/src/graph/graph_util.hpp new file mode 100644 index 00000000..2d5be5f3 --- /dev/null +++ b/src/graph/graph_util.hpp @@ -0,0 +1,63 @@ +#pragma once +#include "pre.hpp" + +#include +#include + +#include "exception.hpp" +#include "graph_manager.hpp" + +namespace cru::graph { +struct Dpi { + float x; + float y; +}; + +inline Dpi GetDpi() { + Dpi dpi; + GraphManager::GetInstance()->GetD2D1Factory()->GetDesktopDpi(&dpi.x, &dpi.y); + return dpi; +} + +inline int DipToPixelInternal(const float dip, const float dpi) { + return static_cast(dip * dpi / 96.0f); +} + +inline int DipToPixelX(const float dip_x) { + return DipToPixelInternal(dip_x, GetDpi().x); +} + +inline int DipToPixelY(const float dip_y) { + return DipToPixelInternal(dip_y, GetDpi().y); +} + +inline float DipToPixelInternal(const int pixel, const float dpi) { + return static_cast(pixel) * 96.0f / dpi; +} + +inline float PixelToDipX(const int pixel_x) { + return DipToPixelInternal(pixel_x, GetDpi().x); +} + +inline float PixelToDipY(const int pixel_y) { + return DipToPixelInternal(pixel_y, GetDpi().y); +} + +inline void WithTransform( + ID2D1RenderTarget* render_target, const D2D1_MATRIX_3X2_F matrix, + const std::function& action) { + D2D1_MATRIX_3X2_F old_transform; + render_target->GetTransform(&old_transform); + render_target->SetTransform(old_transform * matrix); + action(render_target); + render_target->SetTransform(old_transform); +} + +inline ID2D1SolidColorBrush* CreateSolidColorBrush(const D2D1_COLOR_F& color) { + ID2D1SolidColorBrush* brush; + ThrowIfFailed(GraphManager::GetInstance() + ->GetD2D1DeviceContext() + ->CreateSolidColorBrush(color, &brush)); + return brush; +} +} // namespace cru::graph diff --git a/src/graph/window_render_target.cpp b/src/graph/window_render_target.cpp new file mode 100644 index 00000000..d110101a --- /dev/null +++ b/src/graph/window_render_target.cpp @@ -0,0 +1,99 @@ +#include "window_render_target.hpp" + +#include +#include +#include +#include + +#include "exception.hpp" +#include "graph_manager.hpp" +#include "graph_util.hpp" +#include "util/com_util.hpp" + +namespace cru::graph { +WindowRenderTarget::WindowRenderTarget(GraphManager* graph_manager, HWND hwnd) { + this->graph_manager_ = graph_manager; + + const auto d3d11_device = graph_manager->GetD3D11Device(); + const auto dxgi_factory = graph_manager->GetDxgiFactory(); + + // Allocate a descriptor. + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {0}; + swap_chain_desc.Width = 0; // use automatic sizing + swap_chain_desc.Height = 0; + swap_chain_desc.Format = + DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format + swap_chain_desc.Stereo = false; + swap_chain_desc.SampleDesc.Count = 1; // don't use multi-sampling + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.BufferCount = 2; // use double buffering to enable flip + swap_chain_desc.Scaling = DXGI_SCALING_NONE; + swap_chain_desc.SwapEffect = + DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all apps must use this SwapEffect + swap_chain_desc.Flags = 0; + + IDXGISwapChain1* dxgi_swap_chain; + // Get the final swap chain for this window from the DXGI factory. + ThrowIfFailed( + dxgi_factory->CreateSwapChainForHwnd(d3d11_device, hwnd, &swap_chain_desc, + nullptr, nullptr, &dxgi_swap_chain)); + this->dxgi_swap_chain_ = util::CreateComSharedPtr(dxgi_swap_chain); + + CreateTargetBitmap(); +} + +void WindowRenderTarget::ResizeBuffer(const int width, const int height) { + const auto graph_manager = graph_manager_; + const auto d2d1_device_context = graph_manager->GetD2D1DeviceContext(); + + ID2D1Image* old_target; + d2d1_device_context->GetTarget(&old_target); + const auto target_this = old_target == this->target_bitmap_.get(); + if (target_this) d2d1_device_context->SetTarget(nullptr); + + util::SafeRelease(old_target); + target_bitmap_.reset(); + + ThrowIfFailed(dxgi_swap_chain_->ResizeBuffers(0, width, height, + DXGI_FORMAT_UNKNOWN, 0)); + + CreateTargetBitmap(); + + if (target_this) d2d1_device_context->SetTarget(target_bitmap_.get()); +} + +void WindowRenderTarget::SetAsTarget() { + graph_manager_->GetD2D1DeviceContext()->SetTarget(target_bitmap_.get()); +} + +void WindowRenderTarget::Present() { + ThrowIfFailed(dxgi_swap_chain_->Present(1, 0)); +} + +void WindowRenderTarget::CreateTargetBitmap() { + assert(target_bitmap_ == nullptr); // target bitmap must not exist. + + // Direct2D needs the dxgi version of the backbuffer surface pointer. + Microsoft::WRL::ComPtr dxgi_back_buffer; + ThrowIfFailed( + dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgi_back_buffer))); + + const auto dpi = GetDpi(); + + auto bitmap_properties = D2D1::BitmapProperties1( + D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), + dpi.x, dpi.y); + + ID2D1Bitmap1* bitmap; + // Get a D2D surface from the DXGI back buffer to use as the D2D render + // target. + ThrowIfFailed( + graph_manager_->GetD2D1DeviceContext()->CreateBitmapFromDxgiSurface( + dxgi_back_buffer.Get(), &bitmap_properties, &bitmap)); + this->target_bitmap_ = util::CreateComSharedPtr(bitmap); + + dxgi_back_buffer->Release(); +} +} // namespace cru::graph diff --git a/src/graph/window_render_target.hpp b/src/graph/window_render_target.hpp new file mode 100644 index 00000000..9b93df19 --- /dev/null +++ b/src/graph/window_render_target.hpp @@ -0,0 +1,49 @@ +#pragma once +#include "pre.hpp" + +#include +#include + +#include "base.hpp" + +struct IDXGISwapChain1; +struct ID2D1Bitmap1; + +namespace cru::graph { +class GraphManager; + +// Represents a window render target. +class WindowRenderTarget : public Object { + public: + WindowRenderTarget(GraphManager* graph_manager, HWND hwnd); + WindowRenderTarget(const WindowRenderTarget& other) = delete; + WindowRenderTarget(WindowRenderTarget&& other) = delete; + WindowRenderTarget& operator=(const WindowRenderTarget& other) = delete; + WindowRenderTarget& operator=(WindowRenderTarget&& other) = delete; + ~WindowRenderTarget() override = default; + + public: + // Get the graph manager that created the render target. + GraphManager* GetGraphManager() const { return graph_manager_; } + + // Get the target bitmap which can be set as the ID2D1DeviceContext's target. + ID2D1Bitmap1* GetTargetBitmap() const { return target_bitmap_.get(); } + + // Resize the underlying buffer. + void ResizeBuffer(int width, int height); + + // Set this render target as the d2d device context's target. + void SetAsTarget(); + + // Present the data of the underlying buffer to the window. + void Present(); + + private: + void CreateTargetBitmap(); + + private: + GraphManager* graph_manager_; + std::shared_ptr dxgi_swap_chain_; + std::shared_ptr target_bitmap_; +}; +} // namespace cru::graph diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp index e69de29b..5d4e15cc 100644 --- a/src/ui/controls/button.cpp +++ b/src/ui/controls/button.cpp @@ -0,0 +1,13 @@ +#include "button.hpp" + +#include "ui/ui_manager.hpp" +#include "ui/render/border_render_object.hpp" + +namespace cru::ui::controls { +Button::Button() { + const auto predefined_resource = + UiManager::GetInstance()->GetPredefineResources(); + render_object_ = new render::BorderRenderObject(); } + +void Button::OnChildChanged(Control* old_child, Control* new_child) {} +} // namespace cru::ui::controls diff --git a/src/ui/controls/button.hpp b/src/ui/controls/button.hpp index 010c3f5b..27e7fc7b 100644 --- a/src/ui/controls/button.hpp +++ b/src/ui/controls/button.hpp @@ -1,8 +1,35 @@ #pragma once #include "pre.hpp" -#include "ui/control.hpp" +#include "ui/content_control.hpp" + +namespace cru::ui::render { +class BorderRenderObject; +} namespace cru::ui::controls { +class Button : public ContentControl { + public: + static constexpr auto control_type = L"Button"; -} + static Button* Create(); + + protected: + Button(); + + public: + Button(const Button& other) = delete; + Button(Button&& other) = delete; + Button& operator=(const Button& other) = delete; + Button& operator=(Button&& other) = delete; + ~Button(); + + render::RenderObject* GetRenderObject() const override; + + protected: + void OnChildChanged(Control* old_child, Control* new_child) override; + + private: + render::BorderRenderObject* render_object_; +}; +} // namespace cru::ui::controls diff --git a/src/ui/controls/text_block.cpp b/src/ui/controls/text_block.cpp index 85116910..c891b832 100644 --- a/src/ui/controls/text_block.cpp +++ b/src/ui/controls/text_block.cpp @@ -1,7 +1,5 @@ #include "text_block.hpp" -#include - #include "ui/render/text_render_object.hpp" #include "ui/ui_manager.hpp" diff --git a/src/ui/render/border_render_object.cpp b/src/ui/render/border_render_object.cpp index 033f59d8..e1550665 100644 --- a/src/ui/render/border_render_object.cpp +++ b/src/ui/render/border_render_object.cpp @@ -1,16 +1,45 @@ #include "border_render_object.hpp" +#include +#include #include #include "cru_debug.hpp" #include "exception.hpp" -#include "graph/graph.hpp" +#include "graph/graph_manager.hpp" +#include "graph/graph_util.hpp" +#include "util/com_util.hpp" namespace cru::ui::render { -BorderRenderObject::BorderRenderObject(Microsoft::WRL::ComPtr brush) - : border_brush_(std::move(brush)) {} +BorderRenderObject::BorderRenderObject(ID2D1Brush* brush) { + assert(brush); + brush->AddRef(); + this->border_brush_ = brush; + try { + RecreateGeometry(); + } catch (...) { + brush->Release(); + throw; + } +} + +BorderRenderObject::~BorderRenderObject() { + util::SafeRelease(border_brush_); + util::SafeRelease(geometry_); + util::SafeRelease(border_outer_geometry_); +} + +void BorderRenderObject::SetBrush(ID2D1Brush* new_brush) { + assert(new_brush); + util::SafeRelease(border_brush_); + new_brush->AddRef(); + border_brush_ = new_brush; +} void BorderRenderObject::RecreateGeometry() { + util::SafeRelease(geometry_); + util::SafeRelease(border_outer_geometry_); + const auto d2d_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); Microsoft::WRL::ComPtr geometry; @@ -19,8 +48,8 @@ void BorderRenderObject::RecreateGeometry() { Microsoft::WRL::ComPtr border_outer_geometry; ThrowIfFailed(d2d_factory->CreatePathGeometry(&border_outer_geometry)); - ID2D1GeometrySink* sink; - auto f = [sink](const Rect& rect, const CornerRadius& corner) { + Microsoft::WRL::ComPtr sink; + auto f = [&sink](const Rect& rect, const CornerRadius& corner) { sink->BeginFigure(D2D1::Point2F(rect.left + corner.left_top.x, rect.top), D2D1_FIGURE_BEGIN_FILLED); sink->AddLine( @@ -53,21 +82,21 @@ void BorderRenderObject::RecreateGeometry() { ThrowIfFailed(border_outer_geometry->Open(&sink)); f(outer_rect, corner_radius_); ThrowIfFailed(sink->Close()); - sink->Release(); + sink = nullptr; const Rect inner_rect = outer_rect.Shrink(border_thickness_); ThrowIfFailed(geometry->Open(&sink)); f(outer_rect, corner_radius_); f(inner_rect, corner_radius_); ThrowIfFailed(sink->Close()); - sink->Release(); + sink = nullptr; - geometry_ = std::move(geometry); - border_outer_geometry_ = std::move(border_outer_geometry); + geometry_ = geometry.Detach(); + border_outer_geometry_ = border_outer_geometry.Detach(); } void BorderRenderObject::Draw(ID2D1RenderTarget* render_target) { - render_target->FillGeometry(geometry_.Get(), border_brush_.Get()); + render_target->FillGeometry(geometry_, border_brush_); if (const auto child = GetChild()) { auto offset = child->GetOffset(); graph::WithTransform(render_target, @@ -167,7 +196,8 @@ void BorderRenderObject::OnLayoutCore(const Rect& rect) { } if (coerced_content_available_size.height < 0) { debug::DebugMessage( - L"Layout: vertical length of padding, border and margin is bigger than " + L"Layout: vertical length of padding, border and margin is bigger " + L"than " L"available length."); coerced_content_available_size.height = 0; } diff --git a/src/ui/render/border_render_object.hpp b/src/ui/render/border_render_object.hpp index d6effc21..eccb1219 100644 --- a/src/ui/render/border_render_object.hpp +++ b/src/ui/render/border_render_object.hpp @@ -28,20 +28,18 @@ struct CornerRadius { class BorderRenderObject : public RenderObject { public: - explicit BorderRenderObject(Microsoft::WRL::ComPtr brush); + explicit BorderRenderObject(ID2D1Brush* brush); BorderRenderObject(const BorderRenderObject& other) = delete; BorderRenderObject(BorderRenderObject&& other) = delete; BorderRenderObject& operator=(const BorderRenderObject& other) = delete; BorderRenderObject& operator=(BorderRenderObject&& other) = delete; - ~BorderRenderObject() override = default; + ~BorderRenderObject() override; bool IsEnabled() const { return is_enabled_; } void SetEnabled(bool enabled) { is_enabled_ = enabled; } - Microsoft::WRL::ComPtr GetBrush() const { return border_brush_; } - void SetBrush(const Microsoft::WRL::ComPtr new_brush) { - border_brush_ = std::move(new_brush); - } + ID2D1Brush* GetBrush() const { return border_brush_; } + void SetBrush(ID2D1Brush* new_brush); Thickness GetBorderWidth() const { return border_thickness_; } void SetBorderWidth(const Thickness& thickness) { border_thickness_ = thickness; } @@ -51,11 +49,11 @@ class BorderRenderObject : public RenderObject { corner_radius_ = new_corner_radius; } - void RecreateGeometry(); // TODO + void RecreateGeometry(); - void Draw(ID2D1RenderTarget* render_target) override; // TODO + void Draw(ID2D1RenderTarget* render_target) override; - RenderObject* HitTest(const Point& point) override; // TODO + RenderObject* HitTest(const Point& point) override; protected: void OnAddChild(RenderObject* new_child, int position) override; @@ -73,11 +71,11 @@ class BorderRenderObject : public RenderObject { private: bool is_enabled_ = false; - Microsoft::WRL::ComPtr border_brush_; + ID2D1Brush* border_brush_ = nullptr; Thickness border_thickness_ = Thickness::Zero(); CornerRadius corner_radius_{}; - Microsoft::WRL::ComPtr geometry_{nullptr}; - Microsoft::WRL::ComPtr border_outer_geometry_{nullptr}; + ID2D1Geometry* geometry_ = nullptr; + ID2D1Geometry* border_outer_geometry_ = nullptr; }; } // namespace cru::ui::render diff --git a/src/ui/render/flex_layout_render_object.cpp b/src/ui/render/flex_layout_render_object.cpp index 4708d187..e4d327f1 100644 --- a/src/ui/render/flex_layout_render_object.cpp +++ b/src/ui/render/flex_layout_render_object.cpp @@ -4,7 +4,7 @@ #include #include "cru_debug.hpp" -#include "graph/graph.hpp" +#include "graph/graph_util.hpp" namespace cru::ui::render { FlexChildLayoutData* FlexLayoutRenderObject::GetChildLayoutData(int position) { diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index f56baa8f..232eda41 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -1,8 +1,5 @@ #include "render_object.hpp" -#include -#include - #include "cru_debug.hpp" namespace cru::ui::render { diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index 51b0c3ae..abdda605 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -1,7 +1,6 @@ #pragma once #include "pre.hpp" -#include #include #include "base.hpp" @@ -9,7 +8,6 @@ // forward declarations struct ID2D1RenderTarget; - namespace cru::ui { class Control; } diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp index e8967d48..b66fffa3 100644 --- a/src/ui/render/text_render_object.cpp +++ b/src/ui/render/text_render_object.cpp @@ -5,19 +5,61 @@ #include #include "exception.hpp" -#include "graph/graph.hpp" +#include "graph/graph_manager.hpp" +#include "graph/graph_util.hpp" +#include "util/com_util.hpp" namespace cru::ui::render { -TextRenderObject::TextRenderObject( - Microsoft::WRL::ComPtr brush, - Microsoft::WRL::ComPtr format, - Microsoft::WRL::ComPtr selection_brush) - : brush_(std::move(brush)), - text_format_(std::move(format)), - selection_brush_(std::move(selection_brush)) { +TextRenderObject::TextRenderObject(ID2D1Brush* brush, IDWriteTextFormat* format, + ID2D1Brush* selection_brush) { + assert(brush); + assert(format); + 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_); +} + +void TextRenderObject::SetBrush(ID2D1Brush* new_brush) { + assert(new_brush); + util::SafeRelease(brush_); + new_brush->AddRef(); + brush_ = new_brush; +} + +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(); } +void TextRenderObject::SetSelectionBrush(ID2D1Brush* new_brush) { + assert(new_brush); + util::SafeRelease(selection_brush_); + new_brush->AddRef(); + selection_brush_ = new_brush; +} + namespace { void DrawSelectionRect(ID2D1RenderTarget* render_target, IDWriteTextLayout* layout, ID2D1Brush* brush, @@ -54,9 +96,8 @@ void TextRenderObject::Draw(ID2D1RenderTarget* render_target) { D2D1::Matrix3x2F::Translation(GetMargin().left + GetPadding().left, GetMargin().top + GetPadding().top), [this](auto rt) { - DrawSelectionRect(rt, text_layout_.Get(), selection_brush_.Get(), - selection_range_); - rt->DrawTextLayout(D2D1::Point2F(), text_layout_.Get(), brush_.Get()); + DrawSelectionRect(rt, text_layout_, selection_brush_, selection_range_); + rt->DrawTextLayout(D2D1::Point2F(), text_layout_, brush_); }); } @@ -99,7 +140,7 @@ void TextRenderObject::OnLayoutContent(const Rect& content_rect) {} void TextRenderObject::RecreateTextLayout() { assert(text_format_ != nullptr); - text_layout_ = nullptr; // release last one + util::SafeRelease(text_layout_); const auto dwrite_factory = graph::GraphManager::GetInstance()->GetDWriteFactory(); @@ -107,7 +148,7 @@ void TextRenderObject::RecreateTextLayout() { const auto&& size = GetSize(); ThrowIfFailed(dwrite_factory->CreateTextLayout( - text_.c_str(), static_cast(text_.size()), text_format_.Get(), + text_.c_str(), static_cast(text_.size()), text_format_, size.width, size.height, &text_layout_)); } } // namespace cru::ui::render diff --git a/src/ui/render/text_render_object.hpp b/src/ui/render/text_render_object.hpp index ac874b75..30d78736 100644 --- a/src/ui/render/text_render_object.hpp +++ b/src/ui/render/text_render_object.hpp @@ -1,8 +1,6 @@ #pragma once #include "pre.hpp" -#include // for ComPtr - #include "render_object.hpp" // forward declarations @@ -13,14 +11,13 @@ struct IDWriteTextLayout; namespace cru::ui::render { class TextRenderObject : public RenderObject { public: - TextRenderObject(Microsoft::WRL::ComPtr brush, - Microsoft::WRL::ComPtr format, - Microsoft::WRL::ComPtr selection_brush); + TextRenderObject(ID2D1Brush* brush, IDWriteTextFormat* format, + ID2D1Brush* selection_brush); TextRenderObject(const TextRenderObject& other) = delete; TextRenderObject(TextRenderObject&& other) = delete; TextRenderObject& operator=(const TextRenderObject& other) = delete; TextRenderObject& operator=(TextRenderObject&& other) = delete; - ~TextRenderObject() override = default; + ~TextRenderObject() override; String GetText() const { return text_; } void SetText(String new_text) { @@ -28,19 +25,11 @@ class TextRenderObject : public RenderObject { RecreateTextLayout(); } - Microsoft::WRL::ComPtr GetBrush() const { return brush_; } - void SetBrush(Microsoft::WRL::ComPtr new_brush) { - brush_ = std::move(new_brush); - } + ID2D1Brush* GetBrush() const { return brush_; } + void SetBrush(ID2D1Brush* new_brush); - Microsoft::WRL::ComPtr GetTextFormat() const { - return text_format_; - } - void SetTextFormat( - Microsoft::WRL::ComPtr new_text_format) { - text_format_ = std::move(new_text_format); - RecreateTextLayout(); - } + IDWriteTextFormat* GetTextFormat() const { return text_format_; } + void SetTextFormat(IDWriteTextFormat* new_text_format); std::optional GetSelectionRange() const { return selection_range_; @@ -49,12 +38,10 @@ class TextRenderObject : public RenderObject { selection_range_ = std::move(new_range); } - Microsoft::WRL::ComPtr GetSelectionBrush() const { + ID2D1Brush* GetSelectionBrush() const { return selection_brush_; } - void SetSelectionBrush(Microsoft::WRL::ComPtr new_brush) { - selection_brush_ = std::move(new_brush); - } + void SetSelectionBrush(ID2D1Brush* new_brush); void Draw(ID2D1RenderTarget* render_target) override; @@ -72,11 +59,11 @@ class TextRenderObject : public RenderObject { private: String text_; - Microsoft::WRL::ComPtr brush_; - Microsoft::WRL::ComPtr text_format_; - Microsoft::WRL::ComPtr text_layout_; + ID2D1Brush* brush_; + IDWriteTextFormat* text_format_; + IDWriteTextLayout* text_layout_; std::optional selection_range_ = std::nullopt; - Microsoft::WRL::ComPtr selection_brush_; + ID2D1Brush* selection_brush_; }; } // namespace cru::ui::render diff --git a/src/ui/render/window_render_object.cpp b/src/ui/render/window_render_object.cpp index f198c2fa..44c3c426 100644 --- a/src/ui/render/window_render_object.cpp +++ b/src/ui/render/window_render_object.cpp @@ -1,6 +1,6 @@ #include "window_render_object.hpp" -#include "graph/graph.hpp" +#include "graph/graph_util.hpp" #include "ui/window.hpp" namespace cru::ui::render { diff --git a/src/ui/ui_manager.cpp b/src/ui/ui_manager.cpp index 26b1fe62..add77516 100644 --- a/src/ui/ui_manager.cpp +++ b/src/ui/ui_manager.cpp @@ -1,10 +1,14 @@ #include "ui_manager.hpp" #include +#include +#include #include "application.hpp" #include "exception.hpp" -#include "graph/graph.hpp" +#include "graph/graph_manager.hpp" +#include "graph/graph_util.hpp" +#include "util/com_util.hpp" namespace cru::ui { namespace { @@ -17,19 +21,10 @@ void GetSystemCaretInfo(CaretInfo* caret_info) { caret_info->half_caret_width = caret_width / 2.0f; } -Microsoft::WRL::ComPtr CreateSolidBrush( - graph::GraphManager* graph_manager, const D2D1_COLOR_F& color) { - const auto device_context = graph_manager->GetD2D1DeviceContext(); - Microsoft::WRL::ComPtr solid_color_brush; - device_context->CreateSolidColorBrush(color, &solid_color_brush); - return solid_color_brush; -} - -Microsoft::WRL::ComPtr CreateDefaultTextFormat( - graph::GraphManager* graph_manager) { - const auto dwrite_factory = graph_manager->GetDWriteFactory(); - - Microsoft::WRL::ComPtr text_format; +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, @@ -43,26 +38,32 @@ Microsoft::WRL::ComPtr CreateDefaultTextFormat( } } // namespace -PredefineResources::PredefineResources(graph::GraphManager* graph_manager) - : text_block_selection_brush{CreateSolidBrush( - graph_manager, D2D1::ColorF(D2D1::ColorF::LightSkyBlue))}, - text_block_text_brush{ - CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Black))}, - text_block_text_format{CreateDefaultTextFormat(graph_manager)}, - debug_layout_out_border_brush{ - CreateSolidBrush(graph_manager, D2D1::ColorF(D2D1::ColorF::Crimson))}, - debug_layout_margin_brush{CreateSolidBrush( - graph_manager, D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f))}, - debug_layout_padding_brush{CreateSolidBrush( - graph_manager, D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f))} {} +PredefineResources::PredefineResources() { + try { + 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(text_block_selection_brush); + util::SafeRelease(text_block_text_brush); + util::SafeRelease(text_block_text_format); + } +} + +PredefineResources::~PredefineResources() { + util::SafeRelease(text_block_selection_brush); + util::SafeRelease(text_block_text_brush); + util::SafeRelease(text_block_text_format); +} UiManager* UiManager::GetInstance() { return Application::GetInstance()->ResolveSingleton( [](auto) { return new UiManager{}; }); } -UiManager::UiManager() - : predefine_resources_(graph::GraphManager::GetInstance()) { +UiManager::UiManager() : predefine_resources_() { GetSystemCaretInfo(&caret_info_); } } // namespace cru::ui diff --git a/src/ui/ui_manager.hpp b/src/ui/ui_manager.hpp index c2331dd4..b736381d 100644 --- a/src/ui/ui_manager.hpp +++ b/src/ui/ui_manager.hpp @@ -18,22 +18,17 @@ struct CaretInfo { class PredefineResources : public Object { public: - explicit PredefineResources(graph::GraphManager* graph_manager); + PredefineResources(); PredefineResources(const PredefineResources& other) = delete; PredefineResources(PredefineResources&& other) = delete; PredefineResources& operator=(const PredefineResources& other) = delete; PredefineResources& operator=(PredefineResources&& other) = delete; - ~PredefineResources() override = default; + ~PredefineResources() override; // region TextBlock - Microsoft::WRL::ComPtr text_block_selection_brush; - Microsoft::WRL::ComPtr text_block_text_brush; - Microsoft::WRL::ComPtr text_block_text_format; - - // region debug - Microsoft::WRL::ComPtr debug_layout_out_border_brush; - Microsoft::WRL::ComPtr debug_layout_margin_brush; - Microsoft::WRL::ComPtr debug_layout_padding_brush; + ID2D1Brush* text_block_selection_brush = nullptr; + ID2D1Brush* text_block_text_brush = nullptr; + IDWriteTextFormat* text_block_text_format = nullptr; }; class UiManager : public Object { diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 7b00ca05..90add552 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -1,10 +1,13 @@ #include "window.hpp" #include +#include #include "application.hpp" #include "exception.hpp" -#include "graph/graph.hpp" +#include "graph/graph_manager.hpp" +#include "graph/graph_util.hpp" +#include "graph/window_render_target.hpp" #include "render/window_render_object.hpp" namespace cru::ui { @@ -207,10 +210,9 @@ void Window::BeforeCreateHwnd() { window_ = this; } void Window::AfterCreateHwnd(WindowManager* window_manager) { window_manager->RegisterWindow(hwnd_, this); - render_target_ = - graph::GraphManager::GetInstance()->CreateWindowRenderTarget(hwnd_); + render_target_.reset(new graph::WindowRenderTarget(graph::GraphManager::GetInstance(), hwnd_)); - render_object_ = new render::WindowRenderObject(this); + render_object_.reset(new render::WindowRenderObject(this)); } Window::~Window() { @@ -220,12 +222,11 @@ Window::~Window() { } TraverseDescendants( [this](Control* control) { control->OnDetachToWindow(this); }); - delete render_object_; } StringView Window::GetControlType() const { return control_type; } -render::RenderObject* Window::GetRenderObject() const { return render_object_; } +render::RenderObject* Window::GetRenderObject() const { return render_object_.get(); } void Window::SetDeleteThisOnDestroy(bool value) { delete_this_on_destroy_ = value; @@ -556,17 +557,12 @@ void Window::OnDestroyInternal() { void Window::OnPaintInternal() { render_target_->SetAsTarget(); - auto device_context = render_target_->GetD2DDeviceContext(); - + auto device_context = render_target_->GetGraphManager()->GetD2D1DeviceContext(); device_context->BeginDraw(); - // Clear the background. device_context->Clear(D2D1::ColorF(D2D1::ColorF::White)); - - render_object_->Draw(device_context.Get()); - + render_object_->Draw(device_context); ThrowIfFailed(device_context->EndDraw(), "Failed to draw window."); - render_target_->Present(); ValidateRect(hwnd_, nullptr); diff --git a/src/ui/window.hpp b/src/ui/window.hpp index 1c48bf43..dd7631d0 100644 --- a/src/ui/window.hpp +++ b/src/ui/window.hpp @@ -221,8 +221,7 @@ class Window final : public ContentControl { HWND hwnd_ = nullptr; Window* parent_window_ = nullptr; std::shared_ptr render_target_{}; - - render::WindowRenderObject* render_object_; + std::shared_ptr render_object_{}; Control* mouse_hover_control_ = nullptr; diff --git a/src/util/com_util.hpp b/src/util/com_util.hpp new file mode 100644 index 00000000..bbaf1c27 --- /dev/null +++ b/src/util/com_util.hpp @@ -0,0 +1,22 @@ +#pragma once +#include "pre.hpp" + +#include + +namespace cru::util { + +template +std::shared_ptr CreateComSharedPtr(TInterface* p) { + return std::shared_ptr(p, [](TInterface* ptr) { + if (ptr != nullptr) ptr->Release(); + }); +} + +template +void SafeRelease(TInterface*& p) { + if (p != nullptr) { + p->Release(); + p = nullptr; + } +} +} // namespace cru::util -- cgit v1.2.3 From b506c349b793f723df6da170ffa0f412200e26e9 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 24 Mar 2019 21:59:29 +0800 Subject: ... --- src/ui/render/border_render_object.cpp | 120 ++++++++++++++++----------------- src/ui/render/border_render_object.hpp | 4 +- src/ui/render/render_object.cpp | 11 +++ src/ui/render/render_object.hpp | 2 + src/ui/render/text_render_object.cpp | 14 ++-- src/ui/render/text_render_object.hpp | 6 +- 6 files changed, 83 insertions(+), 74 deletions(-) (limited to 'src/ui/render/render_object.cpp') diff --git a/src/ui/render/border_render_object.cpp b/src/ui/render/border_render_object.cpp index 49700869..72cea756 100644 --- a/src/ui/render/border_render_object.cpp +++ b/src/ui/render/border_render_object.cpp @@ -36,66 +36,6 @@ void BorderRenderObject::SetBrush(ID2D1Brush* new_brush) { border_brush_ = new_brush; } -void BorderRenderObject::RecreateGeometry() { - util::SafeRelease(geometry_); - util::SafeRelease(border_outer_geometry_); - - const auto d2d_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); - - Microsoft::WRL::ComPtr geometry; - ThrowIfFailed(d2d_factory->CreatePathGeometry(&geometry)); - - Microsoft::WRL::ComPtr border_outer_geometry; - ThrowIfFailed(d2d_factory->CreatePathGeometry(&border_outer_geometry)); - - Microsoft::WRL::ComPtr sink; - auto f = [&sink](const Rect& rect, const CornerRadius& corner) { - sink->BeginFigure(D2D1::Point2F(rect.left + corner.left_top.x, rect.top), - D2D1_FIGURE_BEGIN_FILLED); - sink->AddLine( - D2D1::Point2F(rect.GetRight() - corner.right_top.x, rect.top)); - sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment( - D2D1::Point2F(rect.GetRight(), rect.top), - D2D1::Point2F(rect.GetRight(), rect.top + corner.right_top.y))); - sink->AddLine(D2D1::Point2F(rect.GetRight(), - rect.GetBottom() - corner.right_bottom.y)); - sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment( - D2D1::Point2F(rect.GetRight(), rect.GetBottom()), - D2D1::Point2F(rect.GetRight() - corner.right_bottom.x, - rect.GetBottom()))); - sink->AddLine( - D2D1::Point2F(rect.left + corner.left_bottom.x, rect.GetBottom())); - sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment( - D2D1::Point2F(rect.left, rect.GetBottom()), - D2D1::Point2F(rect.left, rect.GetBottom() - corner.left_bottom.y))); - sink->AddLine(D2D1::Point2F(rect.left, rect.top + corner.left_top.y)); - sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment( - D2D1::Point2F(rect.left, rect.top), - D2D1::Point2F(rect.left + corner.left_top.x, rect.top))); - sink->EndFigure(D2D1_FIGURE_END_CLOSED); - }; - - const auto size = GetSize(); - const auto margin = GetMargin(); - const Rect outer_rect{margin.left, margin.top, - size.width - margin.GetHorizontalTotal(), - size.height - margin.GetVerticalTotal()}; - ThrowIfFailed(border_outer_geometry->Open(&sink)); - f(outer_rect, corner_radius_); - ThrowIfFailed(sink->Close()); - sink = nullptr; - - const Rect inner_rect = outer_rect.Shrink(border_thickness_); - ThrowIfFailed(geometry->Open(&sink)); - f(outer_rect, corner_radius_); - f(inner_rect, corner_radius_); - ThrowIfFailed(sink->Close()); - sink = nullptr; - - geometry_ = geometry.Detach(); - border_outer_geometry_ = border_outer_geometry.Detach(); -} - void BorderRenderObject::Draw(ID2D1RenderTarget* render_target) { render_target->FillGeometry(geometry_, border_brush_); if (const auto child = GetChild()) { @@ -231,4 +171,64 @@ void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { child->Layout(content_rect); } } + +void BorderRenderObject::RecreateGeometry() { + util::SafeRelease(geometry_); + util::SafeRelease(border_outer_geometry_); + + const auto d2d_factory = graph::GraphManager::GetInstance()->GetD2D1Factory(); + + Microsoft::WRL::ComPtr geometry; + ThrowIfFailed(d2d_factory->CreatePathGeometry(&geometry)); + + Microsoft::WRL::ComPtr border_outer_geometry; + ThrowIfFailed(d2d_factory->CreatePathGeometry(&border_outer_geometry)); + + Microsoft::WRL::ComPtr sink; + auto f = [&sink](const Rect& rect, const CornerRadius& corner) { + sink->BeginFigure(D2D1::Point2F(rect.left + corner.left_top.x, rect.top), + D2D1_FIGURE_BEGIN_FILLED); + sink->AddLine( + D2D1::Point2F(rect.GetRight() - corner.right_top.x, rect.top)); + sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment( + D2D1::Point2F(rect.GetRight(), rect.top), + D2D1::Point2F(rect.GetRight(), rect.top + corner.right_top.y))); + sink->AddLine(D2D1::Point2F(rect.GetRight(), + rect.GetBottom() - corner.right_bottom.y)); + sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment( + D2D1::Point2F(rect.GetRight(), rect.GetBottom()), + D2D1::Point2F(rect.GetRight() - corner.right_bottom.x, + rect.GetBottom()))); + sink->AddLine( + D2D1::Point2F(rect.left + corner.left_bottom.x, rect.GetBottom())); + sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment( + D2D1::Point2F(rect.left, rect.GetBottom()), + D2D1::Point2F(rect.left, rect.GetBottom() - corner.left_bottom.y))); + sink->AddLine(D2D1::Point2F(rect.left, rect.top + corner.left_top.y)); + sink->AddQuadraticBezier(D2D1::QuadraticBezierSegment( + D2D1::Point2F(rect.left, rect.top), + D2D1::Point2F(rect.left + corner.left_top.x, rect.top))); + sink->EndFigure(D2D1_FIGURE_END_CLOSED); + }; + + const auto size = GetSize(); + const auto margin = GetMargin(); + const Rect outer_rect{margin.left, margin.top, + size.width - margin.GetHorizontalTotal(), + size.height - margin.GetVerticalTotal()}; + ThrowIfFailed(border_outer_geometry->Open(&sink)); + f(outer_rect, corner_radius_); + ThrowIfFailed(sink->Close()); + sink = nullptr; + + const Rect inner_rect = outer_rect.Shrink(border_thickness_); + ThrowIfFailed(geometry->Open(&sink)); + f(outer_rect, corner_radius_); + f(inner_rect, corner_radius_); + ThrowIfFailed(sink->Close()); + sink = nullptr; + + geometry_ = geometry.Detach(); + border_outer_geometry_ = border_outer_geometry.Detach(); +} } // namespace cru::ui::render diff --git a/src/ui/render/border_render_object.hpp b/src/ui/render/border_render_object.hpp index 6f9a8c11..80db648a 100644 --- a/src/ui/render/border_render_object.hpp +++ b/src/ui/render/border_render_object.hpp @@ -56,7 +56,7 @@ class BorderRenderObject : public RenderObject { corner_radius_ = new_corner_radius; } - void RecreateGeometry(); + void Refresh() { RecreateGeometry(); } void Draw(ID2D1RenderTarget* render_target) override; @@ -77,6 +77,8 @@ class BorderRenderObject : public RenderObject { return GetChildren().empty() ? nullptr : GetChildren()[0]; } + void RecreateGeometry(); + private: bool is_enabled_ = false; diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp index 232eda41..5c6af580 100644 --- a/src/ui/render/render_object.cpp +++ b/src/ui/render/render_object.cpp @@ -1,5 +1,7 @@ #include "render_object.hpp" +#include + #include "cru_debug.hpp" namespace cru::ui::render { @@ -96,6 +98,15 @@ void RenderObject::OnLayoutCore(const Rect& rect) { coerced_content_available_size.height}); } +Rect RenderObject::GetContentRect() const { + Rect rect{Point::Zero(), GetSize()}; + rect = rect.Shrink(GetMargin()); + rect = rect.Shrink(GetPadding()); + rect.width = std::max(rect.width, 0.0f); + rect.height = std::max(rect.height, 0.0f); + return rect; +} + void RenderObject::SetParent(RenderObject* new_parent) { const auto old_parent = parent_; parent_ = new_parent; diff --git a/src/ui/render/render_object.hpp b/src/ui/render/render_object.hpp index abdda605..824b88e6 100644 --- a/src/ui/render/render_object.hpp +++ b/src/ui/render/render_object.hpp @@ -75,6 +75,8 @@ class RenderObject : public Object { virtual Size OnMeasureContent(const Size& available_size) = 0; virtual void OnLayoutContent(const Rect& content_rect) = 0; + Rect GetContentRect() const; + private: void SetParent(RenderObject* new_parent); diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp index b66fffa3..69563ad7 100644 --- a/src/ui/render/text_render_object.cpp +++ b/src/ui/render/text_render_object.cpp @@ -114,15 +114,9 @@ RenderObject* TextRenderObject::HitTest(const Point& point) { void TextRenderObject::OnSizeChanged(const Size& old_size, const Size& new_size) { - const auto margin = GetMargin(); - const auto padding = GetPadding(); - ThrowIfFailed(text_layout_->SetMaxWidth( - std::max(new_size.width - margin.GetHorizontalTotal() - - padding.GetHorizontalTotal(), - 0.0f))); - ThrowIfFailed(text_layout_->SetMaxHeight(std::max( - new_size.height - margin.GetVerticalTotal() - padding.GetVerticalTotal(), - 0.0f))); + const auto&& size = GetContentRect().GetSize(); + ThrowIfFailed(text_layout_->SetMaxWidth(size.width)); + ThrowIfFailed(text_layout_->SetMaxHeight(size.height)); } Size TextRenderObject::OnMeasureContent(const Size& available_size) { @@ -145,7 +139,7 @@ void TextRenderObject::RecreateTextLayout() { const auto dwrite_factory = graph::GraphManager::GetInstance()->GetDWriteFactory(); - const auto&& size = GetSize(); + const auto&& size = GetContentRect().GetSize(); ThrowIfFailed(dwrite_factory->CreateTextLayout( text_.c_str(), static_cast(text_.size()), text_format_, diff --git a/src/ui/render/text_render_object.hpp b/src/ui/render/text_render_object.hpp index db102b04..7827f994 100644 --- a/src/ui/render/text_render_object.hpp +++ b/src/ui/render/text_render_object.hpp @@ -38,11 +38,11 @@ class TextRenderObject : public RenderObject { selection_range_ = std::move(new_range); } - ID2D1Brush* GetSelectionBrush() const { - return selection_brush_; - } + ID2D1Brush* GetSelectionBrush() const { return selection_brush_; } void SetSelectionBrush(ID2D1Brush* new_brush); + void Refresh() { RecreateTextLayout(); } + void Draw(ID2D1RenderTarget* render_target) override; RenderObject* HitTest(const Point& point) override; -- cgit v1.2.3