diff options
author | crupest <crupest@outlook.com> | 2020-06-25 23:44:43 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-06-25 23:44:43 +0800 |
commit | a48ff30f9d4ac8ae6cc9adef4a07f8d9beb69e7a (patch) | |
tree | 077a74943214da7627cca2ba9966d90d5bed144a | |
parent | ce0ae2c3727f83f1943d528b006eec94ad80ece9 (diff) | |
download | cru-a48ff30f9d4ac8ae6cc9adef4a07f8d9beb69e7a.tar.gz cru-a48ff30f9d4ac8ae6cc9adef4a07f8d9beb69e7a.tar.bz2 cru-a48ff30f9d4ac8ae6cc9adef4a07f8d9beb69e7a.zip |
Write layout logic at half way.
22 files changed, 711 insertions, 408 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index 3f6f33c8..0c147ac6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -72,6 +72,10 @@ "string_span": "cpp", "numeric": "cpp", "pointers": "cpp", - "gsl_assert": "cpp" + "gsl_assert": "cpp", + "cstdarg": "cpp", + "locale": "cpp", + "xlocbuf": "cpp", + "xlocmes": "cpp" } } diff --git a/include/cru/ui/render/BorderRenderObject.hpp b/include/cru/ui/render/BorderRenderObject.hpp index b1fc4ba2..6c33473a 100644 --- a/include/cru/ui/render/BorderRenderObject.hpp +++ b/include/cru/ui/render/BorderRenderObject.hpp @@ -67,9 +67,11 @@ class BorderRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; protected: - Size OnMeasureCore(const MeasureRequirement& requirement) override; - void OnLayoutCore(const Size& size) override; - Size OnMeasureContent(const MeasureRequirement& requirement) override; + Size OnMeasureCore(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; + void OnLayoutCore() override; + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; void OnAfterLayout() override; diff --git a/include/cru/ui/render/CanvasRenderObject.hpp b/include/cru/ui/render/CanvasRenderObject.hpp index 402302cb..323b4800 100644 --- a/include/cru/ui/render/CanvasRenderObject.hpp +++ b/include/cru/ui/render/CanvasRenderObject.hpp @@ -2,9 +2,9 @@ #include "RenderObject.hpp" namespace cru::ui::render { -// The measure logic for `CanvasRenderObject` is that you set a desired size by -// `SetDesiredSize` (not `SetPreferredSize`) and it will compare desired size -// and available size and use the smaller one (by `Min`). +// Layout logic: +// If no preferred size is set. Then (100, 100) is used and then coerced to +// required range. class CanvasRenderObject : public RenderObject { public: CanvasRenderObject(); @@ -21,14 +21,11 @@ class CanvasRenderObject : public RenderObject { Size GetDesiredSize() const { return desired_size_; } - // Set the desired size. This is the content size excluding padding and - // margin. - void SetDesiredSize(const Size& new_size) { desired_size_ = new_size; } - IEvent<CanvasPaintEventArgs>* PaintEvent() { return &paint_event_; } protected: - Size OnMeasureContent(const MeasureRequirement& requirement) override; + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; private: diff --git a/include/cru/ui/render/FlexLayoutRenderObject.hpp b/include/cru/ui/render/FlexLayoutRenderObject.hpp index 217ca6cb..f82bffad 100644 --- a/include/cru/ui/render/FlexLayoutRenderObject.hpp +++ b/include/cru/ui/render/FlexLayoutRenderObject.hpp @@ -31,7 +31,7 @@ class FlexLayoutRenderObject : public LayoutRenderObject<FlexChildLayoutData> { } protected: - Size OnMeasureContent(const MeasureRequirement& requirement) override; + Size OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; private: diff --git a/include/cru/ui/render/LayoutUtility.hpp b/include/cru/ui/render/LayoutUtility.hpp deleted file mode 100644 index 63d13fd3..00000000 --- a/include/cru/ui/render/LayoutUtility.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "Base.hpp" - -namespace cru::ui::render { -Size Min(const Size& left, const Size& right); -Size Max(const Size& left, const Size& right); -} // namespace cru::ui::render diff --git a/include/cru/ui/render/MeasureRequirement.hpp b/include/cru/ui/render/MeasureRequirement.hpp index 661f40a9..5af5a057 100644 --- a/include/cru/ui/render/MeasureRequirement.hpp +++ b/include/cru/ui/render/MeasureRequirement.hpp @@ -1,23 +1,34 @@ #pragma once #include "Base.hpp" +#include <algorithm> #include <limits> namespace cru::ui::render { -class MeasureLength { +constexpr Size Min(const Size& left, const Size& right) { + return Size{std::min(left.width, right.width), + std::min(left.height, right.height)}; +} + +constexpr Size Max(const Size& left, const Size& right) { + return Size{std::max(left.width, right.width), + std::max(left.height, right.height)}; +} + +class MeasureLength final { public: struct tag_not_specify_t {}; constexpr static tag_not_specify_t tag_not_specify{}; - constexpr MeasureLength() : MeasureLength(0) {} + constexpr MeasureLength() : MeasureLength(tag_not_specify) {} constexpr MeasureLength(tag_not_specify_t) : length_(-1) {} constexpr MeasureLength(float length) : length_(length) { Expects(length >= 0); } MeasureLength(const MeasureLength& other) = default; - MeasureLength& operator=(const MeasureLength& other) = default; - MeasureLength& operator=(float length) { + constexpr MeasureLength& operator=(const MeasureLength& other) = default; + constexpr MeasureLength& operator=(float length) { Expects(length >= 0); length_ = length; return *this; @@ -25,17 +36,44 @@ class MeasureLength { ~MeasureLength() = default; - constexpr static MeasureLength NotSpecify() { + constexpr static MeasureLength NotSpecified() { return MeasureLength{tag_not_specify}; } - // What not specify means depends on situation. - // For max size, it means no limit. - constexpr bool IsNotSpecify() const { return length_ < 0; } + constexpr bool IsSpecified() const { return length_ >= 0.0f; } + + // What not specified means depends on situation. + constexpr bool IsNotSpecified() const { return length_ < 0.0f; } + + constexpr float GetLengthOr(float value) const { + return length_ < 0 ? value : length_; + } // If not specify max value of float is returned. - constexpr float GetLength() const { - return length_ < 0 ? std::numeric_limits<float>::max() : length_; + constexpr float GetLengthOrMax() const { + return GetLengthOr(std::numeric_limits<float>::max()); + } + + // If not specify 0 is returned. + constexpr float GetLengthOr0() const { return GetLengthOr(0.f); } + + // If not specify, return value is undefined. + constexpr float GetLengthOrUndefined() const { return length_; } + + // Not operator overload because this semantics is not very clear. + constexpr MeasureLength Plus(float length) const { + if (IsSpecified()) + return length_ + length; + else + return NotSpecified(); + } + + // Not operator overload because this semantics is not very clear. + constexpr MeasureLength Minus(float length) const { + if (IsSpecified()) + return std::max(length_ - length, 0.f); + else + return NotSpecified(); } constexpr bool operator==(MeasureLength other) const { @@ -46,38 +84,151 @@ class MeasureLength { return !operator==(other); } + constexpr static MeasureLength Min(MeasureLength left, MeasureLength right) { + if (left.IsNotSpecified()) { + if (right.IsNotSpecified()) + return NotSpecified(); + else + return right; + } else { + if (left.IsNotSpecified()) + return left; + else + return std::min(left.length_, right.length_); + } + } + + constexpr static MeasureLength Max(MeasureLength left, MeasureLength right) { + if (left.IsNotSpecified()) { + if (right.IsNotSpecified()) + return NotSpecified(); + else + return right; + } else { + if (left.IsNotSpecified()) + return left; + else + return std::max(left.length_, right.length_); + } + } + private: - // -1 for infinate + // -1 for not specify float length_; }; +struct MeasureSize { + MeasureLength width; + MeasureLength height; + + constexpr MeasureSize() = default; + constexpr MeasureSize(MeasureLength width, MeasureLength height) + : width(width), height(height) {} + constexpr MeasureSize(const Size& size) + : width(size.width), height(size.height) {} + constexpr MeasureSize(const MeasureSize& other) = default; + + MeasureSize& operator=(const MeasureSize& other) = default; + constexpr MeasureSize& operator=(const Size& other) { + width = other.width; + height = other.height; + return *this; + } + + constexpr Size GetSizeOrMax() const { + return Size{width.GetLengthOrMax(), height.GetLengthOrMax()}; + } + + constexpr Size GetSizeOr0() const { + return Size{width.GetLengthOr0(), height.GetLengthOr0()}; + } + + constexpr MeasureSize Plus(const Size& size) const { + return MeasureSize{width.Plus(size.width), height.Plus(size.height)}; + } + + constexpr MeasureSize Minus(const Size& size) const { + return MeasureSize{width.Minus(size.width), height.Minus(size.height)}; + } + + constexpr MeasureSize OverrideBy(const MeasureSize& size) const { + return MeasureSize{ + size.width.IsSpecified() ? size.width.GetLengthOrUndefined() + : this->width, + size.height.IsSpecified() ? size.height.GetLengthOrUndefined() + : this->height, + }; + } + + constexpr static MeasureSize NotSpecified() { + return MeasureSize{MeasureLength::NotSpecified(), + MeasureLength::NotSpecified()}; + } + + constexpr static MeasureSize Min(const MeasureSize& left, + const MeasureSize& right) { + return MeasureSize{MeasureLength::Min(left.width, right.width), + MeasureLength::Min(left.height, right.height)}; + } + + constexpr static MeasureSize Max(const MeasureSize& left, + const MeasureSize& right) { + return MeasureSize{MeasureLength::Max(left.width, right.width), + MeasureLength::Max(left.height, right.height)}; + } +}; + struct MeasureRequirement { - MeasureLength max_width; - MeasureLength max_height; + MeasureSize max; + MeasureSize min; constexpr MeasureRequirement() = default; - constexpr MeasureRequirement(MeasureLength max_width, - MeasureLength max_height) - : max_width(max_width), max_height(max_height) {} - - constexpr MeasureRequirement(const Size& max_size) - : max_width(max_size.width), max_height(max_size.height) {} + constexpr MeasureRequirement(const MeasureSize& max, const MeasureSize& min) + : max(max), min(min) {} constexpr bool Satisfy(const Size& size) const { - if (!max_width.IsNotSpecify() && max_width.GetLength() < size.width) - return false; - if (!max_height.IsNotSpecify() && max_height.GetLength() < size.height) - return false; - return true; + return max.width.GetLengthOrMax() >= size.width && + max.height.GetLengthOrMax() >= size.height && + min.width.GetLengthOr0() <= size.width && + min.height.GetLengthOr0() <= size.height; + } + + constexpr MeasureRequirement Normalize() const { + MeasureRequirement result = *this; + if (result.min.width.GetLengthOr0() > result.max.width.GetLengthOrMax()) + result.min.width = result.max.width; + + if (result.min.height.GetLengthOr0() > result.max.height.GetLengthOrMax()) + result.min.height = result.max.height; + return result; + } + + constexpr Size Coerce(const Size& size) const { + // This order guarentees result is the same as normalized. + return Min(Max(size, min.GetSizeOr0()), max.GetSizeOrMax()); } - constexpr Size GetMaxSize() const { - return Size{max_width.GetLength(), max_height.GetLength()}; + constexpr MeasureSize Coerce(const MeasureSize& size) const { + MeasureSize result = size; + if (result.width.IsSpecified()) + // This order guarentees result is the same as normalized. + result.width = std::min(std::max(min.width.GetLengthOr0(), + result.width.GetLengthOrUndefined()), + max.width.GetLengthOrMax()); + + if (result.height.IsSpecified()) + // This order guarentees result is the same as normalized. + result.height = std::min(std::max(min.height.GetLengthOr0(), + result.height.GetLengthOrUndefined()), + max.height.GetLengthOrMax()); + + return result; } - constexpr static MeasureRequirement Infinate() { - return MeasureRequirement{MeasureLength::NotSpecify(), - MeasureLength::NotSpecify()}; + constexpr static MeasureRequirement Merge(const MeasureRequirement& left, + const MeasureRequirement& right) { + return MeasureRequirement{MeasureSize::Min(left.max, right.max), + MeasureSize::Max(left.min, right.min)}; } }; } // namespace cru::ui::render diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp index 72716d2e..823eabd6 100644 --- a/include/cru/ui/render/RenderObject.hpp +++ b/include/cru/ui/render/RenderObject.hpp @@ -10,6 +10,23 @@ namespace cru::ui::render { // 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::graph::IPainter* painter) override; @@ -59,16 +76,46 @@ class RenderObject : public Object { Point FromRootToContent(const Point& point) const; Thickness GetMargin() const { return margin_; } - void SetMargin(const Thickness& margin) { margin_ = margin; } + void SetMargin(const Thickness& margin) { + margin_ = margin; + InvalidateLayout(); + } Thickness GetPadding() const { return padding_; } - void SetPadding(const Thickness& padding) { padding_ = 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(); + } - Size GetMeasuredSize() const { return measured_size_; } + MeasureSize GetMaxSize() const { return custom_measure_requirement_.max; } + void SetMaxSize(const MeasureSize& max_size) { + custom_measure_requirement_.max = max_size; + InvalidateLayout(); + } - void Measure(const MeasureRequirement& requirement); - // Size of rect must not be negative. - void Layout(const Rect& rect); + MeasureRequirement GetCustomMeasureRequirement() const { + return custom_measure_requirement_; + } + + // This will call OnMeasureCore and 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 void Draw(platform::graph::IPainter* painter) = 0; @@ -95,21 +142,25 @@ class RenderObject : public Object { virtual void OnRemoveChild(RenderObject* removed_child, Index position); // Size measure including margin and padding. Please reduce margin and padding - // or other custom things and pass the result content measure requirement to - // OnMeasureContent. - // Return value must not be negative and not bigger than requirement. - virtual Size OnMeasureCore(const MeasureRequirement& requirement); - - // Size including margin and padding. Please reduce margin and padding or - // other custom things and pass the result content rect to OnLayoutContent. - // Parameter size are never negative. - virtual void OnLayoutCore(const Size& size); - - // Do not consider margin or padding in this method because they are already - // considered in OnMeasureCore. Returned size should never be bigger than - // requirement. - virtual Size OnMeasureContent(const MeasureRequirement& requirement) = 0; - + // 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. Preferred size may not be in range so need to be + // coerced. + 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; @@ -136,9 +187,10 @@ class RenderObject : public Object { Point offset_{}; Size size_{}; + MeasureSize preferred_size_; + MeasureRequirement custom_measure_requirement_; + Thickness margin_{}; Thickness padding_{}; - - Size measured_size_{}; }; } // namespace cru::ui::render diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp index 774501a1..9799c0d9 100644 --- a/include/cru/ui/render/ScrollRenderObject.hpp +++ b/include/cru/ui/render/ScrollRenderObject.hpp @@ -27,7 +27,7 @@ class ScrollRenderObject : public RenderObject { // If available size is bigger than child's preferred size, then child's // preferred size is taken. // If not, all available size is taken while forming a scroll area. - Size OnMeasureContent(const MeasureRequirement& requirement) override; + Size OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; private: diff --git a/include/cru/ui/render/StackLayoutRenderObject.hpp b/include/cru/ui/render/StackLayoutRenderObject.hpp index 2320e1ca..902c9d5c 100644 --- a/include/cru/ui/render/StackLayoutRenderObject.hpp +++ b/include/cru/ui/render/StackLayoutRenderObject.hpp @@ -11,7 +11,7 @@ class StackLayoutRenderObject ~StackLayoutRenderObject() = default; protected: - Size OnMeasureContent(const MeasureRequirement& requirement) override; + Size OnMeasureContent(const MeasureRequirement& requirement, const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; }; } // namespace cru::ui::render diff --git a/include/cru/ui/render/TextRenderObject.hpp b/include/cru/ui/render/TextRenderObject.hpp index 96bb5997..efa8c2b8 100644 --- a/include/cru/ui/render/TextRenderObject.hpp +++ b/include/cru/ui/render/TextRenderObject.hpp @@ -4,6 +4,19 @@ #include <string> namespace cru::ui::render { +// Layout logic: +// 1. If preferred width is set then it is taken to do a text measure. If not +// set, then max width is taken to do that. +// 2. If the actual width of text after measure exceeds the required width, +// coerced value is returned and an error is reported. If preferred width is set +// and actual width is smaller than that, then preferred width is used. If +// preferred width is not set, then the same thing is applied to min width. +// 3. If actual height of text is bigger than max height, an error is reported +// and coerced value is returned. Or height is lifted up to be at least +// preferred size if set or min height. +// +// If the result layout box is bigger than actual text box, then text is center +// aligned. class TextRenderObject : public RenderObject { public: constexpr static float default_caret_width = 2; @@ -64,7 +77,9 @@ class TextRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; protected: - Size OnMeasureContent(const MeasureRequirement& requirement) override; + // 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; diff --git a/include/cru/ui/render/WindowRenderObject.hpp b/include/cru/ui/render/WindowRenderObject.hpp index d993dc58..d64a216d 100644 --- a/include/cru/ui/render/WindowRenderObject.hpp +++ b/include/cru/ui/render/WindowRenderObject.hpp @@ -16,7 +16,8 @@ class WindowRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; protected: - Size OnMeasureContent(const MeasureRequirement& requirement) override; + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; private: diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 68efa903..9043974e 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -23,7 +23,6 @@ add_library(cru_ui STATIC render/BorderRenderObject.cpp render/CanvasRenderObject.cpp render/FlexLayoutRenderObject.cpp - render/LayoutUtility.cpp render/RenderObject.cpp render/ScrollRenderObject.cpp render/StackLayoutRenderObject.cpp @@ -53,7 +52,6 @@ target_sources(cru_ui PUBLIC ${CRU_UI_INCLUDE_DIR}/render/CanvasRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/FlexLayoutRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/LayoutRenderObject.hpp - ${CRU_UI_INCLUDE_DIR}/render/LayoutUtility.hpp ${CRU_UI_INCLUDE_DIR}/render/MeasureRequirement.hpp ${CRU_UI_INCLUDE_DIR}/render/RenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/ScrollRenderObject.hpp diff --git a/src/ui/UiHost.cpp b/src/ui/UiHost.cpp index 7047d43f..09f8fd9b 100644 --- a/src/ui/UiHost.cpp +++ b/src/ui/UiHost.cpp @@ -166,8 +166,11 @@ void UiHost::Relayout() { const auto client_size = native_window ? native_window->GetClientSize() : Size{100, 100}; // a reasonable assumed size - root_render_object_->Measure(client_size); - root_render_object_->Layout(Rect{Point{}, client_size}); + root_render_object_->Measure( + render::MeasureRequirement{client_size, + render::MeasureSize::NotSpecified()}, + render::MeasureSize::NotSpecified()); + root_render_object_->Layout(Point{}); } bool UiHost::RequestFocusFor(Control* control) { diff --git a/src/ui/render/BorderRenderObject.cpp b/src/ui/render/BorderRenderObject.cpp index ad0c6455..74e50561 100644 --- a/src/ui/render/BorderRenderObject.cpp +++ b/src/ui/render/BorderRenderObject.cpp @@ -73,9 +73,10 @@ RenderObject* BorderRenderObject::HitTest(const Point& point) { } } -Size BorderRenderObject::OnMeasureCore(const MeasureRequirement& requirement) { +Size BorderRenderObject::OnMeasureCore(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) { if (!is_border_enabled_) { - return RenderObject::OnMeasureCore(requirement); + return RenderObject::OnMeasureCore(requirement, preferred_size); } const auto margin = GetMargin(); @@ -90,36 +91,51 @@ Size BorderRenderObject::OnMeasureCore(const MeasureRequirement& requirement) { MeasureRequirement content_requirement = requirement; - if (!requirement.max_width.IsNotSpecify()) { - const auto max_width = requirement.max_width.GetLength(); + if (!requirement.max.width.IsNotSpecified()) { + const auto max_width = requirement.max.width.GetLengthOrMax(); if (coerced_space_size.width > max_width) { log::Warn( - "Measure: horizontal length of padding and margin is bigger than " - "available length."); + "BorderRenderObject: During measure, horizontal length of padding, " + "border and margin is bigger than required max length."); coerced_space_size.width = max_width; } - content_requirement.max_width = max_width - coerced_space_size.width; + content_requirement.max.width = max_width - coerced_space_size.width; } - if (!requirement.max_height.IsNotSpecify()) { - const auto max_height = requirement.max_height.GetLength(); + if (!requirement.min.width.IsNotSpecified()) { + const auto min_width = requirement.min.width.GetLengthOr0(); + content_requirement.min.width = std::max(0.f, min_width - space_size.width); + } + + if (!requirement.max.height.IsNotSpecified()) { + const auto max_height = requirement.max.height.GetLengthOrMax(); if (coerced_space_size.height > max_height) { log::Warn( - "Measure: horizontal length of padding and margin is bigger than " - "available length."); + "BorderRenderObject: During measure, vertical length of padding, " + "border and margin is bigger than required max length."); coerced_space_size.height = max_height; } - content_requirement.max_height = max_height - coerced_space_size.height; + content_requirement.max.height = max_height - coerced_space_size.height; } - const auto content_size = OnMeasureContent(content_requirement); + if (!requirement.min.height.IsNotSpecified()) { + const auto min_height = requirement.min.height.GetLengthOr0(); + content_requirement.min.height = + std::max(0.f, min_height - space_size.height); + } + + MeasureSize content_preferred_size = + content_requirement.Coerce(preferred_size.Minus(space_size)); + + const auto content_size = + OnMeasureContent(content_requirement, content_preferred_size); return coerced_space_size + content_size; -} // namespace cru::ui::render +} -void BorderRenderObject::OnLayoutCore(const Size& size) { +void BorderRenderObject::OnLayoutCore() { if (!is_border_enabled_) { - return RenderObject::OnLayoutCore(size); + return RenderObject::OnLayoutCore(); } const auto margin = GetMargin(); @@ -129,18 +145,20 @@ void BorderRenderObject::OnLayoutCore(const Size& size) { margin.GetVerticalTotal() + padding.GetVerticalTotal() + border_thickness_.GetVerticalTotal()}; + const auto size = GetSize(); + auto content_size = size - space_size; if (content_size.width < 0) { log::Warn( - "Layout: horizontal length of padding, border and margin is bigger " - "than available length."); + "BorderRenderObject: During layout, horizontal length of padding, " + "border and margin is bigger than available length."); content_size.width = 0; } if (content_size.height < 0) { log::Warn( - "Layout: vertical length of padding, border and margin is bigger " - "than available length."); + "BorderRenderObject: During layout, vertical length of padding, " + "border and margin is bigger than available length."); content_size.height = 0; } @@ -158,12 +176,12 @@ void BorderRenderObject::OnLayoutCore(const Size& size) { OnLayoutContent(content_rect); } -Size BorderRenderObject::OnMeasureContent( - const MeasureRequirement& requirement) { +Size BorderRenderObject::OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) { const auto child = GetSingleChild(); if (child) { - child->Measure(requirement); - return child->GetMeasuredSize(); + child->Measure(requirement, preferred_size); + return child->GetSize(); } else { return Size{}; } @@ -172,7 +190,7 @@ Size BorderRenderObject::OnMeasureContent( void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { const auto child = GetSingleChild(); if (child) { - child->Layout(content_rect); + child->Layout(content_rect.GetLeftTop()); } } diff --git a/src/ui/render/CanvasRenderObject.cpp b/src/ui/render/CanvasRenderObject.cpp index 72eb3570..211d1fce 100644 --- a/src/ui/render/CanvasRenderObject.cpp +++ b/src/ui/render/CanvasRenderObject.cpp @@ -1,7 +1,5 @@ #include "cru/ui/render/CanvasRenderObject.hpp" -#include "cru/ui/render/LayoutUtility.hpp" - namespace cru::ui::render { CanvasRenderObject::CanvasRenderObject() : RenderObject(ChildMode::None) {} @@ -18,9 +16,10 @@ RenderObject* CanvasRenderObject::HitTest(const Point& point) { return padding_rect.IsPointInside(point) ? this : nullptr; } -Size CanvasRenderObject::OnMeasureContent( - const MeasureRequirement& requirement) { - return Min(requirement.GetMaxSize(), GetDesiredSize()); +Size CanvasRenderObject::OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) { + return requirement.Coerce(Size{preferred_size.width.GetLengthOr(100), + preferred_size.height.GetLengthOr(100)}); } void CanvasRenderObject::OnLayoutContent(const Rect& content_rect) { diff --git a/src/ui/render/FlexLayoutRenderObject.cpp b/src/ui/render/FlexLayoutRenderObject.cpp index 72341a64..fcaf6f9a 100644 --- a/src/ui/render/FlexLayoutRenderObject.cpp +++ b/src/ui/render/FlexLayoutRenderObject.cpp @@ -7,194 +7,207 @@ namespace cru::ui::render { Size FlexLayoutRenderObject::OnMeasureContent( - const MeasureRequirement& requirement) { - const bool horizontal = (direction_ == FlexDirection::Horizontal || - direction_ == FlexDirection::HorizontalReverse); - - const auto main_max_length = - horizontal ? requirement.max_width : requirement.max_height; - const auto cross_max_length = - horizontal ? requirement.max_height : requirement.max_width; - - std::function<float(const Size&)> get_main_length; - std::function<float(const Size&)> get_cross_length; - std::function<void(Size&, const Size&)> calculate_result_size; - std::function<MeasureRequirement(MeasureLength main, MeasureLength cross)> - create_requirement; - - if (horizontal) { - get_main_length = [](const Size& size) { return size.width; }; - get_cross_length = [](const Size& size) { return size.height; }; - calculate_result_size = [](Size& result, const Size& child_size) { - result.width += child_size.width; - result.height = std::max(result.height, child_size.height); - }; - create_requirement = [](MeasureLength main, MeasureLength cross) { - return MeasureRequirement{main, cross}; - }; - } else { - get_main_length = [](const Size& size) { return size.height; }; - get_cross_length = [](const Size& size) { return size.width; }; - calculate_result_size = [](Size& result, const Size& child_size) { - result.height += child_size.height; - result.width = std::max(result.width, child_size.width); - }; - create_requirement = [](MeasureLength main, MeasureLength cross) { - return MeasureRequirement{cross, main}; - }; - } - - const auto& children = GetChildren(); - Index children_count = children.size(); - - if (!main_max_length.IsNotSpecify()) { - float remain_main_length = main_max_length.GetLength(); - - for (Index i = 0; i < children_count; i++) { - const auto child = children[i]; - child->Measure(create_requirement(remain_main_length, cross_max_length)); - const auto measure_result = child->GetMeasuredSize(); - remain_main_length -= get_main_length(measure_result); - } - - if (remain_main_length > 0) { - std::vector<Index> expand_children; - float total_expand_factor = 0; - - for (Index i = 0; i < children_count; i++) { - const auto factor = GetChildLayoutData(i)->expand_factor; - if (factor > 0) { - expand_children.push_back(i); - total_expand_factor += factor; - } - } - - for (const Index i : expand_children) { - const float distributed_grow_length = - remain_main_length * - (GetChildLayoutData(i)->expand_factor / total_expand_factor); - const auto child = children[i]; - const float new_main_length = - get_main_length(child->GetMeasuredSize()) + distributed_grow_length; - child->Measure(create_requirement(new_main_length, cross_max_length)); - } - } else if (remain_main_length < 0) { - std::vector<Index> shrink_children; - float total_shrink_factor = 0; - - for (Index i = 0; i < children_count; i++) { - const auto factor = GetChildLayoutData(i)->shrink_factor; - if (factor > 0) { - shrink_children.push_back(i); - total_shrink_factor += factor; - } - } - - for (const Index i : shrink_children) { - const float distributed_shrink_length = // negative - remain_main_length * - (GetChildLayoutData(i)->shrink_factor / total_shrink_factor); - const auto child = children[i]; - float new_main_length = get_main_length(child->GetMeasuredSize()) + - distributed_shrink_length; - new_main_length = new_main_length > 0 ? new_main_length : 0; - child->Measure(create_requirement(new_main_length, cross_max_length)); - } - } - } else { - for (Index i = 0; i < children_count; i++) { - const auto child = children[i]; - child->Measure(requirement); - } - } - - Size result; - for (auto child : children) { - calculate_result_size(result, child->GetMeasuredSize()); - } - - return result; + const MeasureRequirement& requirement, const MeasureSize& preferred_size) { + // TODO: Rewrite this. + CRU_UNUSED(requirement); + CRU_UNUSED(preferred_size); + throw std::runtime_error("Not implemented."); + + // const bool horizontal = (direction_ == FlexDirection::Horizontal || + // direction_ == FlexDirection::HorizontalReverse); + + // const auto main_max_length = + // horizontal ? requirement.max_width : requirement.max_height; + // const auto cross_max_length = + // horizontal ? requirement.max_height : requirement.max_width; + + // std::function<float(const Size&)> get_main_length; + // std::function<float(const Size&)> get_cross_length; + // std::function<void(Size&, const Size&)> calculate_result_size; + // std::function<MeasureRequirement(MeasureLength main, MeasureLength cross)> + // create_requirement; + + // if (horizontal) { + // get_main_length = [](const Size& size) { return size.width; }; + // get_cross_length = [](const Size& size) { return size.height; }; + // calculate_result_size = [](Size& result, const Size& child_size) { + // result.width += child_size.width; + // result.height = std::max(result.height, child_size.height); + // }; + // create_requirement = [](MeasureLength main, MeasureLength cross) { + // return MeasureRequirement{main, cross}; + // }; + // } else { + // get_main_length = [](const Size& size) { return size.height; }; + // get_cross_length = [](const Size& size) { return size.width; }; + // calculate_result_size = [](Size& result, const Size& child_size) { + // result.height += child_size.height; + // result.width = std::max(result.width, child_size.width); + // }; + // create_requirement = [](MeasureLength main, MeasureLength cross) { + // return MeasureRequirement{cross, main}; + // }; + // } + + // const auto& children = GetChildren(); + // Index children_count = children.size(); + + // if (!main_max_length.IsNotSpecify()) { + // float remain_main_length = main_max_length.GetLengthOrMax(); + + // for (Index i = 0; i < children_count; i++) { + // const auto child = children[i]; + // child->Measure(create_requirement(remain_main_length, + // cross_max_length)); const auto measure_result = + // child->GetMeasuredSize(); remain_main_length -= + // get_main_length(measure_result); + // } + + // if (remain_main_length > 0) { + // std::vector<Index> expand_children; + // float total_expand_factor = 0; + + // for (Index i = 0; i < children_count; i++) { + // const auto factor = GetChildLayoutData(i)->expand_factor; + // if (factor > 0) { + // expand_children.push_back(i); + // total_expand_factor += factor; + // } + // } + + // for (const Index i : expand_children) { + // const float distributed_grow_length = + // remain_main_length * + // (GetChildLayoutData(i)->expand_factor / total_expand_factor); + // const auto child = children[i]; + // const float new_main_length = + // get_main_length(child->GetMeasuredSize()) + + // distributed_grow_length; + // child->Measure(create_requirement(new_main_length, + // cross_max_length)); + // } + // } else if (remain_main_length < 0) { + // std::vector<Index> shrink_children; + // float total_shrink_factor = 0; + + // for (Index i = 0; i < children_count; i++) { + // const auto factor = GetChildLayoutData(i)->shrink_factor; + // if (factor > 0) { + // shrink_children.push_back(i); + // total_shrink_factor += factor; + // } + // } + + // for (const Index i : shrink_children) { + // const float distributed_shrink_length = // negative + // remain_main_length * + // (GetChildLayoutData(i)->shrink_factor / total_shrink_factor); + // const auto child = children[i]; + // float new_main_length = get_main_length(child->GetMeasuredSize()) + + // distributed_shrink_length; + // new_main_length = new_main_length > 0 ? new_main_length : 0; + // child->Measure(create_requirement(new_main_length, + // cross_max_length)); + // } + // } + // } else { + // for (Index i = 0; i < children_count; i++) { + // const auto child = children[i]; + // child->Measure(requirement); + // } + // } + + // Size result; + // for (auto child : children) { + // calculate_result_size(result, child->GetMeasuredSize()); + // } + + // return result; } void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { - auto calculate_anchor = [](int alignment, float start_point, - float total_length, - float content_length) -> float { - switch (alignment) { - case internal::align_start: - return start_point; - case internal::align_center: - return start_point + (total_length - content_length) / 2.0f; - case internal::align_end: - return start_point + total_length - content_length; - default: - return start_point; - } - }; - - const auto& children = GetChildren(); - if (direction_ == FlexDirection::Horizontal || - direction_ == FlexDirection::HorizontalReverse) { - float actual_content_width = 0; - for (const auto child : children) { - actual_content_width += child->GetMeasuredSize().width; - } - - const float content_anchor_x = - calculate_anchor(static_cast<int>(content_main_align_), 0, - content_rect.width, actual_content_width); - - float anchor_x = 0; - for (int i = 0; i < static_cast<int>(children.size()); i++) { - const auto child = children[i]; - const auto size = child->GetMeasuredSize(); - - float real_anchor_x = anchor_x + content_anchor_x; - if (direction_ == FlexDirection::Horizontal) - real_anchor_x = content_rect.left + real_anchor_x; - else - real_anchor_x = content_rect.GetRight() - real_anchor_x; - child->Layout(Rect{ - real_anchor_x, - calculate_anchor( - static_cast<int>(GetChildLayoutData(i)->cross_alignment.value_or( - this->item_cross_align_)), - content_rect.top, content_rect.height, size.height), - size.width, size.height}); - - anchor_x += size.width; - } - } else { - float actual_content_height = 0; - for (const auto child : children) { - actual_content_height = child->GetMeasuredSize().height; - } - - const float content_anchor_y = - calculate_anchor(static_cast<int>(content_main_align_), 0, - content_rect.height, actual_content_height); - - float anchor_y = 0; - for (int i = 0; i < static_cast<int>(children.size()); i++) { - const auto child = children[i]; - const auto size = child->GetMeasuredSize(); - - float real_anchor_y = anchor_y + content_anchor_y; - if (direction_ == FlexDirection::Vertical) { - real_anchor_y = content_rect.top + real_anchor_y; - } else { - real_anchor_y = content_rect.GetBottom() - real_anchor_y; - } - child->Layout(Rect{ - real_anchor_y, - calculate_anchor( - static_cast<int>(GetChildLayoutData(i)->cross_alignment.value_or( - this->item_cross_align_)), - content_rect.left, content_rect.width, size.width), - size.width, size.height}); - - anchor_y += size.height; - } - } + // TODO: Rewrite this. + CRU_UNUSED(content_rect); + throw std::runtime_error("Not implemented."); + + // auto calculate_anchor = [](int alignment, float start_point, + // float total_length, + // float content_length) -> float { + // switch (alignment) { + // case internal::align_start: + // return start_point; + // case internal::align_center: + // return start_point + (total_length - content_length) / 2.0f; + // case internal::align_end: + // return start_point + total_length - content_length; + // default: + // return start_point; + // } + // }; + + // const auto& children = GetChildren(); + // if (direction_ == FlexDirection::Horizontal || + // direction_ == FlexDirection::HorizontalReverse) { + // float actual_content_width = 0; + // for (const auto child : children) { + // actual_content_width += child->GetMeasuredSize().width; + // } + + // const float content_anchor_x = + // calculate_anchor(static_cast<int>(content_main_align_), 0, + // content_rect.width, actual_content_width); + + // float anchor_x = 0; + // for (int i = 0; i < static_cast<int>(children.size()); i++) { + // const auto child = children[i]; + // const auto size = child->GetMeasuredSize(); + + // float real_anchor_x = anchor_x + content_anchor_x; + // if (direction_ == FlexDirection::Horizontal) + // real_anchor_x = content_rect.left + real_anchor_x; + // else + // real_anchor_x = content_rect.GetRight() - real_anchor_x; + // child->Layout(Rect{ + // real_anchor_x, + // calculate_anchor( + // static_cast<int>(GetChildLayoutData(i)->cross_alignment.value_or( + // this->item_cross_align_)), + // content_rect.top, content_rect.height, size.height), + // size.width, size.height}); + + // anchor_x += size.width; + // } + // } else { + // float actual_content_height = 0; + // for (const auto child : children) { + // actual_content_height = child->GetMeasuredSize().height; + // } + + // const float content_anchor_y = + // calculate_anchor(static_cast<int>(content_main_align_), 0, + // content_rect.height, actual_content_height); + + // float anchor_y = 0; + // for (int i = 0; i < static_cast<int>(children.size()); i++) { + // const auto child = children[i]; + // const auto size = child->GetMeasuredSize(); + + // float real_anchor_y = anchor_y + content_anchor_y; + // if (direction_ == FlexDirection::Vertical) { + // real_anchor_y = content_rect.top + real_anchor_y; + // } else { + // real_anchor_y = content_rect.GetBottom() - real_anchor_y; + // } + // child->Layout(Rect{ + // real_anchor_y, + // calculate_anchor( + // static_cast<int>(GetChildLayoutData(i)->cross_alignment.value_or( + // this->item_cross_align_)), + // content_rect.left, content_rect.width, size.width), + // size.width, size.height}); + + // anchor_y += size.height; + // } + // } } } // namespace cru::ui::render diff --git a/src/ui/render/LayoutUtility.cpp b/src/ui/render/LayoutUtility.cpp deleted file mode 100644 index 03252f1c..00000000 --- a/src/ui/render/LayoutUtility.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "cru/ui/render/LayoutUtility.hpp" - -#include <algorithm> - -namespace cru::ui::render { -Size Min(const Size& left, const Size& right) { - return Size{std::min(left.width, right.width), - std::min(left.height, right.height)}; -} - -Size Max(const Size& left, const Size& right) { - return Size{std::max(left.width, right.width), - std::max(left.height, right.height)}; -} -} // namespace cru::ui::render diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index abd5e1c4..ac38c6b5 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -57,20 +57,23 @@ Point RenderObject::FromRootToContent(const Point& point) const { point.y - (offset.y + rect.top)}; } -void RenderObject::Measure(const MeasureRequirement& requirement) { - measured_size_ = OnMeasureCore(requirement); +void RenderObject::Measure(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) { + MeasureRequirement merged_requirement = + MeasureRequirement::Merge(requirement, custom_measure_requirement_) + .Normalize(); + MeasureSize merged_preferred_size = + preferred_size.OverrideBy(preferred_size_); - Ensures(measured_size_.width >= 0); - Ensures(measured_size_.height >= 0); - Ensures(requirement.Satisfy(measured_size_)); + size_ = OnMeasureCore(merged_requirement, merged_preferred_size); + Ensures(size_.width >= 0); + Ensures(size_.height >= 0); + Ensures(requirement.Satisfy(size_)); } -void RenderObject::Layout(const Rect& rect) { - Expects(rect.width >= 0); - Expects(rect.height >= 0); - offset_ = rect.GetLeftTop(); - size_ = rect.GetSize(); - OnLayoutCore(rect.GetSize()); +void RenderObject::Layout(const Point& offset) { + offset_ = offset; + OnLayoutCore(); } RenderObject* RenderObject::GetSingleChild() const { @@ -104,7 +107,8 @@ void RenderObject::OnRemoveChild(RenderObject* removed_child, Index position) { InvalidatePaint(); } -Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement) { +Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) { const Size space_size{ margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; @@ -113,58 +117,74 @@ Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement) { MeasureRequirement content_requirement = requirement; - if (!requirement.max_width.IsNotSpecify()) { - const auto max_width = requirement.max_width.GetLength(); + if (!requirement.max.width.IsNotSpecified()) { + const auto max_width = requirement.max.width.GetLengthOrMax(); if (coerced_space_size.width > max_width) { log::Warn( - "Measure: horizontal length of padding and margin is bigger than " - "available length."); + "RenderObject: During measure, horizontal length of padding and " + "margin is bigger than required max length."); coerced_space_size.width = max_width; } - content_requirement.max_width = max_width - coerced_space_size.width; + content_requirement.max.width = max_width - coerced_space_size.width; } - if (!requirement.max_height.IsNotSpecify()) { - const auto max_height = requirement.max_height.GetLength(); + if (!requirement.min.width.IsNotSpecified()) { + const auto min_width = requirement.min.width.GetLengthOr0(); + content_requirement.min.width = std::max(0.f, min_width - space_size.width); + } + + if (!requirement.max.height.IsNotSpecified()) { + const auto max_height = requirement.max.height.GetLengthOrMax(); if (coerced_space_size.height > max_height) { log::Warn( - "Measure: horizontal length of padding and margin is bigger than " - "available length."); + "RenderObject: During measure, vertical length of padding and " + "margin is bigger than required max length."); coerced_space_size.height = max_height; } - content_requirement.max_height = max_height - coerced_space_size.height; + content_requirement.max.height = max_height - coerced_space_size.height; } - const auto content_size = OnMeasureContent(content_requirement); + if (!requirement.min.height.IsNotSpecified()) { + const auto min_height = requirement.min.height.GetLengthOr0(); + content_requirement.min.height = + std::max(0.f, min_height - space_size.height); + } + + MeasureSize content_preferred_size = + content_requirement.Coerce(preferred_size.Minus(space_size)); + + const auto content_size = + OnMeasureContent(content_requirement, content_preferred_size); return coerced_space_size + content_size; } -void RenderObject::OnLayoutCore(const Size& size) { +void RenderObject::OnLayoutCore() { + Size total_size = GetSize(); Size space_size{margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; - auto content_size = size - space_size; + auto content_size = total_size - space_size; if (content_size.width < 0) { log::Warn( - "Layout: horizontal length of padding and margin is bigger than " - "available length."); + "RenderObject: During layout, horizontal length of padding and margin " + "is bigger than available length."); content_size.width = 0; } if (content_size.height < 0) { log::Warn( - "Layout: vertical length of padding and margin is bigger than " - "available length."); + "RenderObject: During layout, vertical length of padding and margin " + "is bigger than available length."); content_size.height = 0; } Point lefttop{margin_.left + padding_.left, margin_.top + padding_.top}; - if (lefttop.x > size.width) { - lefttop.x = size.width; + if (lefttop.x > total_size.width) { + lefttop.x = total_size.width; } - if (lefttop.y > size.height) { - lefttop.y = size.height; + if (lefttop.y > total_size.height) { + lefttop.y = total_size.height; } const Rect content_rect{lefttop, content_size}; diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index 98254690..e4cc27f3 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -2,7 +2,6 @@ #include "cru/platform/graph/Painter.hpp" #include "cru/platform/graph/util/Painter.hpp" -#include "cru/ui/render/LayoutUtility.hpp" #include <algorithm> @@ -67,23 +66,32 @@ void ScrollRenderObject::SetScrollOffset(const Point& offset) { InvalidateLayout(); } -Size ScrollRenderObject::OnMeasureContent( - const MeasureRequirement& requirement) { - if (const auto child = GetSingleChild()) { - child->Measure(MeasureRequirement::Infinate()); - const auto preferred_size = child->GetMeasuredSize(); - return Min(preferred_size, requirement.GetMaxSize()); - } else { - return Size{}; - } +Size ScrollRenderObject::OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) { + // TODO: Rewrite this. + CRU_UNUSED(requirement); + CRU_UNUSED(preferred_size); + throw std::runtime_error("Not implemented."); + + // 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) { - 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}); - } + // TODO: Rewrite this. + CRU_UNUSED(content_rect); + throw std::runtime_error("Not implemented."); + + // 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 diff --git a/src/ui/render/StackLayoutRenderObject.cpp b/src/ui/render/StackLayoutRenderObject.cpp index 7dce1a83..b953ae7e 100644 --- a/src/ui/render/StackLayoutRenderObject.cpp +++ b/src/ui/render/StackLayoutRenderObject.cpp @@ -4,47 +4,57 @@ namespace cru::ui::render { Size StackLayoutRenderObject::OnMeasureContent( - const MeasureRequirement& requirement) { - auto size = Size{}; - for (const auto child : GetChildren()) { - child->Measure(requirement); - const auto& measure_result = child->GetMeasuredSize(); - size.width = std::max(size.width, measure_result.width); - size.height = std::max(size.height, measure_result.height); - } - return size; + const MeasureRequirement& requirement, const MeasureSize& preferred_size) { + // TODO: Rewrite this. + CRU_UNUSED(requirement); + CRU_UNUSED(preferred_size); + throw std::runtime_error("Not implemented."); + + // throw std::runtime_error("Not implemented."); + // auto size = Size{}; + // for (const auto child : GetChildren()) { + // child->Measure(requirement); + // const auto& measure_result = child->GetMeasuredSize(); + // size.width = std::max(size.width, measure_result.width); + // size.height = std::max(size.height, measure_result.height); + // } + // return size; } -void StackLayoutRenderObject::OnLayoutContent(const Rect& rect) { - auto calculate_anchor = [](int alignment, float start_point, - float total_length, - float content_length) -> float { - switch (alignment) { - case internal::align_start: - return start_point; - case internal::align_center: - return start_point + (total_length - content_length) / 2.0f; - case internal::align_end: - return start_point + total_length - content_length; - default: - return start_point; - } - }; +void StackLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { + // TODO: Rewrite this. + CRU_UNUSED(content_rect); + throw std::runtime_error("Not implemented."); - const auto count = GetChildCount(); - const auto& children = GetChildren(); + // auto calculate_anchor = [](int alignment, float start_point, + // float total_length, + // float content_length) -> float { + // switch (alignment) { + // case internal::align_start: + // return start_point; + // case internal::align_center: + // return start_point + (total_length - content_length) / 2.0f; + // case internal::align_end: + // return start_point + total_length - content_length; + // default: + // return start_point; + // } + // }; - for (int i = 0; i < count; i++) { - const auto layout_data = GetChildLayoutData(i); - const auto child = children[i]; - const auto& size = child->GetMeasuredSize(); - child->Layout( - Rect{calculate_anchor(static_cast<int>(layout_data->horizontal), - rect.left, rect.width, size.width), - calculate_anchor(static_cast<int>(layout_data->vertical), rect.top, - rect.height, size.height), - size.width, size.height}); - } -} + // const auto count = GetChildCount(); + // const auto& children = GetChildren(); + // for (int i = 0; i < count; i++) { + // const auto layout_data = GetChildLayoutData(i); + // const auto child = children[i]; + // const auto& size = child->GetMeasuredSize(); + // child->Layout( + // Rect{calculate_anchor(static_cast<int>(layout_data->horizontal), + // rect.left, rect.width, size.width), + // calculate_anchor(static_cast<int>(layout_data->vertical), + // rect.top, + // rect.height, size.height), + // size.width, size.height}); + // } +} } // namespace cru::ui::render diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp index ececfbc2..9cae65d6 100644 --- a/src/ui/render/TextRenderObject.cpp +++ b/src/ui/render/TextRenderObject.cpp @@ -1,10 +1,10 @@ #include "cru/ui/render/TextRenderObject.hpp" #include "../Helper.hpp" +#include "cru/common/Logger.hpp" #include "cru/platform/graph/Factory.hpp" #include "cru/platform/graph/TextLayout.hpp" #include "cru/platform/graph/util/Painter.hpp" -#include "cru/ui/render/LayoutUtility.hpp" #include <algorithm> #include <limits> @@ -160,10 +160,44 @@ RenderObject* TextRenderObject::HitTest(const Point& point) { return padding_rect.IsPointInside(point) ? this : nullptr; } -Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement) { - text_layout_->SetMaxWidth(requirement.max_height.GetLength()); - text_layout_->SetMaxHeight(requirement.max_height.GetLength()); - return Min(text_layout_->GetTextBounds().GetSize(), requirement.GetMaxSize()); +Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) { + float measure_width; + if (preferred_size.width.IsSpecified()) + measure_width = preferred_size.width.GetLengthOrUndefined(); + else + measure_width = preferred_size.width.GetLengthOrMax(); + + text_layout_->SetMaxWidth(measure_width); + text_layout_->SetMaxHeight(std::numeric_limits<float>::max()); + + const auto text_size = text_layout_->GetTextBounds().GetSize(); + auto result = text_size; + + if (requirement.max.width.IsSpecified() && + text_size.width > requirement.max.width.GetLengthOrUndefined()) { + log::Warn( + "TextRenderObject: Text actual width exceeds the required max width."); + result.width = requirement.max.width.GetLengthOrUndefined(); + } else { + result.width = std::max(result.width, preferred_size.width.GetLengthOr0()); + result.width = std::max(result.width, requirement.min.width.GetLengthOr0()); + } + + if (requirement.max.height.IsSpecified() && + text_size.height > requirement.max.height.GetLengthOrUndefined()) { + log::Warn( + "TextRenderObject: Text actual height exceeds the required max " + "height."); + result.width = requirement.max.width.GetLengthOrUndefined(); + } else { + result.height = + std::max(result.height, preferred_size.height.GetLengthOr0()); + result.width = + std::max(result.height, requirement.min.height.GetLengthOr0()); + } + + return result; } void TextRenderObject::OnLayoutContent(const Rect& content_rect) { diff --git a/src/ui/render/WindowRenderObject.cpp b/src/ui/render/WindowRenderObject.cpp index 28afe01d..a2c7ae4d 100644 --- a/src/ui/render/WindowRenderObject.cpp +++ b/src/ui/render/WindowRenderObject.cpp @@ -34,17 +34,17 @@ RenderObject* WindowRenderObject::HitTest(const Point& point) { return Rect{Point{}, GetSize()}.IsPointInside(point) ? this : nullptr; } -Size WindowRenderObject::OnMeasureContent( - const MeasureRequirement& requirement) { +Size WindowRenderObject::OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) { if (const auto child = GetChild()) { - child->Measure(requirement); - return child->GetMeasuredSize(); + child->Measure(requirement, preferred_size); + return child->GetSize(); } else { return Size{}; } } void WindowRenderObject::OnLayoutContent(const Rect& content_rect) { - if (const auto child = GetChild()) child->Layout(content_rect); + if (const auto child = GetChild()) child->Layout(content_rect.GetLeftTop()); } } // namespace cru::ui::render |