aboutsummaryrefslogtreecommitdiff
path: root/include/cru
diff options
context:
space:
mode:
Diffstat (limited to 'include/cru')
-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
10 files changed, 286 insertions, 75 deletions
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: