diff options
22 files changed, 331 insertions, 456 deletions
diff --git a/include/cru/ui/controls/TreeView.h b/include/cru/ui/controls/TreeView.h new file mode 100644 index 00000000..2e782ee0 --- /dev/null +++ b/include/cru/ui/controls/TreeView.h @@ -0,0 +1,40 @@ +#pragma once +#include "Control.h" + +namespace cru::ui::controls { +class TreeView; + +class CRU_UI_API TreeViewItem : public Object { + friend TreeView; + + private: + TreeViewItem(TreeView* tree_view, TreeViewItem* parent); + + public: + CRU_DELETE_COPY(TreeViewItem) + CRU_DELETE_MOVE(TreeViewItem) + + ~TreeViewItem() override; + + TreeView* GetTreeView() { return tree_view_; } + TreeViewItem* GetParent() { return parent_; } + + const std::vector<TreeViewItem*>& GetChildren() const { return children_; } + Index GetChildCount() const { return children_.size(); } + TreeViewItem* GetChildAt(Index index) { + Expects(index >= 0 && index < children_.size()); + return children_[index]; + } + + TreeViewItem* AddItem(Index position); + void RemoveItem(Index position); + + private: + TreeView* tree_view_; + TreeViewItem* parent_; + std::vector<TreeViewItem*> children_; + + Control* control_; +}; + +} // namespace cru::ui::controls diff --git a/include/cru/ui/render/BorderRenderObject.h b/include/cru/ui/render/BorderRenderObject.h index d75a979f..1720d680 100644 --- a/include/cru/ui/render/BorderRenderObject.h +++ b/include/cru/ui/render/BorderRenderObject.h @@ -1,9 +1,7 @@ #pragma once -#include <string_view> -#include "../style/ApplyBorderStyleInfo.h" #include "RenderObject.h" -#include "cru/platform/GraphicsBase.h" -#include "cru/ui/Base.h" + +#include "../style/ApplyBorderStyleInfo.h" namespace cru::ui::render { class CRU_UI_API BorderRenderObject : public RenderObject { @@ -11,12 +9,13 @@ class CRU_UI_API BorderRenderObject : public RenderObject { public: BorderRenderObject(); - BorderRenderObject(const BorderRenderObject& other) = delete; - BorderRenderObject(BorderRenderObject&& other) = delete; - BorderRenderObject& operator=(const BorderRenderObject& other) = delete; - BorderRenderObject& operator=(BorderRenderObject&& other) = delete; + CRU_DELETE_COPY(BorderRenderObject) + CRU_DELETE_MOVE(BorderRenderObject) ~BorderRenderObject() override; + RenderObject* GetChild() const { return child_; } + void SetChild(RenderObject* new_child); + bool IsBorderEnabled() const { return is_border_enabled_; } void SetBorderEnabled(bool enabled) { is_border_enabled_ = enabled; } @@ -69,26 +68,27 @@ class CRU_UI_API BorderRenderObject : public RenderObject { void ApplyBorderStyle(const style::ApplyBorderStyleInfo& style); RenderObject* HitTest(const Point& point) override; + void Draw(platform::graphics::IPainter* painter) override; Thickness GetOuterSpaceThickness() const override; Rect GetPaddingRect() const override; Rect GetContentRect() const override; - std::u16string_view GetName() const override { return u"BorderRenderObject"; } + String GetName() const override { return u"BorderRenderObject"; } protected: - void OnDrawCore(platform::graphics::IPainter* painter) override; - Size OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; - void OnAfterLayout() override; + void OnResize(const Size& new_size) override; private: void RecreateGeometry(); private: + RenderObject* child_ = nullptr; + bool is_border_enabled_ = false; std::shared_ptr<platform::graphics::IBrush> border_brush_; diff --git a/include/cru/ui/render/CanvasRenderObject.h b/include/cru/ui/render/CanvasRenderObject.h index ca55ebc6..8343a5f5 100644 --- a/include/cru/ui/render/CanvasRenderObject.h +++ b/include/cru/ui/render/CanvasRenderObject.h @@ -1,6 +1,8 @@ #pragma once #include "RenderObject.h" +#include "cru/common/Event.h" + namespace cru::ui::render { // Layout logic: // If no preferred size is set. Then (100, 100) is used and then coerced to @@ -21,9 +23,9 @@ class CRU_UI_API CanvasRenderObject : public RenderObject { IEvent<CanvasPaintEventArgs>* PaintEvent() { return &paint_event_; } - protected: - void OnDrawContent(platform::graphics::IPainter* painter) override; + void Draw(platform::graphics::IPainter* painter) override; + protected: Size OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; diff --git a/include/cru/ui/render/FlexLayoutRenderObject.h b/include/cru/ui/render/FlexLayoutRenderObject.h index 3a8348f6..164bae39 100644 --- a/include/cru/ui/render/FlexLayoutRenderObject.h +++ b/include/cru/ui/render/FlexLayoutRenderObject.h @@ -1,8 +1,6 @@ #pragma once #include "LayoutRenderObject.h" -#include <string_view> - namespace cru::ui::render { // Measure Logic (v0.1): // Cross axis measure logic is the same as stack layout. @@ -75,7 +73,8 @@ namespace cru::ui::render { // (if specified), then coerce the length to the min value but not report error // and just fill the rest space with blank. // -class CRU_UI_API FlexLayoutRenderObject : public LayoutRenderObject<FlexChildLayoutData> { +class CRU_UI_API FlexLayoutRenderObject + : public LayoutRenderObject<FlexChildLayoutData> { CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::FlexLayoutRenderObject") public: @@ -87,7 +86,7 @@ class CRU_UI_API FlexLayoutRenderObject : public LayoutRenderObject<FlexChildLay FlexLayoutRenderObject& operator=(FlexLayoutRenderObject&& other) = delete; ~FlexLayoutRenderObject() override = default; - std::u16string_view GetName() const override; + String GetName() const override; FlexDirection GetFlexDirection() const { return direction_; } void SetFlexDirection(FlexDirection direction) { diff --git a/include/cru/ui/render/LayoutRenderObject.h b/include/cru/ui/render/LayoutRenderObject.h index 42a3aa55..05da1bb8 100644 --- a/include/cru/ui/render/LayoutRenderObject.h +++ b/include/cru/ui/render/LayoutRenderObject.h @@ -9,8 +9,14 @@ class CRU_UI_API LayoutRenderObject : public RenderObject { public: using ChildLayoutData = TChildLayoutData; + private: + struct ChildData { + RenderObject* child; + ChildLayoutData layout_data; + }; + protected: - LayoutRenderObject() : RenderObject(ChildMode::Multiple) {} + LayoutRenderObject() = default; public: CRU_DELETE_COPY(LayoutRenderObject) @@ -18,69 +24,50 @@ class CRU_UI_API LayoutRenderObject : public RenderObject { ~LayoutRenderObject() override = default; - const std::vector<ChildLayoutData>& GetChildLayoutDataList() const { - return this->child_layout_data_; + Index GetChildCount() const { return static_cast<Index>(children_.size()); } + RenderObject* GetChildAt(Index position) { + Expects(position > 0 && position < GetChildCount()); + return children_[position].render_object; + } + void AddChild(RenderObject* render_object, Index position) { + if (position < 0) position = 0; + if (position > GetChildCount()) position = GetChildCount(); + children_.insert(children_.begin() + position, + ChildData{render_object, ChildLayoutData()}); + render_object->SetParent(this); + } + + void RemoveChild(Index position) { + Expects(position > 0 && position < GetChildCount()); + children_[position].render_object->SetParent(nullptr); + children_.erase(children_.begin() + position); } void SetChildLayoutData(Index position, ChildLayoutData data) { - Expects(position >= 0 && - position < static_cast<Index>(this->child_layout_data_.size())); - this->child_layout_data_[position] = std::move(data); - this->InvalidateLayout(); + Expects(position >= 0 && position < GetChildCount()); + children_[position].layout_data = std::move(data); + InvalidateLayout(); } const ChildLayoutData& GetChildLayoutData(Index position) const { - Expects(position >= 0 && - position < static_cast<Index>(this->child_layout_data_.size())); - return this->child_layout_data_[position]; + Expects(position >= 0 && position < GetChildCount()); + return children_[position].layout_data; } - RenderObject* HitTest(const Point& point) override; + RenderObject* HitTest(const Point& point) override { + const auto child_count = GetChildCount(); + for (auto i = child_count - 1; i >= 0; --i) { + const auto child = GetChildAt(i); + const auto result = child->HitTest(point - child->GetOffset()); + if (result != nullptr) { + return result; + } + } - protected: - void OnAddChild(RenderObject* new_child, Index position) override; - void OnRemoveChild(RenderObject* removed_child, Index position) override; + return GetPaddingRect().IsPointInside(point) ? this : nullptr; + } private: - std::vector<ChildLayoutData> child_layout_data_{}; + std::vector<ChildData> children_; }; - -template <typename TChildLayoutData> -RenderObject* LayoutRenderObject<TChildLayoutData>::HitTest( - const Point& point) { - const auto& children = GetChildren(); - for (auto i = children.crbegin(); i != children.crend(); ++i) { - auto offset = (*i)->GetOffset(); - Point p{point.x - offset.x, point.y - offset.y}; - const auto result = (*i)->HitTest(p); - if (result != nullptr) { - return result; - } - } - - const auto margin = GetMargin(); - const auto size = GetSize(); - return Rect{margin.left, margin.top, - std::max(size.width - margin.GetHorizontalTotal(), 0.0f), - std::max(size.height - margin.GetVerticalTotal(), 0.0f)} - .IsPointInside(point) - ? this - : nullptr; -} // namespace cru::ui::render - -template <typename TChildLayoutData> -void LayoutRenderObject<TChildLayoutData>::OnAddChild(RenderObject* new_child, - const Index position) { - CRU_UNUSED(new_child) - - child_layout_data_.emplace(child_layout_data_.cbegin() + position); -} - -template <typename TChildLayoutData> -void LayoutRenderObject<TChildLayoutData>::OnRemoveChild( - RenderObject* removed_child, const Index position) { - CRU_UNUSED(removed_child) - - child_layout_data_.erase(child_layout_data_.cbegin() + position); -} } // namespace cru::ui::render diff --git a/include/cru/ui/render/RenderObject.h b/include/cru/ui/render/RenderObject.h index a4ac0f4b..67ef6162 100644 --- a/include/cru/ui/render/RenderObject.h +++ b/include/cru/ui/render/RenderObject.h @@ -3,13 +3,9 @@ #include "MeasureRequirement.h" #include "cru/common/Base.h" -#include "cru/common/Event.h" +#include "cru/common/String.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 @@ -40,80 +36,46 @@ namespace cru::ui::render { // 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; + CRU_DELETE_COPY(RenderObject) + CRU_DELETE_MOVE(RenderObject) ~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); + void SetParent(RenderObject* new_parent) { parent_ = new_parent; } // 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; + Size GetDesiredSize() const { return desired_size_; } + Thickness GetMargin() const { return margin_; } - void SetMargin(const Thickness& margin) { - margin_ = margin; - InvalidateLayout(); - } + void SetMargin(const Thickness& margin); Thickness GetPadding() const { return padding_; } - void SetPadding(const Thickness& padding) { - padding_ = padding; - InvalidateLayout(); - } + void SetPadding(const Thickness& padding); MeasureSize GetPreferredSize() const { return preferred_size_; } - void SetPreferredSize(const MeasureSize& preferred_size) { - preferred_size_ = preferred_size; - InvalidateLayout(); - } + void SetPreferredSize(const MeasureSize& preferred_size); MeasureSize GetMinSize() const { return custom_measure_requirement_.min; } - void SetMinSize(const MeasureSize& min_size) { - custom_measure_requirement_.min = min_size; - InvalidateLayout(); - } + void SetMinSize(const MeasureSize& min_size); MeasureSize GetMaxSize() const { return custom_measure_requirement_.max; } - void SetMaxSize(const MeasureSize& max_size) { - custom_measure_requirement_.max = max_size; - InvalidateLayout(); - } + void SetMaxSize(const MeasureSize& max_size); MeasureRequirement GetCustomMeasureRequirement() const { return custom_measure_requirement_; @@ -135,56 +97,22 @@ class CRU_UI_API RenderObject : public Object { virtual Rect GetPaddingRect() const; virtual Rect GetContentRect() const; - void Draw(platform::graphics::IPainter* painter); + virtual void Draw(platform::graphics::IPainter* painter) = 0; // 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: + host::WindowHost* GetWindowHost(); void InvalidateLayout(); void InvalidatePaint(); public: - virtual std::u16string_view GetName() const; - std::u16string GetDebugPathInTree() const; - - protected: - void SetChildMode(ChildMode mode) { child_mode_ = mode; } + virtual String GetName() const; + String GetDebugPathInTree() const; 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); - - /** - * \brief Draw all children with offset. - */ - void DefaultDrawChildren(platform::graphics::IPainter* painter); - - /** - * \brief Call OnDrawContent 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 @@ -209,36 +137,24 @@ class CRU_UI_API RenderObject : public Object { // 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); + virtual void OnAttachedControlChanged(controls::Control* old_control, + controls::Control* new_control) {} + virtual void OnResize(const Size& new_size) {} 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_; + Size desired_size_{}; Thickness margin_{}; Thickness padding_{}; - Event<host::WindowHost*> attach_to_host_event_; - Event<std::nullptr_t> detach_from_host_event_; + MeasureSize preferred_size_{}; + MeasureRequirement custom_measure_requirement_{}; }; } // namespace cru::ui::render diff --git a/include/cru/ui/render/ScrollRenderObject.h b/include/cru/ui/render/ScrollRenderObject.h index bb282953..0890ec21 100644 --- a/include/cru/ui/render/ScrollRenderObject.h +++ b/include/cru/ui/render/ScrollRenderObject.h @@ -27,6 +27,9 @@ class CRU_UI_API ScrollRenderObject : public RenderObject { ~ScrollRenderObject() override = default; + RenderObject* GetChild() const { return child_; } + void SetChild(RenderObject* new_child); + RenderObject* HitTest(const Point& point) override; // Return the coerced scroll offset. @@ -61,7 +64,7 @@ class CRU_UI_API ScrollRenderObject : public RenderObject { // Param margin is just for convenience and it will just add to the rect. void ScrollToContain(const Rect& rect, const Thickness& margin = Thickness{}); - std::u16string_view GetName() const override { return u"ScrollRenderObject"; } + String GetName() const override { return u"ScrollRenderObject"; } bool IsMouseWheelScrollEnabled() const { return is_mouse_wheel_enabled_; } void SetMouseWheelScrollEnabled(bool enable); @@ -71,9 +74,9 @@ class CRU_UI_API ScrollRenderObject : public RenderObject { bool VerticalCanScrollUp(); bool VerticalCanScrollDown(); - protected: - void OnDrawCore(platform::graphics::IPainter* painter) override; + void Draw(platform::graphics::IPainter* painter) override; + protected: // Logic: // If available size is bigger than child's preferred size, then child's // preferred size is taken. @@ -82,11 +85,14 @@ class CRU_UI_API ScrollRenderObject : public RenderObject { const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; - void OnAttachedControlChanged(controls::Control* control) override; + void OnAttachedControlChanged(controls::Control* old_control, + controls::Control* new_control) override; void InstallMouseWheelHandler(controls::Control* control); private: + RenderObject* child_; + Point scroll_offset_; std::unique_ptr<ScrollBarDelegate> scroll_bar_delegate_; diff --git a/include/cru/ui/render/StackLayoutRenderObject.h b/include/cru/ui/render/StackLayoutRenderObject.h index e141d16e..68b5b30c 100644 --- a/include/cru/ui/render/StackLayoutRenderObject.h +++ b/include/cru/ui/render/StackLayoutRenderObject.h @@ -32,9 +32,7 @@ class CRU_UI_API StackLayoutRenderObject CRU_DELETE_MOVE(StackLayoutRenderObject) ~StackLayoutRenderObject() = default; - std::u16string_view GetName() const override { - return u"StackLayoutRenderObject"; - } + String GetName() const override { return u"StackLayoutRenderObject"; } Alignment GetDefaultHorizontalAlignment() const { return default_vertical_alignment_; diff --git a/include/cru/ui/render/TextRenderObject.h b/include/cru/ui/render/TextRenderObject.h index 3b5f581a..f3a7332f 100644 --- a/include/cru/ui/render/TextRenderObject.h +++ b/include/cru/ui/render/TextRenderObject.h @@ -91,17 +91,17 @@ class CRU_UI_API TextRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; - std::u16string_view GetName() const override { return u"TextRenderObject"; } + String GetName() const override { return u"TextRenderObject"; } - protected: - void OnDrawContent(platform::graphics::IPainter* painter) override; + void Draw(platform::graphics::IPainter* painter) override; + protected: // See remarks of this class. Size OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; - void OnAfterLayout() override; + void OnResize(const Size& size) override; private: std::shared_ptr<platform::graphics::IBrush> brush_; diff --git a/include/cru/ui/render/TreeRenderObject.h b/include/cru/ui/render/TreeRenderObject.h index 77ec0cff..4a176926 100644 --- a/include/cru/ui/render/TreeRenderObject.h +++ b/include/cru/ui/render/TreeRenderObject.h @@ -26,7 +26,7 @@ class CRU_UI_API TreeRenderObjectItem : public Object { Index GetChildCount() const { return children_.size(); } - TreeRenderObjectItem* GetChild(Index index) { + TreeRenderObjectItem* GetChildAt(Index index) { Expects(index >= 0 && index < children_.size()); return children_[index]; } @@ -34,6 +34,9 @@ class CRU_UI_API TreeRenderObjectItem : public Object { TreeRenderObjectItem* AddItem(Index position); void RemoveItem(Index position); + void* GetUserData() const { return user_data_; } + void SetUserData(void* user_data) { user_data_ = user_data; } + private: TreeRenderObject* tree_render_object_; @@ -41,6 +44,8 @@ class CRU_UI_API TreeRenderObjectItem : public Object { std::vector<TreeRenderObjectItem*> children_; RenderObject* render_object_; + + void* user_data_; }; class CRU_UI_API TreeRenderObject : public RenderObject { @@ -52,7 +57,7 @@ class CRU_UI_API TreeRenderObject : public RenderObject { CRU_DELETE_MOVE(TreeRenderObject) ~TreeRenderObject() override; - std::u16string_view GetName() const override { return u"TreeRenderObject"; } + String GetName() const override { return u"TreeRenderObject"; } TreeRenderObjectItem* GetRootItem() { return root_item_; } @@ -61,9 +66,9 @@ class CRU_UI_API TreeRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; - protected: - void OnDrawCore(platform::graphics::IPainter* painter) override; + void Draw(platform::graphics::IPainter* painter) override; + protected: Size OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index fd15e433..d358940f 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(cru_ui SHARED controls/TextBlock.cpp controls/TextBox.cpp controls/TextHostControlService.cpp + controls/TreeView.cpp controls/Window.cpp events/MouseEventArgs.cpp helper/ClickDetector.cpp diff --git a/src/ui/controls/TreeView.cpp b/src/ui/controls/TreeView.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/ui/controls/TreeView.cpp diff --git a/src/ui/host/WindowHost.cpp b/src/ui/host/WindowHost.cpp index 4373afe9..9773c117 100644 --- a/src/ui/host/WindowHost.cpp +++ b/src/ui/host/WindowHost.cpp @@ -111,7 +111,6 @@ WindowHost::WindowHost(controls::Control* root_control) }); root_render_object_ = root_control->GetRenderObject(); - root_render_object_->SetWindowHostRecursive(this); this->layout_paint_cycler_ = std::make_unique<LayoutPaintCycler>(this); @@ -187,16 +186,12 @@ void WindowHost::RelayoutWithSize(const Size& available_size, render::MeasureSize::NotSpecified()); if (set_window_size_to_fit_content) { - native_window_->SetClientSize(root_render_object_->GetSize()); + native_window_->SetClientSize(root_render_object_->GetDesiredSize()); } root_render_object_->Layout(Point{}); for (auto& action : after_layout_stable_action_) action(); after_layout_event_.Raise(AfterLayoutEventArgs{}); - root_render_object_->TraverseDescendants( - [](render::RenderObject* render_object) { - render_object->OnAfterLayout(); - }); after_layout_stable_action_.clear(); if constexpr (debug_flags::layout) log::TagDebug(log_tag, u"A relayout is finished."); diff --git a/src/ui/render/BorderRenderObject.cpp b/src/ui/render/BorderRenderObject.cpp index 61af27bc..5916c7aa 100644 --- a/src/ui/render/BorderRenderObject.cpp +++ b/src/ui/render/BorderRenderObject.cpp @@ -12,13 +12,18 @@ #include <algorithm> namespace cru::ui::render { -BorderRenderObject::BorderRenderObject() { - SetChildMode(ChildMode::Single); - RecreateGeometry(); -} +BorderRenderObject::BorderRenderObject() { RecreateGeometry(); } BorderRenderObject::~BorderRenderObject() {} +void BorderRenderObject::SetChild(RenderObject* new_child) { + if (child_ == new_child) return; + if (child_ != nullptr) child_->SetParent(nullptr); + child_ = new_child; + if (child_ != nullptr) child_->SetParent(this); + InvalidateLayout(); +} + void BorderRenderObject::ApplyBorderStyle( const style::ApplyBorderStyleInfo& style) { if (style.border_brush) border_brush_ = *style.border_brush; @@ -30,10 +35,8 @@ void BorderRenderObject::ApplyBorderStyle( } RenderObject* BorderRenderObject::HitTest(const Point& point) { - if (const auto child = GetSingleChild()) { - auto offset = child->GetOffset(); - Point p{point.x - offset.x, point.y - offset.y}; - const auto result = child->HitTest(p); + if (child_) { + const auto result = child_->HitTest(point - child_->GetOffset()); if (result != nullptr) { return result; } @@ -45,7 +48,7 @@ RenderObject* BorderRenderObject::HitTest(const Point& point) { return contains ? this : nullptr; } else { const auto margin = GetMargin(); - const auto size = GetSize(); + const auto size = GetDesiredSize(); return Rect{margin.left, margin.top, std::max(size.width - margin.GetHorizontalTotal(), 0.0f), std::max(size.height - margin.GetVerticalTotal(), 0.0f)} @@ -55,7 +58,7 @@ RenderObject* BorderRenderObject::HitTest(const Point& point) { } } -void BorderRenderObject::OnDrawCore(platform::graphics::IPainter* painter) { +void BorderRenderObject::Draw(platform::graphics::IPainter* painter) { if constexpr (debug_flags::draw) { log::TagDebug( log_tag, u"BorderRenderObject draw, background: {}, foreground: {}.", @@ -77,34 +80,35 @@ void BorderRenderObject::OnDrawCore(platform::graphics::IPainter* painter) { } } - DefaultDrawContent(painter); + if (child_) { + painter->PushState(); + painter->ConcatTransform(Matrix::Translation(child_->GetOffset())); + child_->Draw(painter); + painter->PopState(); + } if (foreground_brush_ != nullptr) painter->FillGeometry(border_inner_geometry_.get(), foreground_brush_.get()); - - DefaultDrawChildren(painter); } Size BorderRenderObject::OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) { - const auto child = GetSingleChild(); - if (child) { - child->Measure(requirement, preferred_size); - return child->GetSize(); + if (child_) { + child_->Measure(requirement, preferred_size); + return child_->GetDesiredSize(); } else { return Size{}; } } void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { - const auto child = GetSingleChild(); - if (child) { - child->Layout(content_rect.GetLeftTop()); + if (child_) { + child_->Layout(content_rect.GetLeftTop()); } } -void BorderRenderObject::OnAfterLayout() { RecreateGeometry(); } +void BorderRenderObject::OnResize(const Size& new_size) { RecreateGeometry(); } Thickness BorderRenderObject::GetOuterSpaceThickness() const { return is_border_enabled_ @@ -113,7 +117,7 @@ Thickness BorderRenderObject::GetOuterSpaceThickness() const { } Rect BorderRenderObject::GetPaddingRect() const { - const auto size = GetSize(); + const auto size = GetDesiredSize(); Rect rect{Point{}, size}; rect = rect.Shrink(GetMargin()); if (is_border_enabled_) rect = rect.Shrink(border_thickness_); @@ -125,7 +129,7 @@ Rect BorderRenderObject::GetPaddingRect() const { } Rect BorderRenderObject::GetContentRect() const { - const auto size = GetSize(); + const auto size = GetDesiredSize(); Rect rect{Point{}, size}; rect = rect.Shrink(GetMargin()); if (is_border_enabled_) rect = rect.Shrink(border_thickness_); @@ -180,7 +184,7 @@ void BorderRenderObject::RecreateGeometry() { builder->CloseFigure(true); }; - const auto size = GetSize(); + const auto size = GetDesiredSize(); const auto margin = GetMargin(); const Rect outer_rect{margin.left, margin.top, size.width - margin.GetHorizontalTotal(), diff --git a/src/ui/render/CanvasRenderObject.cpp b/src/ui/render/CanvasRenderObject.cpp index 985a2dae..e818333c 100644 --- a/src/ui/render/CanvasRenderObject.cpp +++ b/src/ui/render/CanvasRenderObject.cpp @@ -1,7 +1,7 @@ #include "cru/ui/render/CanvasRenderObject.h" namespace cru::ui::render { -CanvasRenderObject::CanvasRenderObject() : RenderObject(ChildMode::None) {} +CanvasRenderObject::CanvasRenderObject() : RenderObject() {} CanvasRenderObject::~CanvasRenderObject() = default; @@ -10,7 +10,7 @@ RenderObject* CanvasRenderObject::HitTest(const Point& point) { return padding_rect.IsPointInside(point) ? this : nullptr; } -void CanvasRenderObject::OnDrawContent(platform::graphics::IPainter* painter) { +void CanvasRenderObject::Draw(platform::graphics::IPainter* painter) { const auto rect = GetContentRect(); CanvasPaintEventArgs args{painter, rect.GetSize()}; paint_event_.Raise(args); diff --git a/src/ui/render/FlexLayoutRenderObject.cpp b/src/ui/render/FlexLayoutRenderObject.cpp index e4d16169..261a0f43 100644 --- a/src/ui/render/FlexLayoutRenderObject.cpp +++ b/src/ui/render/FlexLayoutRenderObject.cpp @@ -11,8 +11,7 @@ #include <type_traits> namespace cru::ui::render { - -std::u16string_view FlexLayoutRenderObject::GetName() const { +String FlexLayoutRenderObject::GetName() const { return u"FlexLayoutRenderObject"; } @@ -121,7 +120,7 @@ Size FlexLayoutMeasureContentImpl( float total_length = 0.f; for (auto child : children) { - total_length += GetMain(child->GetSize(), direction_tag); + total_length += GetMain(child->GetDesiredSize(), direction_tag); } // step 2. @@ -190,7 +189,7 @@ Size FlexLayoutMeasureContentImpl( const float shrink_length = layout_data[i].shrink_factor / total_shrink_factor * total_shrink_length; float new_measure_length = - GetMain(child->GetSize(), direction_tag) - shrink_length; + GetMain(child->GetDesiredSize(), direction_tag) - shrink_length; MeasureLength child_min_main_length = GetMain(child->GetMinSize(), direction_tag); @@ -210,7 +209,7 @@ Size FlexLayoutMeasureContentImpl( MeasureLength::NotSpecified(), direction_tag)); - const Size new_size = child->GetSize(); + const Size new_size = child->GetDesiredSize(); const float new_main_length = GetMain(new_size, direction_tag); if (new_main_length > new_measure_length) { to_remove.push_back(i); @@ -219,7 +218,7 @@ Size FlexLayoutMeasureContentImpl( total_length = 0.f; for (auto child : children) { - total_length += GetMain(child->GetSize(), direction_tag); + total_length += GetMain(child->GetDesiredSize(), direction_tag); } if (total_length <= target_length) break; @@ -250,7 +249,7 @@ Size FlexLayoutMeasureContentImpl( const float expand_length = layout_data[i].expand_factor / total_expand_factor * total_expand_length; float new_measure_length = - GetMain(child->GetSize(), direction_tag) + expand_length; + GetMain(child->GetDesiredSize(), direction_tag) + expand_length; MeasureLength child_max_main_length = GetMain(child->GetMaxSize(), direction_tag); @@ -271,7 +270,7 @@ Size FlexLayoutMeasureContentImpl( MeasureLength::NotSpecified(), direction_tag)); - const Size new_size = child->GetSize(); + const Size new_size = child->GetDesiredSize(); const float new_main_length = GetMain(new_size, direction_tag); if (new_main_length < new_measure_length) { to_remove.push_back(i); @@ -280,7 +279,7 @@ Size FlexLayoutMeasureContentImpl( total_length = 0.f; for (auto child : children) { - total_length += GetMain(child->GetSize(), direction_tag); + total_length += GetMain(child->GetDesiredSize(), direction_tag); } if (total_length >= target_length) break; @@ -292,7 +291,7 @@ Size FlexLayoutMeasureContentImpl( float child_max_cross_length = 0.f; for (auto child : children) { - const float cross_length = GetCross(child->GetSize(), direction_tag); + const float cross_length = GetCross(child->GetDesiredSize(), direction_tag); if (cross_length > child_max_cross_length) { child_max_cross_length = cross_length; } @@ -319,7 +318,7 @@ Size FlexLayoutMeasureContentImpl( auto child = children[i]; if (child_layout_data.cross_alignment.value_or(item_cross_align) == Alignment::Stretch) { - auto size = child->GetSize(); + auto size = child->GetDesiredSize(); GetCross(size, direction_tag) = child_max_cross_length; child->Measure({size, size}, MeasureSize::NotSpecified()); } @@ -333,29 +332,40 @@ Size FlexLayoutRenderObject::OnMeasureContent( const MeasureRequirement& requirement, const MeasureSize& preferred_size) { const bool horizontal = (direction_ == FlexDirection::Horizontal || direction_ == FlexDirection::HorizontalReverse); + std::vector<RenderObject*> children; + std::vector<FlexChildLayoutData> layout_data_list; + for (int i = 0; i < GetChildCount(); i++) { + children.push_back(GetChildAt(i)); + layout_data_list.push_back(GetChildLayoutData(i)); + } + if (horizontal) { return FlexLayoutMeasureContentImpl<tag_horizontal_t>( - requirement, preferred_size, GetChildren(), GetChildLayoutDataList(), + requirement, preferred_size, children, layout_data_list, item_cross_align_, log_tag); } else { return FlexLayoutMeasureContentImpl<tag_vertical_t>( - requirement, preferred_size, GetChildren(), GetChildLayoutDataList(), + requirement, preferred_size, children, layout_data_list, item_cross_align_, log_tag); } } void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { - const auto& children = GetChildren(); - const Index child_count = children.size(); + const auto child_count = GetChildCount(); + std::vector<RenderObject*> children; + std::vector<FlexChildLayoutData> layout_data_list; + for (int i = 0; i < child_count; i++) { + children.push_back(GetChildAt(i)); + layout_data_list.push_back(GetChildLayoutData(i)); + } if (direction_ == FlexDirection::Horizontal) { float current_main_offset = 0; for (Index i = 0; i < child_count; i++) { const auto child = children[i]; - const auto size = child->GetSize(); + const auto size = child->GetDesiredSize(); const auto cross_align = - GetChildLayoutDataList()[i].cross_alignment.value_or( - GetItemCrossAlign()); + layout_data_list[i].cross_alignment.value_or(GetItemCrossAlign()); child->Layout( Point{content_rect.left + current_main_offset, CalculateAnchorByAlignment(cross_align, content_rect.top, @@ -367,10 +377,9 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { float current_main_offset = 0; for (Index i = 0; i < child_count; i++) { const auto child = children[i]; - const auto size = child->GetSize(); + const auto size = child->GetDesiredSize(); const auto cross_align = - GetChildLayoutDataList()[i].cross_alignment.value_or( - GetItemCrossAlign()); + layout_data_list[i].cross_alignment.value_or(GetItemCrossAlign()); child->Layout( Point{content_rect.GetRight() - current_main_offset, CalculateAnchorByAlignment(cross_align, content_rect.top, @@ -381,10 +390,9 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { float current_main_offset = 0; for (Index i = 0; i < child_count; i++) { const auto child = children[i]; - const auto size = child->GetSize(); + const auto size = child->GetDesiredSize(); const auto cross_align = - GetChildLayoutDataList()[i].cross_alignment.value_or( - GetItemCrossAlign()); + layout_data_list[i].cross_alignment.value_or(GetItemCrossAlign()); child->Layout(Point{ CalculateAnchorByAlignment(cross_align, content_rect.left, content_rect.width, size.width), @@ -396,10 +404,9 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { float current_main_offset = 0; for (Index i = 0; i < child_count; i++) { const auto child = children[i]; - const auto size = child->GetSize(); + const auto size = child->GetDesiredSize(); const auto cross_align = - GetChildLayoutDataList()[i].cross_alignment.value_or( - GetItemCrossAlign()); + layout_data_list[i].cross_alignment.value_or(GetItemCrossAlign()); child->Layout( Point{CalculateAnchorByAlignment(cross_align, content_rect.left, content_rect.width, size.width), diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index 00488425..67bbae12 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -1,66 +1,15 @@ #include "cru/ui/render/RenderObject.h" #include "cru/common/Logger.h" -#include "cru/platform/GraphicsBase.h" -#include "cru/platform/graphics/util/Painter.h" -#include "cru/ui/Base.h" #include "cru/ui/DebugFlags.h" +#include "cru/ui/controls/Control.h" #include "cru/ui/host/WindowHost.h" -#include <algorithm> -#include <string> -#include <string_view> -#include <vector> - namespace cru::ui::render { void RenderObject::SetAttachedControl(controls::Control* new_control) { + auto old_control = control_; control_ = new_control; - OnAttachedControlChanged(new_control); -} - -void RenderObject::AddChild(RenderObject* render_object, const Index position) { - Expects(child_mode_ != ChildMode::None); - Expects(!(child_mode_ == ChildMode::Single && children_.size() > 0)); - - Expects(render_object->GetParent() == - nullptr); // Render object already has a parent. - Expects(position >= 0); // Position index is less than 0. - Expects( - position <= - static_cast<Index>(children_.size())); // Position index is out of bound. - - children_.insert(children_.cbegin() + position, render_object); - render_object->SetParent(this); - render_object->SetWindowHostRecursive(GetWindowHost()); - OnAddChild(render_object, position); -} - -void RenderObject::RemoveChild(const Index position) { - Expects(position >= 0); // Position index is less than 0. - Expects(position < static_cast<Index>( - children_.size())); // Position index is out of bound. - - const auto i = children_.cbegin() + position; - const auto removed_child = *i; - children_.erase(i); - removed_child->SetParent(nullptr); - removed_child->SetWindowHostRecursive(nullptr); - OnRemoveChild(removed_child, position); -} - -RenderObject* RenderObject::GetFirstChild() const { - const auto& children = GetChildren(); - if (children.empty()) { - return nullptr; - } else { - return children.front(); - } -} - -void RenderObject::TraverseDescendants( - const std::function<void(RenderObject*)>& action) { - action(this); - for (auto child : children_) child->TraverseDescendants(action); + OnAttachedControlChanged(old_control, new_control); } Point RenderObject::GetTotalOffset() const { @@ -84,6 +33,31 @@ Point RenderObject::FromRootToContent(const Point& point) const { point.y - (offset.y + rect.top)}; } +void RenderObject::SetMargin(const Thickness& margin) { + margin_ = margin; + InvalidateLayout(); +} + +void RenderObject::SetPadding(const Thickness& padding) { + padding_ = padding; + InvalidateLayout(); +} + +void RenderObject::SetPreferredSize(const MeasureSize& preferred_size) { + preferred_size_ = preferred_size; + InvalidateLayout(); +} + +void RenderObject::SetMinSize(const MeasureSize& min_size) { + custom_measure_requirement_.min = min_size; + InvalidateLayout(); +} + +void RenderObject::SetMaxSize(const MeasureSize& max_size) { + custom_measure_requirement_.max = max_size; + InvalidateLayout(); +} + void RenderObject::Measure(const MeasureRequirement& requirement, const MeasureSize& preferred_size) { MeasureRequirement merged_requirement = @@ -98,23 +72,27 @@ void RenderObject::Measure(const MeasureRequirement& requirement, preferred_size.ToDebugString()); } - size_ = OnMeasureCore(merged_requirement, merged_preferred_size); + desired_size_ = OnMeasureCore(merged_requirement, merged_preferred_size); if constexpr (cru::ui::debug_flags::layout) { log::Debug(u"{} Measure ends :\nresult size: {}", - this->GetDebugPathInTree(), size_); + this->GetDebugPathInTree(), desired_size_); } - Ensures(size_.width >= 0); - Ensures(size_.height >= 0); + Ensures(desired_size_.width >= 0); + Ensures(desired_size_.height >= 0); } void RenderObject::Layout(const Point& offset) { if constexpr (cru::ui::debug_flags::layout) { log::Debug(u"{} Layout :\noffset: {} size: {}", this->GetDebugPathInTree(), - offset, GetSize()); + offset, desired_size_); } offset_ = offset; + size_ = desired_size_; + + OnResize(size_); + OnLayoutCore(); } @@ -122,67 +100,6 @@ Thickness RenderObject::GetOuterSpaceThickness() const { return margin_ + padding_; } -void RenderObject::Draw(platform::graphics::IPainter* painter) { - OnDrawCore(painter); -} - -RenderObject* RenderObject::GetSingleChild() const { - Expects(child_mode_ == ChildMode::Single); - const auto& children = GetChildren(); - if (children.empty()) - return nullptr; - else - return children.front(); -} - -void RenderObject::OnParentChanged(RenderObject* old_parent, - RenderObject* new_parent) { - CRU_UNUSED(old_parent) - CRU_UNUSED(new_parent) -} - -void RenderObject::OnAddChild(RenderObject* new_child, Index position) { - CRU_UNUSED(new_child) - CRU_UNUSED(position) - - InvalidateLayout(); - InvalidatePaint(); -} - -void RenderObject::OnRemoveChild(RenderObject* removed_child, Index position) { - CRU_UNUSED(removed_child) - CRU_UNUSED(position) - - InvalidateLayout(); - InvalidatePaint(); -} - -void RenderObject::DefaultDrawChildren(platform::graphics::IPainter* painter) { - for (const auto child : GetChildren()) { - auto offset = child->GetOffset(); - platform::graphics::util::WithTransform( - painter, platform::Matrix::Translation(offset.x, offset.y), - [child](auto p) { child->Draw(p); }); - } -} - -void RenderObject::DefaultDrawContent(platform::graphics::IPainter* painter) { - const auto content_rect = GetContentRect(); - - platform::graphics::util::WithTransform( - painter, Matrix::Translation(content_rect.left, content_rect.top), - [this](auto p) { this->OnDrawContent(p); }); -} - -void RenderObject::OnDrawCore(platform::graphics::IPainter* painter) { - DefaultDrawContent(painter); - DefaultDrawChildren(painter); -} - -void RenderObject::OnDrawContent(platform::graphics::IPainter* painter) { - CRU_UNUSED(painter); -} - Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement, const MeasureSize& preferred_size) { const Thickness outer_space = GetOuterSpaceThickness(); @@ -204,7 +121,7 @@ Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement, } void RenderObject::OnLayoutCore() { - Size total_size = GetSize(); + Size total_size = GetDesiredSize(); const Thickness outer_space = GetOuterSpaceThickness(); const Size space_size{outer_space.GetHorizontalTotal(), outer_space.GetVerticalTotal()}; @@ -231,10 +148,8 @@ void RenderObject::OnLayoutCore() { OnLayoutContent(content_rect); } -void RenderObject::OnAfterLayout() {} - Rect RenderObject::GetPaddingRect() const { - const auto size = GetSize(); + const auto size = GetDesiredSize(); Rect rect{Point{}, size}; rect = rect.Shrink(GetMargin()); rect.left = std::min(rect.left, size.width); @@ -245,7 +160,7 @@ Rect RenderObject::GetPaddingRect() const { } Rect RenderObject::GetContentRect() const { - const auto size = GetSize(); + const auto size = GetDesiredSize(); Rect rect{Point{}, size}; rect = rect.Shrink(GetMargin()); rect = rect.Shrink(GetPadding()); @@ -256,33 +171,37 @@ Rect RenderObject::GetContentRect() const { return rect; } -void RenderObject::SetParent(RenderObject* new_parent) { - const auto old_parent = parent_; - parent_ = new_parent; - OnParentChanged(old_parent, new_parent); +host::WindowHost* RenderObject::GetWindowHost() { + if (control_) { + return control_->GetWindowHost(); + } + return nullptr; } void RenderObject::InvalidateLayout() { - if (window_host_ != nullptr) window_host_->InvalidateLayout(); + if (auto window_host = GetWindowHost()) { + window_host->InvalidateLayout(); + } } void RenderObject::InvalidatePaint() { - if (window_host_ != nullptr) window_host_->InvalidatePaint(); + if (auto window_host = GetWindowHost()) { + window_host->InvalidatePaint(); + } } -constexpr std::u16string_view kUnamedName(u"UNNAMED"); - -std::u16string_view RenderObject::GetName() const { return kUnamedName; } +String kUnamedName(u"UNNAMED"); +String RenderObject::GetName() const { return kUnamedName; } -std::u16string RenderObject::GetDebugPathInTree() const { - std::vector<std::u16string_view> chain; +String RenderObject::GetDebugPathInTree() const { + std::vector<String> chain; const RenderObject* parent = this; while (parent != nullptr) { chain.push_back(parent->GetName()); parent = parent->GetParent(); } - std::u16string result(chain.back()); + String result(chain.back()); for (auto iter = chain.crbegin() + 1; iter != chain.crend(); ++iter) { result += u" -> "; result += *iter; @@ -290,17 +209,4 @@ std::u16string RenderObject::GetDebugPathInTree() const { return result; } - -void RenderObject::SetWindowHostRecursive(host::WindowHost* host) { - if (window_host_ != nullptr) { - detach_from_host_event_.Raise(nullptr); - } - window_host_ = host; - if (host != nullptr) { - attach_to_host_event_.Raise(nullptr); - } - for (const auto child : GetChildren()) { - child->SetWindowHostRecursive(host); - } -} } // namespace cru::ui::render diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index e3cc006b..e6389e16 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -452,7 +452,7 @@ bool HorizontalScrollBar::IsShowBar() { if (child == nullptr) return false; const auto view_rect = render_object_->GetViewRect(); - const auto child_size = child->GetSize(); + const auto child_size = child->GetDesiredSize(); if (view_rect.width >= child_size.width) return false; @@ -469,7 +469,7 @@ std::optional<Rect> HorizontalScrollBar::GetExpandedAreaRect( const auto child = render_object_->GetFirstChild(); const auto view_rect = render_object_->GetViewRect(); - const auto child_size = child->GetSize(); + const auto child_size = child->GetDesiredSize(); const float start_percentage = view_rect.left / child_size.width; const float length_percentage = view_rect.width / child_size.width; @@ -521,7 +521,7 @@ std::optional<Rect> HorizontalScrollBar::GetCollapsedThumbRect() { const auto child = render_object_->GetFirstChild(); const auto view_rect = render_object_->GetViewRect(); - const auto child_size = child->GetSize(); + const auto child_size = child->GetDesiredSize(); const float start_percentage = view_rect.left / child_size.width; const float length_percentage = view_rect.width / child_size.width; @@ -547,7 +547,7 @@ float HorizontalScrollBar::CalculateNewScrollPosition( auto thumb_head_end = scroll_area_end - thumb_original_rect.width; const auto child = render_object_->GetFirstChild(); - const auto child_size = child->GetSize(); + const auto child_size = child->GetDesiredSize(); new_thumb_start = std::clamp(new_thumb_start, scroll_area_start, thumb_head_end); @@ -601,7 +601,7 @@ bool VerticalScrollBar::IsShowBar() { if (child == nullptr) return false; const auto view_rect = render_object_->GetViewRect(); - const auto child_size = child->GetSize(); + const auto child_size = child->GetDesiredSize(); if (view_rect.height >= child_size.height) return false; @@ -618,7 +618,7 @@ std::optional<Rect> VerticalScrollBar::GetExpandedAreaRect( const auto child = render_object_->GetFirstChild(); const auto view_rect = render_object_->GetViewRect(); - const auto child_size = child->GetSize(); + const auto child_size = child->GetDesiredSize(); const float start_percentage = view_rect.top / child_size.height; const float length_percentage = view_rect.height / child_size.height; @@ -668,7 +668,7 @@ std::optional<Rect> VerticalScrollBar::GetCollapsedThumbRect() { const auto view_rect = render_object_->GetViewRect(); const auto padding_rect = render_object_->GetPaddingRect(); - const auto child_size = child->GetSize(); + const auto child_size = child->GetDesiredSize(); if (view_rect.height >= child_size.height) return std::nullopt; @@ -694,7 +694,7 @@ float VerticalScrollBar::CalculateNewScrollPosition( auto thumb_head_end = scroll_area_end - thumb_original_rect.height; const auto child = render_object_->GetFirstChild(); - const auto child_size = child->GetSize(); + const auto child_size = child->GetDesiredSize(); new_thumb_start = std::clamp(new_thumb_start, scroll_area_start, thumb_head_end); diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index 2400cc11..e2710de4 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -38,12 +38,19 @@ Point CoerceScroll(const Point& scroll_offset, const Size& content_size, } } // namespace -ScrollRenderObject::ScrollRenderObject() : RenderObject(ChildMode::Single) { +ScrollRenderObject::ScrollRenderObject() : RenderObject() { scroll_bar_delegate_ = std::make_unique<ScrollBarDelegate>(this); scroll_bar_delegate_->ScrollAttemptEvent()->AddHandler( [this](const struct Scroll& scroll) { this->ApplyScroll(scroll); }); } +void ScrollRenderObject::SetChild(RenderObject* new_child) { + if (child_ == new_child) return; + if (child_) child_->SetParent(nullptr); + child_ = new_child; + if (child_) child_->SetParent(this); +} + void ScrollRenderObject::ApplyScroll(const struct Scroll& scroll) { auto direction = scroll.direction; @@ -72,9 +79,9 @@ void ScrollRenderObject::ApplyScroll(const struct Scroll& scroll) { } RenderObject* ScrollRenderObject::HitTest(const Point& point) { - if (const auto child = GetSingleChild()) { - const auto offset = child->GetOffset(); - const auto r = child->HitTest(point - offset); + if (child_) { + const auto offset = child_->GetOffset(); + const auto r = child_->HitTest(point - offset); if (r != nullptr) return r; } @@ -82,23 +89,23 @@ RenderObject* ScrollRenderObject::HitTest(const Point& point) { return rect.IsPointInside(point) ? this : nullptr; } // namespace cru::ui::render -void ScrollRenderObject::OnDrawCore(platform::graphics::IPainter* painter) { - DefaultDrawContent(painter); - if (const auto child = GetSingleChild()) { +void ScrollRenderObject::Draw(platform::graphics::IPainter* painter) { + if (child_) { painter->PushLayer(this->GetContentRect()); - const auto offset = child->GetOffset(); - platform::graphics::util::WithTransform( - painter, Matrix::Translation(offset.x, offset.y), - [child](platform::graphics::IPainter* p) { child->Draw(p); }); + const auto offset = child_->GetOffset(); + painter->PushState(); + painter->ConcatTransform(Matrix::Translation(offset)); + child_->Draw(painter); + painter->PopState(); painter->PopLayer(); } scroll_bar_delegate_->DrawScrollBar(painter); } Point ScrollRenderObject::GetScrollOffset() { - if (const auto child = GetSingleChild()) + if (child_) return CoerceScroll(scroll_offset_, GetContentRect().GetSize(), - child->GetSize()); + child_->GetDesiredSize()); else return scroll_offset_; } @@ -164,12 +171,12 @@ void ScrollRenderObject::SetMouseWheelScrollEnabled(bool enable) { Size ScrollRenderObject::OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) { - if (const auto child = GetSingleChild()) { - child->Measure(MeasureRequirement{MeasureSize::NotSpecified(), - MeasureSize::NotSpecified()}, - MeasureSize::NotSpecified()); + if (child_) { + child_->Measure(MeasureRequirement{MeasureSize::NotSpecified(), + MeasureSize::NotSpecified()}, + MeasureSize::NotSpecified()); - Size result = requirement.Coerce(child->GetSize()); + Size result = requirement.Coerce(child_->GetDesiredSize()); if (preferred_size.width.IsSpecified()) { result.width = preferred_size.width.GetLengthOrUndefined(); } @@ -189,16 +196,17 @@ Size ScrollRenderObject::OnMeasureContent(const MeasureRequirement& requirement, } // namespace cru::ui::render void ScrollRenderObject::OnLayoutContent(const Rect& content_rect) { - if (const auto child = GetSingleChild()) { - child->Layout(content_rect.GetLeftTop() - GetScrollOffset()); + if (child_) { + child_->Layout(content_rect.GetLeftTop() - GetScrollOffset()); } } -void ScrollRenderObject::OnAttachedControlChanged(controls::Control* control) { - if (control) { - scroll_bar_delegate_->InstallHandlers(control); +void ScrollRenderObject::OnAttachedControlChanged( + controls::Control* old_control, controls::Control* new_control) { + if (new_control) { + scroll_bar_delegate_->InstallHandlers(new_control); if (is_mouse_wheel_enabled_) { - InstallMouseWheelHandler(control); + InstallMouseWheelHandler(new_control); } } else { InstallMouseWheelHandler(nullptr); @@ -248,7 +256,7 @@ bool ScrollRenderObject::HorizontalCanScrollUp() { bool ScrollRenderObject::HorizontalCanScrollDown() { return GetScrollOffset().x < - GetFirstChild()->GetSize().width - GetViewRect().width; + child_->GetDesiredSize().width - GetViewRect().width; } bool ScrollRenderObject::VerticalCanScrollUp() { @@ -257,6 +265,6 @@ bool ScrollRenderObject::VerticalCanScrollUp() { bool ScrollRenderObject::VerticalCanScrollDown() { return GetScrollOffset().y < - GetFirstChild()->GetSize().height - GetViewRect().height; + child_->GetDesiredSize().height - GetViewRect().height; } } // namespace cru::ui::render diff --git a/src/ui/render/StackLayoutRenderObject.cpp b/src/ui/render/StackLayoutRenderObject.cpp index ea65fe02..6c79716f 100644 --- a/src/ui/render/StackLayoutRenderObject.cpp +++ b/src/ui/render/StackLayoutRenderObject.cpp @@ -21,11 +21,12 @@ void StackLayoutRenderObject::SetDefaultVertialAlignment(Alignment alignment) { Size StackLayoutRenderObject::OnMeasureContent( const MeasureRequirement& requirement, const MeasureSize& preferred_size) { Size child_max_size; - for (const auto child : GetChildren()) { + for (int i = 0; i < GetChildCount(); i++) { + auto child = GetChildAt(i); child->Measure( MeasureRequirement(requirement.max, MeasureSize::NotSpecified()), MeasureSize::NotSpecified()); - const auto size = child->GetSize(); + const auto size = child->GetDesiredSize(); child_max_size.width = std::max(child_max_size.width, size.width); child_max_size.height = std::max(child_max_size.height, size.height); } @@ -33,7 +34,7 @@ Size StackLayoutRenderObject::OnMeasureContent( child_max_size = Max(preferred_size.GetSizeOr0(), child_max_size); child_max_size = Max(requirement.min.GetSizeOr0(), child_max_size); - for (Index i = 0; i < GetChildren().size(); ++i) { + for (Index i = 0; i < GetChildCount(); ++i) { auto child_layout_data = GetChildLayoutData(i); auto horizontal_stretch = child_layout_data.horizontal.value_or(default_horizontal_alignment_) == @@ -42,8 +43,8 @@ Size StackLayoutRenderObject::OnMeasureContent( child_layout_data.vertical.value_or(default_vertical_alignment_) == Alignment::Stretch; if (horizontal_stretch || vertical_stretch) { - auto child = GetChildren()[i]; - auto child_size = child->GetSize(); + auto child = GetChildAt(i); + auto child_size = child->GetDesiredSize(); MeasureRequirement child_requirement(child_size, child_size); if (horizontal_stretch) { child_requirement.min.width = child_requirement.max.width = @@ -63,12 +64,11 @@ Size StackLayoutRenderObject::OnMeasureContent( void StackLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { const auto count = GetChildCount(); - const auto& children = GetChildren(); for (int i = 0; i < count; i++) { - const auto child = children[i]; + const auto child = GetChildAt(i); const auto& layout_data = GetChildLayoutData(i); - const auto& size = child->GetSize(); + const auto& size = child->GetDesiredSize(); child->Layout(Point{ CalculateAnchorByAlignment( layout_data.horizontal.value_or(default_horizontal_alignment_), diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp index 675bac3c..ec5ce662 100644 --- a/src/ui/render/TextRenderObject.cpp +++ b/src/ui/render/TextRenderObject.cpp @@ -21,8 +21,6 @@ TextRenderObject::TextRenderObject( Expects(selection_brush); Expects(caret_brush); - SetChildMode(ChildMode::None); - brush.swap(brush_); font.swap(font_); selection_brush.swap(selection_brush_); @@ -176,16 +174,19 @@ RenderObject* TextRenderObject::HitTest(const Point& point) { return padding_rect.IsPointInside(point) ? this : nullptr; } -void TextRenderObject::OnDrawContent(platform::graphics::IPainter* painter) { +void TextRenderObject::Draw(platform::graphics::IPainter* painter) { if constexpr (debug_flags::draw) { log::TagDebug(log_tag, u"Begin to paint, total_offset: {}, size: {}, text_layout: " u"{}, brush: {}.", - this->GetTotalOffset(), this->GetSize(), + this->GetTotalOffset(), this->GetDesiredSize(), this->text_layout_->GetDebugString(), this->brush_->GetDebugString()); } + painter->PushState(); + painter->ConcatTransform(Matrix::Translation(GetOffset())); + if (this->selection_range_.has_value()) { const auto&& rects = text_layout_->TextRangeRect(this->selection_range_.value()); @@ -198,6 +199,8 @@ void TextRenderObject::OnDrawContent(platform::graphics::IPainter* painter) { if (this->draw_caret_ && this->caret_width_ != 0.0f) { painter->FillRectangle(GetCaretRectInContent(), this->caret_brush_.get()); } + + painter->PopState(); } Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement, @@ -229,6 +232,4 @@ Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement, void TextRenderObject::OnLayoutContent(const Rect& content_rect) { CRU_UNUSED(content_rect) } - -void TextRenderObject::OnAfterLayout() {} } // namespace cru::ui::render diff --git a/src/ui/render/TreeRenderObject.cpp b/src/ui/render/TreeRenderObject.cpp index bea5b9de..9bf74535 100644 --- a/src/ui/render/TreeRenderObject.cpp +++ b/src/ui/render/TreeRenderObject.cpp @@ -34,7 +34,6 @@ void TreeRenderObjectItem::RemoveItem(Index position) { } TreeRenderObject::TreeRenderObject() { - SetChildMode(ChildMode::None); root_item_ = new TreeRenderObjectItem(this, nullptr); } @@ -81,7 +80,7 @@ void TreeRenderObjectItemDraw(TreeRenderObjectItem* item, } } -void TreeRenderObject::OnDrawCore(platform::graphics::IPainter* painter) { +void TreeRenderObject::Draw(platform::graphics::IPainter* painter) { TreeRenderObjectItemDraw(root_item_, painter); } @@ -95,7 +94,7 @@ static Size MeasureTreeRenderObjectItem(MeasureSize max_size, MeasureSize::NotSpecified()); } - Size item_size = render_object ? render_object->GetSize() : Size{}; + Size item_size = render_object ? render_object->GetDesiredSize() : Size{}; if (max_size.width.IsSpecified()) { max_size.width = max_size.width.GetLengthOrUndefined() - tab_width; @@ -133,7 +132,7 @@ static void LayoutTreeRenderObjectItem(Rect rect, TreeRenderObjectItem* item, float item_height = 0.f; if (render_object) { render_object->Layout(rect.GetLeftTop()); - item_height = render_object->GetSize().height; + item_height = render_object->GetDesiredSize().height; } rect.left += tab_width; @@ -144,8 +143,9 @@ static void LayoutTreeRenderObjectItem(Rect rect, TreeRenderObjectItem* item, for (auto child : item->GetChildren()) { LayoutTreeRenderObjectItem(rect, child, tab_width); auto child_render_object = child->GetRenderObject(); - auto child_height = - child_render_object ? child_render_object->GetSize().height : 0.f; + auto child_height = child_render_object + ? child_render_object->GetDesiredSize().height + : 0.f; rect.top += child_height; rect.height -= child_height; } |