diff options
-rw-r--r-- | include/cru/ui/render/BorderRenderObject.hpp | 7 | ||||
-rw-r--r-- | include/cru/ui/render/MeasureRequirement.hpp | 24 | ||||
-rw-r--r-- | include/cru/ui/render/RenderObject.hpp | 6 | ||||
-rw-r--r-- | include/cru/ui/render/ScrollRenderObject.hpp | 4 | ||||
-rw-r--r-- | src/ui/render/BorderRenderObject.cpp | 37 | ||||
-rw-r--r-- | src/ui/render/FlexLayoutRenderObject.cpp | 2 | ||||
-rw-r--r-- | src/ui/render/RenderObject.cpp | 23 | ||||
-rw-r--r-- | src/ui/render/ScrollRenderObject.cpp | 59 |
8 files changed, 110 insertions, 52 deletions
diff --git a/include/cru/ui/render/BorderRenderObject.hpp b/include/cru/ui/render/BorderRenderObject.hpp index 45df89f9..b1fc4ba2 100644 --- a/include/cru/ui/render/BorderRenderObject.hpp +++ b/include/cru/ui/render/BorderRenderObject.hpp @@ -74,11 +74,10 @@ class BorderRenderObject : public RenderObject { void OnAfterLayout() override; - private: - RenderObject* GetChild() const { - return GetChildren().empty() ? nullptr : GetChildren()[0]; - } + Rect GetPaddingRect() const override; + Rect GetContentRect() const override; + private: void RecreateGeometry(); private: diff --git a/include/cru/ui/render/MeasureRequirement.hpp b/include/cru/ui/render/MeasureRequirement.hpp index 83de255d..661f40a9 100644 --- a/include/cru/ui/render/MeasureRequirement.hpp +++ b/include/cru/ui/render/MeasureRequirement.hpp @@ -6,11 +6,11 @@ namespace cru::ui::render { class MeasureLength { public: - struct tag_infinate_t {}; - constexpr static tag_infinate_t tag_infinate{}; + struct tag_not_specify_t {}; + constexpr static tag_not_specify_t tag_not_specify{}; constexpr MeasureLength() : MeasureLength(0) {} - constexpr MeasureLength(tag_infinate_t) : length_(-1) {} + constexpr MeasureLength(tag_not_specify_t) : length_(-1) {} constexpr MeasureLength(float length) : length_(length) { Expects(length >= 0); } @@ -25,11 +25,15 @@ class MeasureLength { ~MeasureLength() = default; - constexpr static MeasureLength Infinate() { - return MeasureLength{tag_infinate}; + constexpr static MeasureLength NotSpecify() { + return MeasureLength{tag_not_specify}; } - constexpr bool IsInfinate() const { return length_ < 0; } + // What not specify means depends on situation. + // For max size, it means no limit. + constexpr bool IsNotSpecify() const { return length_ < 0; } + + // If not specify max value of float is returned. constexpr float GetLength() const { return length_ < 0 ? std::numeric_limits<float>::max() : length_; } @@ -60,9 +64,9 @@ struct MeasureRequirement { : max_width(max_size.width), max_height(max_size.height) {} constexpr bool Satisfy(const Size& size) const { - if (!max_width.IsInfinate() && max_width.GetLength() < size.width) + if (!max_width.IsNotSpecify() && max_width.GetLength() < size.width) return false; - if (!max_height.IsInfinate() && max_height.GetLength() < size.height) + if (!max_height.IsNotSpecify() && max_height.GetLength() < size.height) return false; return true; } @@ -72,8 +76,8 @@ struct MeasureRequirement { } constexpr static MeasureRequirement Infinate() { - return MeasureRequirement{MeasureLength::Infinate(), - MeasureLength::Infinate()}; + return MeasureRequirement{MeasureLength::NotSpecify(), + MeasureLength::NotSpecify()}; } }; } // namespace cru::ui::render diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp index 9de2cc27..72716d2e 100644 --- a/include/cru/ui/render/RenderObject.hpp +++ b/include/cru/ui/render/RenderObject.hpp @@ -84,6 +84,8 @@ class RenderObject : public Object { void SetChildMode(ChildMode mode) { child_mode_ = mode; } protected: + RenderObject* GetSingleChild() const; + virtual void OnParentChanged(RenderObject* old_parent, RenderObject* new_parent); @@ -114,8 +116,8 @@ class RenderObject : public Object { virtual void OnAfterLayout(); static void NotifyAfterLayoutRecursive(RenderObject* render_object); - Rect GetPaddingRect() const; - Rect GetContentRect() const; + virtual Rect GetPaddingRect() const; + virtual Rect GetContentRect() const; private: void SetParent(RenderObject* new_parent); diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp index 924b8ca6..774501a1 100644 --- a/include/cru/ui/render/ScrollRenderObject.hpp +++ b/include/cru/ui/render/ScrollRenderObject.hpp @@ -17,8 +17,10 @@ class ScrollRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; - Point GetScrollOffset() { return scroll_offset_; } + // Return the coerced scroll offset. + Point GetScrollOffset(); void SetScrollOffset(const Point& offset); + Point GetRawScrollOffset() const { return scroll_offset_; } protected: // Logic: diff --git a/src/ui/render/BorderRenderObject.cpp b/src/ui/render/BorderRenderObject.cpp index fbc3c65f..ad0c6455 100644 --- a/src/ui/render/BorderRenderObject.cpp +++ b/src/ui/render/BorderRenderObject.cpp @@ -27,7 +27,7 @@ void BorderRenderObject::Draw(platform::graph::IPainter* painter) { painter->FillGeometry(geometry_.get(), border_brush_.get()); } } - if (const auto child = GetChild()) { + if (const auto child = GetSingleChild()) { auto offset = child->GetOffset(); platform::graph::util::WithTransform( painter, platform::Matrix::Translation(offset.x, offset.y), @@ -48,7 +48,7 @@ void BorderRenderObject::SetBorderStyle(const BorderStyle& style) { } RenderObject* BorderRenderObject::HitTest(const Point& point) { - if (const auto child = GetChild()) { + 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); @@ -90,7 +90,7 @@ Size BorderRenderObject::OnMeasureCore(const MeasureRequirement& requirement) { MeasureRequirement content_requirement = requirement; - if (!requirement.max_width.IsInfinate()) { + if (!requirement.max_width.IsNotSpecify()) { const auto max_width = requirement.max_width.GetLength(); if (coerced_space_size.width > max_width) { log::Warn( @@ -101,7 +101,7 @@ Size BorderRenderObject::OnMeasureCore(const MeasureRequirement& requirement) { content_requirement.max_width = max_width - coerced_space_size.width; } - if (!requirement.max_height.IsInfinate()) { + if (!requirement.max_height.IsNotSpecify()) { const auto max_height = requirement.max_height.GetLength(); if (coerced_space_size.height > max_height) { log::Warn( @@ -160,7 +160,7 @@ void BorderRenderObject::OnLayoutCore(const Size& size) { Size BorderRenderObject::OnMeasureContent( const MeasureRequirement& requirement) { - const auto child = GetChild(); + const auto child = GetSingleChild(); if (child) { child->Measure(requirement); return child->GetMeasuredSize(); @@ -170,7 +170,7 @@ Size BorderRenderObject::OnMeasureContent( } void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { - const auto child = GetChild(); + const auto child = GetSingleChild(); if (child) { child->Layout(content_rect); } @@ -178,6 +178,31 @@ void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { void BorderRenderObject::OnAfterLayout() { RecreateGeometry(); } +Rect BorderRenderObject::GetPaddingRect() const { + const auto size = GetSize(); + Rect rect{Point{}, size}; + rect = rect.Shrink(GetMargin()); + if (is_border_enabled_) rect = rect.Shrink(border_thickness_); + rect.left = std::min(rect.left, size.width); + rect.top = std::min(rect.top, size.height); + rect.width = std::max(rect.width, 0.0f); + rect.height = std::max(rect.height, 0.0f); + return rect; +} + +Rect BorderRenderObject::GetContentRect() const { + const auto size = GetSize(); + Rect rect{Point{}, size}; + rect = rect.Shrink(GetMargin()); + if (is_border_enabled_) rect = rect.Shrink(border_thickness_); + rect = rect.Shrink(GetPadding()); + rect.left = std::min(rect.left, size.width); + rect.top = std::min(rect.top, size.height); + rect.width = std::max(rect.width, 0.0f); + rect.height = std::max(rect.height, 0.0f); + return rect; +} + void BorderRenderObject::RecreateGeometry() { geometry_.reset(); border_outer_geometry_.reset(); diff --git a/src/ui/render/FlexLayoutRenderObject.cpp b/src/ui/render/FlexLayoutRenderObject.cpp index 98eda066..72341a64 100644 --- a/src/ui/render/FlexLayoutRenderObject.cpp +++ b/src/ui/render/FlexLayoutRenderObject.cpp @@ -47,7 +47,7 @@ Size FlexLayoutRenderObject::OnMeasureContent( const auto& children = GetChildren(); Index children_count = children.size(); - if (!main_max_length.IsInfinate()) { + if (!main_max_length.IsNotSpecify()) { float remain_main_length = main_max_length.GetLength(); for (Index i = 0; i < children_count; i++) { diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index cdc3032f..abd5e1c4 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -73,6 +73,15 @@ void RenderObject::Layout(const Rect& rect) { OnLayoutCore(rect.GetSize()); } +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) @@ -104,7 +113,7 @@ Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement) { MeasureRequirement content_requirement = requirement; - if (!requirement.max_width.IsInfinate()) { + if (!requirement.max_width.IsNotSpecify()) { const auto max_width = requirement.max_width.GetLength(); if (coerced_space_size.width > max_width) { log::Warn( @@ -115,7 +124,7 @@ Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement) { content_requirement.max_width = max_width - coerced_space_size.width; } - if (!requirement.max_height.IsInfinate()) { + if (!requirement.max_height.IsNotSpecify()) { const auto max_height = requirement.max_height.GetLength(); if (coerced_space_size.height > max_height) { log::Warn( @@ -166,17 +175,23 @@ void RenderObject::OnLayoutCore(const Size& size) { void RenderObject::OnAfterLayout() {} Rect RenderObject::GetPaddingRect() const { - Rect rect{Point{}, GetSize()}; + const auto size = GetSize(); + Rect rect{Point{}, size}; rect = rect.Shrink(GetMargin()); + rect.left = std::min(rect.left, size.width); + rect.top = std::min(rect.top, size.height); rect.width = std::max(rect.width, 0.0f); rect.height = std::max(rect.height, 0.0f); return rect; } Rect RenderObject::GetContentRect() const { - Rect rect{Point{}, GetSize()}; + const auto size = GetSize(); + Rect rect{Point{}, size}; rect = rect.Shrink(GetMargin()); rect = rect.Shrink(GetPadding()); + rect.left = std::min(rect.left, size.width); + rect.top = std::min(rect.top, size.height); rect.width = std::max(rect.width, 0.0f); rect.height = std::max(rect.height, 0.0f); return rect; diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index f1df9d8b..98254690 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -2,6 +2,7 @@ #include "cru/platform/graph/Painter.hpp" #include "cru/platform/graph/util/Painter.hpp" +#include "cru/ui/render/LayoutUtility.hpp" #include <algorithm> @@ -9,13 +10,11 @@ namespace cru::ui::render { namespace { // This method assumes margin offset is already considered. // It promises that it won't return negetive value. -Point CalculateChildOffsetOfScroll(const Point& scroll_offset, - const Size& content_area_size, - const Thickness& padding, - const Size& child_size) { +Point CoerceScroll(const Point& scroll_offset, const Size& content_size, + const Size& child_size) { Point result(scroll_offset); - Point max_scroll(child_size - content_area_size); + Point max_scroll(child_size - content_size); max_scroll.x = std::max(max_scroll.x, 0.f); max_scroll.y = std::max(max_scroll.y, 0.f); @@ -29,50 +28,62 @@ Point CalculateChildOffsetOfScroll(const Point& scroll_offset, coerce(result.x, scroll_offset.x); coerce(result.y, scroll_offset.y); - result.x += padding.left; - result.y += padding.top; - return result; } } // namespace void ScrollRenderObject::Draw(platform::graph::IPainter* painter) { - for (auto child : this->GetChildren()) { + if (const auto child = GetSingleChild()) { painter->PushLayer(this->GetPaddingRect()); - const auto true_offset = - CalculateChildOffsetOfScroll(scroll_offset_, GetContentRect().GetSize(), - GetPadding(), child->GetSize()); + const auto offset = child->GetOffset(); platform::graph::util::WithTransform( - painter, Matrix::Translation(true_offset.x, true_offset.y), + painter, Matrix::Translation(offset.x, offset.y), [child](platform::graph::IPainter* p) { child->Draw(p); }); painter->PopLayer(); } } RenderObject* ScrollRenderObject::HitTest(const Point& point) { + if (const auto child = GetSingleChild()) { + const auto offset = child->GetOffset(); + const auto r = child->HitTest(point - offset); + if (r != nullptr) return r; + } + const auto rect = GetPaddingRect(); return rect.IsPointInside(point) ? this : nullptr; +} // namespace cru::ui::render + +Point ScrollRenderObject::GetScrollOffset() { + if (const auto child = GetSingleChild()) + return CoerceScroll(scroll_offset_, GetContentRect().GetSize(), + child->GetSize()); + else + return scroll_offset_; } void ScrollRenderObject::SetScrollOffset(const Point& offset) { scroll_offset_ = offset; - InvalidatePaint(); + InvalidateLayout(); } Size ScrollRenderObject::OnMeasureContent( const MeasureRequirement& requirement) { - CRU_UNUSED(requirement); - // TODO! - // const auto& children = GetChildren(); - // if (children.empty()) return Size{}; - // const auto child = children.front(); - // child->Measure(MeasureRequirement::Infinate()); - // const auto preferred_size = child->GetMeasuredSize(); - return Size{}; + if (const auto child = GetSingleChild()) { + child->Measure(MeasureRequirement::Infinate()); + const auto preferred_size = child->GetMeasuredSize(); + return Min(preferred_size, requirement.GetMaxSize()); + } else { + return Size{}; + } } // namespace cru::ui::render void ScrollRenderObject::OnLayoutContent(const Rect& content_rect) { - CRU_UNUSED(content_rect); - // TODO! + if (const auto child = GetSingleChild()) { + const auto child_size = child->GetMeasuredSize(); + const auto true_scroll = + CoerceScroll(scroll_offset_, content_rect.GetSize(), child_size); + child->Layout(Rect{content_rect.GetLeftTop() - true_scroll, child_size}); + } } } // namespace cru::ui::render |