#pragma once #include "../Base.h" #include "MeasureRequirement.h" #include #include #include namespace cru::ui::render { class RenderObject; struct CRU_UI_API RenderObjectDrawContext { /** * Coordinates are related to the render object to be painted. So parent * render object should adjust this before passing to children. */ Rect paint_invalid_area; platform::graphics::IPainter* painter; void DrawChild(RenderObject* render_object); }; /** * * ### Create New Render Object * * To write a custom RenderObject, override following methods: * * public: * RenderObject* HitTest(const Point& point) override; * protected: * Size OnMeasureContent(const MeasureRequirement& requirement) override; * void OnLayoutContent(const Rect& content_rect) override; * void OnDraw(RenderObjectDrawContext& context) override; */ class CRU_UI_API RenderObject : public Object { private: constexpr static auto kLogTag = "cru::ui::render::RenderObject"; public: RenderObject(std::string name); ~RenderObject() override; controls::Control* GetAttachedControl() { return control_; } void SetAttachedControl(controls::Control* new_control); RenderObject* GetParent() { return parent_; } void SetParent(RenderObject* new_parent); template void WalkUp(const F& f, bool include_this = true) { auto parent = include_this ? this : GetParent(); while (parent) { f(parent); parent = parent->GetParent(); } } // Offset from parent's lefttop to lefttop of this render object. Margin is // accounted for. Point GetOffset() { return offset_; } Size GetSize() { return size_; } Point GetTotalOffset(); Point FromRootToContent(const Point& point); Size GetMeasureResultSize() { return measure_result_size_; } Thickness GetMargin() { return margin_; } void SetMargin(const Thickness& margin); Thickness GetPadding() { return padding_; } void SetPadding(const Thickness& padding); MeasureSize GetSuggestSize() { return custom_measure_requirement_.suggest; } void SetSuggestSize(const MeasureSize& suggest_size); MeasureSize GetMinSize() { return custom_measure_requirement_.min; } void SetMinSize(const MeasureSize& min_size); MeasureSize GetMaxSize() { return custom_measure_requirement_.max; } void SetMaxSize(const MeasureSize& max_size); MeasureRequirement GetCustomMeasureRequirement() { return custom_measure_requirement_; } /** * First checks if layout cache is still valid. If not, invokes OnMeasureCore * to do the real measure and save the result. Use GetMeasureResultSize to get * the result size. * * The cache is valid only if, * 1. Measure requirement does not change. * 2. InvalidateLayout is not called on any descendents. */ void Measure(const MeasureRequirement& requirement); /** * Sets offset and size (from GetMeasureResult) of this render object and call * OnLayoutCore. This will set layout as valid. */ void Layout(const Point& offset); /** * Sets offset and size of this render object and call OnLayoutCore. This will * set layout as valid. */ void Layout(const Rect& rect); virtual Thickness GetTotalSpaceThickness(); virtual Thickness GetInnerSpaceThickness(); virtual Rect GetPaddingRect(); virtual Rect GetContentRect(); virtual Rect GetRenderRect(); void Draw(RenderObjectDrawContext& context); // 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; public: controls::ControlHost* GetControlHost(); void InvalidateLayout(); void InvalidatePaint(); public: std::string GetName(); std::string GetDebugPathInTree(); CRU_DEFINE_EVENT(Destroy, RenderObject*) protected: /** * Implementation should adjust the requirement for content (like decreasing * the size of padding and margin), call OnMeasureContent to measure content * and adjust back to get the final measure result. * * The default implementation in RenderObject of calculating content's * requirement is first merging the given requirement with RenderObject's * custom measure requirement and then reducing GetTotalSpaceThickness. */ virtual Size OnMeasureCore(const MeasureRequirement& requirement); // Please reduce margin and padding or other custom things and pass the result // content rect to OnLayoutContent. virtual void OnLayoutCore(const Rect& rect); // 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. virtual Size OnMeasureContent(const MeasureRequirement& requirement) = 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 OnDraw(RenderObjectDrawContext& context) = 0; virtual void OnAttachedControlChanged(controls::Control* old_control, controls::Control* new_control) {} virtual void OnResize(const Size& new_size) {} private: std::string name_; controls::Control* control_; RenderObject* parent_; Point offset_; Size size_; Thickness margin_; Thickness padding_; bool layout_valid_; MeasureRequirement last_measure_requirement_; Size measure_result_size_; MeasureRequirement custom_measure_requirement_; }; } // namespace cru::ui::render