aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CruUI.vcxproj7
-rw-r--r--CruUI.vcxproj.filters13
-rw-r--r--README.md1
-rw-r--r--snippets/vc++snippets.snippet55
-rw-r--r--src/application.hpp2
-rw-r--r--src/math_util.hpp2
-rw-r--r--src/ui/control.cpp2
-rw-r--r--src/ui/controls/list_item.cpp2
-rw-r--r--src/ui/controls/scroll_control.cpp2
-rw-r--r--src/ui/controls/toggle_button.cpp2
-rw-r--r--src/ui/convert_util.hpp21
-rw-r--r--src/ui/d2d_util.hpp94
-rw-r--r--src/ui/render/render_object.cpp98
-rw-r--r--src/ui/render/render_object.hpp257
-rw-r--r--src/ui/ui_base.hpp116
15 files changed, 634 insertions, 40 deletions
diff --git a/CruUI.vcxproj b/CruUI.vcxproj
index 449779d3..ee2c7eb1 100644
--- a/CruUI.vcxproj
+++ b/CruUI.vcxproj
@@ -150,6 +150,7 @@
<ClCompile Include="src\ui\events\ui_event.cpp" />
<ClCompile Include="src\ui\input_util.cpp" />
<ClCompile Include="src\ui\layout_base.cpp" />
+ <ClCompile Include="src\ui\render\render_object.cpp" />
<ClCompile Include="src\ui\ui_manager.cpp" />
<ClCompile Include="src\ui\ui_base.cpp" />
<ClCompile Include="src\ui\window.cpp" />
@@ -172,15 +173,19 @@
<ClInclude Include="src\ui\controls\text_box.hpp" />
<ClCompile Include="src\ui\controls\text_control.cpp" />
<ClInclude Include="src\ui\controls\toggle_button.hpp" />
- <ClInclude Include="src\ui\convert_util.hpp" />
+ <ClInclude Include="src\ui\d2d_util.hpp" />
<ClInclude Include="src\ui\cursor.hpp" />
<ClInclude Include="src\ui\events\ui_event.hpp" />
<ClInclude Include="src\ui\input_util.hpp" />
<ClInclude Include="src\ui\layout_base.hpp" />
+ <ClInclude Include="src\ui\render\render_object.hpp" />
<ClInclude Include="src\ui\ui_manager.hpp" />
<ClInclude Include="src\ui\ui_base.hpp" />
<ClInclude Include="src\ui\window.hpp" />
</ItemGroup>
+ <ItemGroup>
+ <Xml Include="snippets\vc++snippets.snippet" />
+ </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
diff --git a/CruUI.vcxproj.filters b/CruUI.vcxproj.filters
index 99033bf7..13976a7d 100644
--- a/CruUI.vcxproj.filters
+++ b/CruUI.vcxproj.filters
@@ -93,7 +93,7 @@
<ClCompile Include="src\ui\controls\scroll_control.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\ui\input_util.cpp">
+ <ClCompile Include="src\ui\render\render_object.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
@@ -176,9 +176,6 @@
<ClInclude Include="src\ui\controls\list_item.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\ui\convert_util.hpp">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\ui\controls\popup_menu.hpp">
<Filter>Header Files</Filter>
</ClInclude>
@@ -194,7 +191,10 @@
<ClInclude Include="src\pre.hpp">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\ui\input_util.hpp">
+ <ClInclude Include="src\ui\render\render_object.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\ui\d2d_util.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@@ -254,4 +254,7 @@
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
+ <ItemGroup>
+ <Xml Include="snippets\vc++snippets.snippet" />
+ </ItemGroup>
</Project> \ No newline at end of file
diff --git a/README.md b/README.md
index 9df2c94f..934a0d80 100644
--- a/README.md
+++ b/README.md
@@ -30,3 +30,4 @@ Please using VS2017 open the `CruUI.sln` file in the root directory.
- `CruUI-Generate/` contains codes and project file that used to test merged codes.
+- `snippets/` contains useful snippets file for *Visual Studio*, you can import it.
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
+ <CodeSnippet Format="1.0.0">
+ <Header>
+ <Title>Deleted Copy Constructor/Assignment</Title>
+ <Author>crupest</Author>
+ <Description>Declare a deleted copy constructor and a deleted copy assignment operator for a class.</Description>
+ <Shortcut>dcopy</Shortcut>
+ <SnippetTypes>
+ <SnippetType>Expansion</SnippetType>
+ </SnippetTypes>
+ </Header>
+ <Snippet>
+ <Declarations>
+ <Literal>
+ <ID>classname</ID>
+ <Default>class_name</Default>
+ <ToolTip>The name of the class.</ToolTip>
+ </Literal>
+ </Declarations>
+ <Code Language="CPP" Kind="method decl">
+ <![CDATA[
+ $classname$(const $classname$& other) = delete;
+ $classname$& operator=(const $classname$& other) = delete;
+ ]]>
+ </Code>
+ </Snippet>
+ </CodeSnippet>
+ <CodeSnippet Format="1.0.0">
+ <Header>
+ <Title>Deleted Move Constructor/Assignment</Title>
+ <Author>crupest</Author>
+ <Description>Declare a deleted move constructor and a deleted move assignment operator for a class.</Description>
+ <Shortcut>dmove</Shortcut>
+ <SnippetTypes>
+ <SnippetType>Expansion</SnippetType>
+ </SnippetTypes>
+ </Header>
+ <Snippet>
+ <Declarations>
+ <Literal>
+ <ID>classname</ID>
+ <Default>class_name</Default>
+ <ToolTip>The name of the class.</ToolTip>
+ </Literal>
+ </Declarations>
+ <Code Language="CPP" Kind="method decl">
+ <![CDATA[
+ $classname$($classname$&& other) = delete;
+ $classname$& operator=($classname$&& other) = delete;
+ ]]>
+ </Code>
+ </Snippet>
+ </CodeSnippet>
+</CodeSnippets> \ No newline at end of file
diff --git a/src/application.hpp b/src/application.hpp
index a8d59cc8..8d739938 100644
--- a/src/application.hpp
+++ b/src/application.hpp
@@ -109,7 +109,7 @@ namespace cru
std::unordered_map<std::type_index, Object*> singleton_map_;
std::list<Object*> singleton_list_; // used for reverse destroy.
#ifdef CRU_DEBUG
- std::unordered_set<std::type_index> singleton_type_set_; // used for dead recursion.
+ std::unordered_set<std::type_index> singleton_type_set_; // used for detecting dead recursion.
#endif
};
diff --git a/src/math_util.hpp b/src/math_util.hpp
index b9830d6b..8f0741b8 100644
--- a/src/math_util.hpp
+++ b/src/math_util.hpp
@@ -9,7 +9,7 @@
namespace cru
{
- template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+ template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
float Coerce(const T n, const std::optional<T> min, const std::optional<T> max)
{
if (min.has_value() && n < min.value())
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 <limits>
#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..5f2e10b2
--- /dev/null
+++ b/src/ui/d2d_util.hpp
@@ -0,0 +1,94 @@
+#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 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);
+ }
+}
diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp
new file mode 100644
index 00000000..0828fc9c
--- /dev/null
+++ b/src/ui/render/render_object.cpp
@@ -0,0 +1,98 @@
+#include "render_object.hpp"
+#include <utility>
+
+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<ID2D1Brush> new_brush)
+ {
+ if (brush_ == new_brush)
+ return;
+
+ brush_ = std::move(new_brush);
+ InvalidateRenderHost();
+ }
+
+ void StrokeRenderObject::SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> new_stroke_style)
+ {
+ if (stroke_style_ == new_stroke_style)
+ return;
+
+ stroke_style_ = std::move(new_stroke_style);
+ InvalidateRenderHost();
+ }
+
+ void FillRenderObject::SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> new_brush)
+ {
+ if (brush_ == new_brush)
+ return;
+
+ brush_ = std::move(new_brush);
+ InvalidateRenderHost();
+ }
+
+ namespace details
+ {
+ template class ShapeRenderObject<Rect>;
+ template class ShapeRenderObject<RoundedRect>;
+ template class ShapeRenderObject<Ellipse>;
+ }
+
+ namespace details
+ {
+ template ShapeStrokeRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::DrawRectangle>;
+ template ShapeStrokeRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::DrawRoundedRectangle>;
+ template ShapeStrokeRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::DrawEllipse>;
+ }
+
+ namespace details
+ {
+ template ShapeFillRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::FillRectangle>;
+ template ShapeFillRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::FillRoundedRectangle>;
+ template ShapeFillRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::FillEllipse>;
+ }
+
+ 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
new file mode 100644
index 00000000..31745be5
--- /dev/null
+++ b/src/ui/render/render_object.hpp
@@ -0,0 +1,257 @@
+#pragma once
+
+#include "pre.hpp"
+
+#include "system_headers.hpp"
+#include <functional>
+#include <cassert>
+
+#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<ID2D1Brush> GetBrush() const
+ {
+ return brush_;
+ }
+
+ void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> new_brush);
+
+ Microsoft::WRL::ComPtr<ID2D1StrokeStyle> GetStrokeStyle() const
+ {
+ return stroke_style_;
+ }
+
+ void SetStrokeStyle(Microsoft::WRL::ComPtr<ID2D1StrokeStyle> new_stroke_style);
+
+ private:
+ float stroke_width_ = 1.0f;
+ Microsoft::WRL::ComPtr<ID2D1Brush> brush_ = nullptr;
+ Microsoft::WRL::ComPtr<ID2D1StrokeStyle> 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<ID2D1Brush> GetBrush() const
+ {
+ return brush_;
+ }
+
+ void SetBrush(Microsoft::WRL::ComPtr<ID2D1Brush> new_brush);
+
+ private:
+ Microsoft::WRL::ComPtr<ID2D1Brush> brush_ = nullptr;
+ };
+
+
+ namespace details
+ {
+ template <typename TShapeType>
+ 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_;
+ };
+
+
+ extern template class ShapeRenderObject<Rect>;
+ extern template class ShapeRenderObject<RoundedRect>;
+ extern template class ShapeRenderObject<Ellipse>;
+ }
+
+
+ using RectangleRenderObject = details::ShapeRenderObject<Rect>;
+ using RoundedRectangleRenderObject = details::ShapeRenderObject<RoundedRect>;
+ using EllipseRenderObject = details::ShapeRenderObject<Ellipse>;
+
+
+ namespace details
+ {
+ template<typename TShapeType, typename TD2D1ShapeType, void (ID2D1RenderTarget::*draw_function)(const TD2D1ShapeType&, ID2D1Brush*, float, ID2D1StrokeStyle*)>
+ class ShapeStrokeRenderObject : public ShapeRenderObject<TShapeType>, 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
+ {
+ const auto brush = GetBrush();
+ if (brush != nullptr)
+ (render_target->*draw_function)(Convert(GetShape()), brush.Get(), GetStrokeWidth(), GetStrokeStyle().Get());
+ }
+ };
+
+ extern template ShapeStrokeRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::DrawRectangle>;
+ extern template ShapeStrokeRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::DrawRoundedRectangle>;
+ extern template ShapeStrokeRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::DrawEllipse>;
+ }
+
+ using RectangleStrokeRenderObject = details::ShapeStrokeRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::DrawRectangle>;
+ using RoundedRectangleStrokeRenderObject = details::ShapeStrokeRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::DrawRoundedRectangle>;
+ using EllipseStrokeRenderObject = details::ShapeStrokeRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::DrawEllipse>;
+
+
+ namespace details
+ {
+ template<typename TShapeType, typename TD2D1ShapeType, void (ID2D1RenderTarget::*fill_function)(const TD2D1ShapeType&, ID2D1Brush*)>
+ class ShapeFillRenderObject : public ShapeRenderObject<TShapeType>, 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<Rect, D2D1_RECT_F, &ID2D1RenderTarget::FillRectangle>;
+ extern template ShapeFillRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::FillRoundedRectangle>;
+ extern template ShapeFillRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::FillEllipse>;
+ }
+
+ using RectangleFillRenderObject = details::ShapeFillRenderObject<Rect, D2D1_RECT_F, &ID2D1RenderTarget::FillRectangle>;
+ using RoundedRectangleFillRenderObject = details::ShapeFillRenderObject<RoundedRect, D2D1_ROUNDED_RECT, &ID2D1RenderTarget::FillRoundedRectangle>;
+ using EllipseFillRenderObject = details::ShapeFillRenderObject<Ellipse, D2D1_ELLIPSE, &ID2D1RenderTarget::FillEllipse>;
+
+
+ class CustomDrawHandlerRenderObject : public RenderObject
+ {
+ public:
+ using DrawHandler = std::function<void(ID2D1RenderTarget*)>;
+
+ 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_{};
+ };
+}
diff --git a/src/ui/ui_base.hpp b/src/ui/ui_base.hpp
index e57af530..c26bfe0e 100644
--- a/src/ui/ui_base.hpp
+++ b/src/ui/ui_base.hpp
@@ -8,7 +8,7 @@
namespace cru::ui
{
- struct Point final
+ struct Point
{
constexpr static Point Zero()
{
@@ -33,7 +33,7 @@ namespace cru::ui
}
- struct Size final
+ struct Size
{
constexpr static Size Zero()
{
@@ -47,12 +47,12 @@ namespace cru::ui
float height = 0;
};
- constexpr Size operator+(const Size& left, const Size& right)
+ 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)
+ constexpr Size operator - (const Size& left, const Size& right)
{
return Size(left.width - right.width, left.height - right.height);
}
@@ -68,7 +68,7 @@ namespace cru::ui
}
- struct Thickness final
+ struct Thickness
{
constexpr static Thickness Zero()
{
@@ -122,7 +122,21 @@ namespace cru::ui
float bottom;
};
- struct Rect final
+ 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;
constexpr Rect(const float left, const float top, const float width, const float height)
@@ -135,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;
@@ -165,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);
@@ -190,8 +214,82 @@ 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);
+ }
+
+
+ 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;
+ }
- struct TextRange final
+ 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,
+ Right,
+ Middle
+ };
+
+
+ struct TextRange
{
constexpr static std::optional<TextRange> FromTwoSides(unsigned first, unsigned second)
{
@@ -219,4 +317,8 @@ namespace cru::ui
unsigned position = 0;
unsigned count = 0;
};
+
+ bool IsKeyDown(int virtual_code);
+ bool IsKeyToggled(int virtual_code);
+ bool IsAnyMouseButtonDown();
}