aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json6
-rw-r--r--include/cru/ui/render/BorderRenderObject.hpp8
-rw-r--r--include/cru/ui/render/CanvasRenderObject.hpp13
-rw-r--r--include/cru/ui/render/FlexLayoutRenderObject.hpp2
-rw-r--r--include/cru/ui/render/LayoutUtility.hpp7
-rw-r--r--include/cru/ui/render/MeasureRequirement.hpp209
-rw-r--r--include/cru/ui/render/RenderObject.hpp98
-rw-r--r--include/cru/ui/render/ScrollRenderObject.hpp2
-rw-r--r--include/cru/ui/render/StackLayoutRenderObject.hpp2
-rw-r--r--include/cru/ui/render/TextRenderObject.hpp17
-rw-r--r--include/cru/ui/render/WindowRenderObject.hpp3
-rw-r--r--src/ui/CMakeLists.txt2
-rw-r--r--src/ui/UiHost.cpp7
-rw-r--r--src/ui/render/BorderRenderObject.cpp68
-rw-r--r--src/ui/render/CanvasRenderObject.cpp9
-rw-r--r--src/ui/render/FlexLayoutRenderObject.cpp385
-rw-r--r--src/ui/render/LayoutUtility.cpp15
-rw-r--r--src/ui/render/RenderObject.cpp86
-rw-r--r--src/ui/render/ScrollRenderObject.cpp40
-rw-r--r--src/ui/render/StackLayoutRenderObject.cpp86
-rw-r--r--src/ui/render/TextRenderObject.cpp44
-rw-r--r--src/ui/render/WindowRenderObject.cpp10
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