diff options
Diffstat (limited to 'src/ui/render')
-rw-r--r-- | src/ui/render/render_object.cpp | 98 | ||||
-rw-r--r-- | src/ui/render/render_object.hpp | 257 |
2 files changed, 355 insertions, 0 deletions
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_{}; + }; +} |