diff options
author | crupest <crupest@outlook.com> | 2022-02-08 16:53:51 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2022-02-08 16:53:51 +0800 |
commit | 74bb9cd27242b9320f99ff4d2b50c3051576cc14 (patch) | |
tree | 744bac5799c593d1d6f81e7b09581bea626f2cde /include/cru/ui/render/RenderObject.h | |
parent | b90c398de829d1ba5329651d75bae82f5e4085fe (diff) | |
download | cru-74bb9cd27242b9320f99ff4d2b50c3051576cc14.tar.gz cru-74bb9cd27242b9320f99ff4d2b50c3051576cc14.tar.bz2 cru-74bb9cd27242b9320f99ff4d2b50c3051576cc14.zip |
...
Diffstat (limited to 'include/cru/ui/render/RenderObject.h')
-rw-r--r-- | include/cru/ui/render/RenderObject.h | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/include/cru/ui/render/RenderObject.h b/include/cru/ui/render/RenderObject.h new file mode 100644 index 00000000..1e47e7fc --- /dev/null +++ b/include/cru/ui/render/RenderObject.h @@ -0,0 +1,240 @@ +#pragma once +#include "Base.h" + +#include "MeasureRequirement.h" +#include "cru/common/Base.h" +#include "cru/common/Event.h" +#include "cru/ui/Base.h" + +#include <cstddef> +#include <string> +#include <string_view> + +namespace cru::ui::render { +// Render object will not destroy its children when destroyed. Control must +// manage lifecycle of its render objects. Since control will destroy its +// children when destroyed, render objects will be destroyed along with it. +// +// About layout: +// Render object should be able to deal with arbitrary size as the result of +// measure and layout. +// +// Parent may pass calculated preferred size down. But the preferred size set on +// child itself takes precedence. +// +// Each render object should obey the measure requirement to set size and report +// a warning when that requirement can't be satisfied with probably bigger size +// of children than that of itself and optional visual effect to indicate that. +// +// If size of chilren are less than min size requirement, then render object +// should try to fill the rest area. If size of children are more than max size +// requirement, then render object should display a warning of the layout +// problem and use the max size of itself with children exceeding its bound. +// (Or better you could use some visual effect to indicate that.) +// +// To write a custom RenderObject, override following methods: +// public: +// void Draw(platform::graphics::IPainter* painter) override; +// RenderObject* HitTest(const Point& point) override; +// protected: +// Size OnMeasureContent(const MeasureRequirement& requirement) override; +// void OnLayoutContent(const Rect& content_rect) override; +class CRU_UI_API RenderObject : public Object { + friend host::WindowHost; + + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::RenderObject") + + protected: + enum class ChildMode { + None, + Single, + Multiple, + }; + + RenderObject() = default; + RenderObject(ChildMode child_mode) : RenderObject() { + SetChildMode(child_mode); + } + + public: + RenderObject(const RenderObject& other) = delete; + RenderObject(RenderObject&& other) = delete; + RenderObject& operator=(const RenderObject& other) = delete; + RenderObject& operator=(RenderObject&& other) = delete; + ~RenderObject() override = default; + + controls::Control* GetAttachedControl() const { return control_; } + void SetAttachedControl(controls::Control* new_control); + + host::WindowHost* GetWindowHost() const { return window_host_; } + + RenderObject* GetParent() const { return parent_; } + + const std::vector<RenderObject*>& GetChildren() const { return children_; } + Index GetChildCount() const { return static_cast<Index>(children_.size()); } + void AddChild(RenderObject* render_object, Index position); + void RemoveChild(Index position); + + RenderObject* GetFirstChild() const; + void TraverseDescendants(const std::function<void(RenderObject*)>& action); + + // Offset from parent's lefttop to lefttop of this render object. Margin is + // accounted for. + Point GetOffset() const { return offset_; } + Size GetSize() const { return size_; } + Point GetTotalOffset() const; + Point FromRootToContent(const Point& point) const; + + Thickness GetMargin() const { return margin_; } + void SetMargin(const Thickness& margin) { + margin_ = margin; + InvalidateLayout(); + } + + Thickness GetPadding() const { return padding_; } + void SetPadding(const Thickness& padding) { + padding_ = padding; + InvalidateLayout(); + } + + MeasureSize GetPreferredSize() const { return preferred_size_; } + void SetPreferredSize(const MeasureSize& preferred_size) { + preferred_size_ = preferred_size; + InvalidateLayout(); + } + + MeasureSize GetMinSize() const { return custom_measure_requirement_.min; } + void SetMinSize(const MeasureSize& min_size) { + custom_measure_requirement_.min = min_size; + InvalidateLayout(); + } + + MeasureSize GetMaxSize() const { return custom_measure_requirement_.max; } + void SetMaxSize(const MeasureSize& max_size) { + custom_measure_requirement_.max = max_size; + InvalidateLayout(); + } + + MeasureRequirement GetCustomMeasureRequirement() const { + return custom_measure_requirement_; + } + + // This method will merge requirement passed by argument and requirement of + // the render object using MeasureRequirement::Merge and then call + // MeasureRequirement::Normalize on it. And it will use preferred size of the + // render object to override the one passed by argument. Then pass the two to + // OnMeasureCore and use the return value of it to set the size of this render + // object. This can be called multiple times on children during measure to + // adjust for better size. + void Measure(const MeasureRequirement& requirement, + const MeasureSize& preferred_size); + // This will set offset of this render object and call OnLayoutCore. + void Layout(const Point& offset); + + virtual Thickness GetOuterSpaceThickness() const; + virtual Rect GetPaddingRect() const; + virtual Rect GetContentRect() const; + + void Draw(platform::graphics::IPainter* painter); + + // Param point must be relative the lefttop of render object including margin. + // Add offset before pass point to children. + virtual RenderObject* HitTest(const Point& point) = 0; + + IEvent<host::WindowHost*>* AttachToHostEvent() { + return &attach_to_host_event_; + } + IEvent<std::nullptr_t>* DetachFromHostEvent() { + return &detach_from_host_event_; + } + + public: + void InvalidateLayout(); + void InvalidatePaint(); + + public: + virtual std::u16string_view GetName() const; + std::u16string GetDebugPathInTree() const; + + protected: + void SetChildMode(ChildMode mode) { child_mode_ = mode; } + + protected: + RenderObject* GetSingleChild() const; + + virtual void OnParentChanged(RenderObject* old_parent, + RenderObject* new_parent); + + // default is to invalidate both layout and paint + virtual void OnAddChild(RenderObject* new_child, Index position); + // default is to invalidate both layout and paint + virtual void OnRemoveChild(RenderObject* removed_child, Index position); + + // Draw all children with offset. + void DefaultDrawChildren(platform::graphics::IPainter* painter); + + // Draw all children with translation of content rect lefttop. + void DefaultDrawContent(platform::graphics::IPainter* painter); + + // Call DefaultDrawContent. Then call DefaultDrawChildren. + virtual void OnDrawCore(platform::graphics::IPainter* painter); + + virtual void OnDrawContent(platform::graphics::IPainter* painter); + + // Size measure including margin and padding. Please reduce margin and padding + // or other custom things and pass the result content measure requirement and + // preferred size to OnMeasureContent. Return value must not be negative and + // must obey requirement. + // Note: Implementation should coerce the preferred size into the requirement + // when pass them to OnMeasureContent. + virtual Size OnMeasureCore(const MeasureRequirement& requirement, + const MeasureSize& preferred_size); + + // Please reduce margin and padding or other custom things and pass the result + // content rect to OnLayoutContent. + virtual void OnLayoutCore(); + + // Override this function to measure content and children(Call Measure on + // them). Do not consider margin or padding in this method because they are + // already considered in OnMeasureCore. Returned size must obey requirement. + // Caller should guarantee preferred_size is corerced into required range. + virtual Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) = 0; + + // Layout all content and children(Call Layout on them). + // Lefttop of content_rect should be added when calculated children's offset. + virtual void OnLayoutContent(const Rect& content_rect) = 0; + + virtual void OnAttachedControlChanged(controls::Control* control) { + CRU_UNUSED(control) + } + + virtual void OnAfterLayout(); + + private: + void SetParent(RenderObject* new_parent); + + void SetWindowHostRecursive(host::WindowHost* host); + + private: + controls::Control* control_ = nullptr; + host::WindowHost* window_host_ = nullptr; + + RenderObject* parent_ = nullptr; + std::vector<RenderObject*> children_{}; + + ChildMode child_mode_ = ChildMode::None; + + Point offset_{}; + Size size_{}; + + MeasureSize preferred_size_; + MeasureRequirement custom_measure_requirement_; + + Thickness margin_{}; + Thickness padding_{}; + + Event<host::WindowHost*> attach_to_host_event_; + Event<std::nullptr_t> detach_from_host_event_; +}; +} // namespace cru::ui::render |