aboutsummaryrefslogtreecommitdiff
path: root/include/cru/ui/render/RenderObject.h
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-02-08 16:53:51 +0800
committercrupest <crupest@outlook.com>2022-02-08 16:53:51 +0800
commit74bb9cd27242b9320f99ff4d2b50c3051576cc14 (patch)
tree744bac5799c593d1d6f81e7b09581bea626f2cde /include/cru/ui/render/RenderObject.h
parentb90c398de829d1ba5329651d75bae82f5e4085fe (diff)
downloadcru-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.h240
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