aboutsummaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/CMakeLists.txt1
-rw-r--r--src/ui/UiHost.cpp11
-rw-r--r--src/ui/render/BorderRenderObject.cpp117
-rw-r--r--src/ui/render/CanvasRenderObject.cpp5
-rw-r--r--src/ui/render/FlexLayoutRenderObject.cpp180
-rw-r--r--src/ui/render/RenderObject.cpp94
-rw-r--r--src/ui/render/ScrollRenderObject.cpp77
-rw-r--r--src/ui/render/StackLayoutRenderObject.cpp15
-rw-r--r--src/ui/render/TextRenderObject.cpp10
-rw-r--r--src/ui/render/WindowRenderObject.cpp11
10 files changed, 323 insertions, 198 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index f37982e9..68efa903 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -54,6 +54,7 @@ target_sources(cru_ui PUBLIC
${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
${CRU_UI_INCLUDE_DIR}/render/StackLayoutRenderObject.hpp
diff --git a/src/ui/UiHost.cpp b/src/ui/UiHost.cpp
index 069e68de..7047d43f 100644
--- a/src/ui/UiHost.cpp
+++ b/src/ui/UiHost.cpp
@@ -1,12 +1,12 @@
#include "cru/ui/UiHost.hpp"
+#include "RoutedEventDispatch.hpp"
#include "cru/common/Logger.hpp"
#include "cru/platform/graph/Painter.hpp"
#include "cru/platform/native/UiApplication.hpp"
#include "cru/platform/native/Window.hpp"
-#include "cru/ui/render/WindowRenderObject.hpp"
#include "cru/ui/Window.hpp"
-#include "RoutedEventDispatch.hpp"
+#include "cru/ui/render/WindowRenderObject.hpp"
namespace cru::ui {
using platform::native::INativeWindow;
@@ -28,7 +28,6 @@ CRU_DEFINE_EVENT_NAME(MouseDown)
CRU_DEFINE_EVENT_NAME(MouseUp)
CRU_DEFINE_EVENT_NAME(KeyDown)
CRU_DEFINE_EVENT_NAME(KeyUp)
-CRU_DEFINE_EVENT_NAME(Char)
#undef CRU_DEFINE_EVENT_NAME
} // namespace event_names
@@ -93,10 +92,10 @@ inline void BindNativeEvent(
} // namespace
UiHost::UiHost(Window* window)
- : mouse_hover_control_(nullptr),
+ : window_control_(window),
+ mouse_hover_control_(nullptr),
focus_control_(window),
- mouse_captured_control_(nullptr),
- window_control_(window) {
+ mouse_captured_control_(nullptr) {
native_window_resolver_ =
IUiApplication::GetInstance()->CreateWindow(nullptr);
diff --git a/src/ui/render/BorderRenderObject.cpp b/src/ui/render/BorderRenderObject.cpp
index a656cb99..fbc3c65f 100644
--- a/src/ui/render/BorderRenderObject.cpp
+++ b/src/ui/render/BorderRenderObject.cpp
@@ -73,84 +73,97 @@ RenderObject* BorderRenderObject::HitTest(const Point& point) {
}
}
-void BorderRenderObject::OnMeasureCore(const Size& available_size) {
+Size BorderRenderObject::OnMeasureCore(const MeasureRequirement& requirement) {
+ if (!is_border_enabled_) {
+ return RenderObject::OnMeasureCore(requirement);
+ }
+
const auto margin = GetMargin();
const auto padding = GetPadding();
- Size margin_border_padding_size{
- margin.GetHorizontalTotal() + padding.GetHorizontalTotal(),
- margin.GetVerticalTotal() + padding.GetVerticalTotal()};
-
- if (is_border_enabled_) {
- margin_border_padding_size.width += border_thickness_.GetHorizontalTotal();
- margin_border_padding_size.height += border_thickness_.GetVerticalTotal();
+ const Size space_size{margin.GetHorizontalTotal() +
+ padding.GetHorizontalTotal() +
+ border_thickness_.GetHorizontalTotal(),
+ margin.GetVerticalTotal() + padding.GetVerticalTotal() +
+ border_thickness_.GetVerticalTotal()};
+
+ auto coerced_space_size = space_size;
+
+ MeasureRequirement content_requirement = requirement;
+
+ if (!requirement.max_width.IsInfinate()) {
+ const auto max_width = requirement.max_width.GetLength();
+ if (coerced_space_size.width > max_width) {
+ log::Warn(
+ "Measure: horizontal length of padding and margin is bigger than "
+ "available length.");
+ coerced_space_size.width = max_width;
+ }
+ content_requirement.max_width = max_width - coerced_space_size.width;
}
- auto coerced_margin_border_padding_size = margin_border_padding_size;
- if (coerced_margin_border_padding_size.width > available_size.width) {
- log::Warn(
- "Measure: horizontal length of padding, border and margin is bigger "
- "than available length.");
- coerced_margin_border_padding_size.width = available_size.width;
- }
- if (coerced_margin_border_padding_size.height > available_size.height) {
- log::Warn(
- "Measure: vertical length of padding, border and margin is bigger "
- "than available length.");
- coerced_margin_border_padding_size.height = available_size.height;
+ if (!requirement.max_height.IsInfinate()) {
+ const auto max_height = requirement.max_height.GetLength();
+ if (coerced_space_size.height > max_height) {
+ log::Warn(
+ "Measure: horizontal length of padding and margin is bigger than "
+ "available length.");
+ coerced_space_size.height = max_height;
+ }
+ content_requirement.max_height = max_height - coerced_space_size.height;
}
- const auto coerced_content_available_size =
- available_size - coerced_margin_border_padding_size;
+ const auto content_size = OnMeasureContent(content_requirement);
- const auto actual_content_size =
- OnMeasureContent(coerced_content_available_size);
+ return coerced_space_size + content_size;
+} // namespace cru::ui::render
- SetPreferredSize(coerced_margin_border_padding_size + actual_content_size);
-}
+void BorderRenderObject::OnLayoutCore(const Size& size) {
+ if (!is_border_enabled_) {
+ return RenderObject::OnLayoutCore(size);
+ }
-void BorderRenderObject::OnLayoutCore(const Rect& rect) {
const auto margin = GetMargin();
const auto padding = GetPadding();
- Size margin_border_padding_size{
- margin.GetHorizontalTotal() + padding.GetHorizontalTotal(),
- margin.GetVerticalTotal() + padding.GetVerticalTotal()};
-
- if (is_border_enabled_) {
- margin_border_padding_size.width += border_thickness_.GetHorizontalTotal();
- margin_border_padding_size.height += border_thickness_.GetVerticalTotal();
- }
+ Size space_size{margin.GetHorizontalTotal() + padding.GetHorizontalTotal() +
+ border_thickness_.GetHorizontalTotal(),
+ margin.GetVerticalTotal() + padding.GetVerticalTotal() +
+ border_thickness_.GetVerticalTotal()};
- const auto content_available_size =
- rect.GetSize() - margin_border_padding_size;
- auto coerced_content_available_size = content_available_size;
+ auto content_size = size - space_size;
- if (coerced_content_available_size.width < 0) {
+ if (content_size.width < 0) {
log::Warn(
"Layout: horizontal length of padding, border and margin is bigger "
"than available length.");
- coerced_content_available_size.width = 0;
+ content_size.width = 0;
}
- if (coerced_content_available_size.height < 0) {
+ if (content_size.height < 0) {
log::Warn(
"Layout: vertical length of padding, border and margin is bigger "
"than available length.");
- coerced_content_available_size.height = 0;
+ content_size.height = 0;
+ }
+
+ Point lefttop{margin.left + padding.left + border_thickness_.left,
+ margin.top + padding.top + border_thickness_.top};
+ if (lefttop.x > size.width) {
+ lefttop.x = size.width;
}
+ if (lefttop.y > size.height) {
+ lefttop.y = size.height;
+ }
+
+ const Rect content_rect{lefttop, content_size};
- OnLayoutContent(
- Rect{margin.left + (is_border_enabled_ ? border_thickness_.left : 0) +
- padding.left,
- margin.top + (is_border_enabled_ ? border_thickness_.top : 0) +
- padding.top,
- coerced_content_available_size.width,
- coerced_content_available_size.height});
+ OnLayoutContent(content_rect);
}
-Size BorderRenderObject::OnMeasureContent(const Size& available_size) {
+Size BorderRenderObject::OnMeasureContent(
+ const MeasureRequirement& requirement) {
const auto child = GetChild();
if (child) {
- child->Measure(available_size);
- return child->GetPreferredSize();
+ child->Measure(requirement);
+ return child->GetMeasuredSize();
} else {
return Size{};
}
diff --git a/src/ui/render/CanvasRenderObject.cpp b/src/ui/render/CanvasRenderObject.cpp
index 16ac9239..72eb3570 100644
--- a/src/ui/render/CanvasRenderObject.cpp
+++ b/src/ui/render/CanvasRenderObject.cpp
@@ -18,8 +18,9 @@ RenderObject* CanvasRenderObject::HitTest(const Point& point) {
return padding_rect.IsPointInside(point) ? this : nullptr;
}
-Size CanvasRenderObject::OnMeasureContent(const Size& available_size) {
- return Min(available_size, GetDesiredSize());
+Size CanvasRenderObject::OnMeasureContent(
+ const MeasureRequirement& requirement) {
+ return Min(requirement.GetMaxSize(), GetDesiredSize());
}
void CanvasRenderObject::OnLayoutContent(const Rect& content_rect) {
diff --git a/src/ui/render/FlexLayoutRenderObject.cpp b/src/ui/render/FlexLayoutRenderObject.cpp
index b609fd97..079bc5a9 100644
--- a/src/ui/render/FlexLayoutRenderObject.cpp
+++ b/src/ui/render/FlexLayoutRenderObject.cpp
@@ -6,111 +6,115 @@
#include <functional>
namespace cru::ui::render {
-Size FlexLayoutRenderObject::OnMeasureContent(const Size& available_size) {
- std::vector<int> has_basis_children;
- std::vector<int> no_basis_children;
- std::vector<int> grow_children;
- std::vector<int> shrink_chilren;
- const auto child_count = GetChildCount();
- for (int i = 0; i < child_count; i++) {
- const auto& layout_data = *GetChildLayoutData(i);
- if (layout_data.flex_basis.has_value())
- has_basis_children.push_back(i);
- else
- no_basis_children.push_back(i);
- if (layout_data.flex_grow > 0) grow_children.push_back(i);
- if (layout_data.flex_shrink > 0) shrink_chilren.push_back(i);
- }
+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<Size(float main, float cross)> create_size;
+ std::function<void(Size&, const Size&)> calculate_result_size;
+ std::function<MeasureRequirement(MeasureLength main, MeasureLength cross)>
+ create_requirement;
- if (direction_ == FlexDirection::Horizontal ||
- direction_ == FlexDirection::HorizontalReverse) {
+ if (horizontal) {
get_main_length = [](const Size& size) { return size.width; };
get_cross_length = [](const Size& size) { return size.height; };
- create_size = [](float main, float cross) { return Size(main, cross); };
+ 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; };
- create_size = [](float main, float cross) { return Size(cross, main); };
+ 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();
- float remain_main_length = get_main_length(available_size);
- float max_cross_length = 0;
-
- for (const int i : has_basis_children) {
- const auto child = children[i];
- const float basis = GetChildLayoutData(i)->flex_basis.value();
- child->Measure(create_size(basis, get_cross_length(available_size)));
- remain_main_length -= basis;
- const float child_preferred_cross_length =
- get_cross_length(child->GetPreferredSize());
- child->SetPreferredSize(create_size(basis, child_preferred_cross_length));
- max_cross_length = std::max(max_cross_length, child_preferred_cross_length);
- }
-
- for (const int i : no_basis_children) {
- const auto child = children[i];
- child->Measure(create_size(remain_main_length > 0 ? remain_main_length : 0,
- get_cross_length(available_size)));
- remain_main_length -= get_main_length(child->GetPreferredSize());
- max_cross_length =
- std::max(max_cross_length, get_cross_length(child->GetPreferredSize()));
- }
-
- if (remain_main_length > 0) {
- float total_grow = 0;
- for (const int i : grow_children)
- total_grow += GetChildLayoutData(i)->flex_grow;
+ if (!main_max_length.IsInfinate()) {
+ float remain_main_length = main_max_length.GetLength();
- for (const int i : grow_children) {
- const float distributed_grow_length =
- remain_main_length * (GetChildLayoutData(i)->flex_grow / total_grow);
+ for (Index i = 0; i < children_count; i++) {
const auto child = children[i];
- const float new_main_length =
- get_main_length(child->GetPreferredSize()) + distributed_grow_length;
- child->Measure(
- create_size(new_main_length, get_cross_length(available_size)));
- const float new_child_preferred_cross_length =
- get_cross_length(child->GetPreferredSize());
- child->SetPreferredSize(
- create_size(new_main_length, new_child_preferred_cross_length));
- max_cross_length =
- std::max(max_cross_length, new_child_preferred_cross_length);
+ 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) {
- float total_shrink = 0;
- for (const int i : shrink_chilren)
- total_shrink += GetChildLayoutData(i)->flex_shrink;
+ 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 int i : shrink_chilren) {
- const float distributed_shrink_length = // negative
- remain_main_length *
- (GetChildLayoutData(i)->flex_shrink / total_shrink);
+ for (const int 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 int 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];
- float new_main_length = get_main_length(child->GetPreferredSize()) +
- distributed_shrink_length;
- new_main_length = new_main_length > 0 ? new_main_length : 0;
- child->Measure(
- create_size(new_main_length, get_cross_length(available_size)));
- const float new_child_preferred_cross_length =
- get_cross_length(child->GetPreferredSize());
- child->SetPreferredSize(
- create_size(new_main_length, new_child_preferred_cross_length));
- max_cross_length =
- std::max(max_cross_length, new_child_preferred_cross_length);
+ child->Measure(requirement);
}
}
- return create_size(get_main_length(available_size) -
- (remain_main_length > 0 ? remain_main_length : 0),
- max_cross_length);
+ Size result;
+ for (auto child : children) {
+ calculate_result_size(result, child->GetMeasuredSize());
+ }
+
+ return result;
}
void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) {
@@ -125,7 +129,7 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) {
case internal::align_end:
return start_point + total_length - content_length;
default:
- return 0;
+ return start_point;
}
};
@@ -134,7 +138,7 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) {
direction_ == FlexDirection::HorizontalReverse) {
float actual_content_width = 0;
for (const auto child : children) {
- actual_content_width += child->GetPreferredSize().width;
+ actual_content_width += child->GetMeasuredSize().width;
}
const float content_anchor_x =
@@ -144,7 +148,7 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) {
float anchor_x = 0;
for (int i = 0; i < static_cast<int>(children.size()); i++) {
const auto child = children[i];
- const auto size = child->GetPreferredSize();
+ const auto size = child->GetMeasuredSize();
float real_anchor_x = anchor_x + content_anchor_x;
if (direction_ == FlexDirection::Horizontal)
@@ -164,7 +168,7 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) {
} else {
float actual_content_height = 0;
for (const auto child : children) {
- actual_content_height = child->GetPreferredSize().height;
+ actual_content_height = child->GetMeasuredSize().height;
}
const float content_anchor_y =
@@ -174,7 +178,7 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) {
float anchor_y = 0;
for (int i = 0; i < static_cast<int>(children.size()); i++) {
const auto child = children[i];
- const auto size = child->GetPreferredSize();
+ const auto size = child->GetMeasuredSize();
float real_anchor_y = anchor_y + content_anchor_y;
if (direction_ == FlexDirection::Vertical) {
diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp
index 83e306a8..cdc3032f 100644
--- a/src/ui/render/RenderObject.cpp
+++ b/src/ui/render/RenderObject.cpp
@@ -57,14 +57,20 @@ Point RenderObject::FromRootToContent(const Point& point) const {
point.y - (offset.y + rect.top)};
}
-void RenderObject::Measure(const Size& available_size) {
- OnMeasureCore(available_size);
+void RenderObject::Measure(const MeasureRequirement& requirement) {
+ measured_size_ = OnMeasureCore(requirement);
+
+ Ensures(measured_size_.width >= 0);
+ Ensures(measured_size_.height >= 0);
+ Ensures(requirement.Satisfy(measured_size_));
}
void RenderObject::Layout(const Rect& rect) {
- SetOffset(rect.GetLeftTop());
- SetSize(rect.GetSize());
- OnLayoutCore(Rect{Point{}, rect.GetSize()});
+ Expects(rect.width >= 0);
+ Expects(rect.height >= 0);
+ offset_ = rect.GetLeftTop();
+ size_ = rect.GetSize();
+ OnLayoutCore(rect.GetSize());
}
void RenderObject::OnParentChanged(RenderObject* old_parent,
@@ -89,56 +95,72 @@ void RenderObject::OnRemoveChild(RenderObject* removed_child, Index position) {
InvalidatePaint();
}
-void RenderObject::OnMeasureCore(const Size& available_size) {
- Size margin_padding_size{
+Size RenderObject::OnMeasureCore(const MeasureRequirement& requirement) {
+ const Size space_size{
margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(),
margin_.GetVerticalTotal() + padding_.GetVerticalTotal()};
- auto coerced_margin_padding_size = margin_padding_size;
- if (coerced_margin_padding_size.width > available_size.width) {
- log::Warn(
- "Measure: horizontal length of padding and margin is bigger than "
- "available length.");
- coerced_margin_padding_size.width = available_size.width;
+ auto coerced_space_size = space_size;
+
+ MeasureRequirement content_requirement = requirement;
+
+ if (!requirement.max_width.IsInfinate()) {
+ const auto max_width = requirement.max_width.GetLength();
+ if (coerced_space_size.width > max_width) {
+ log::Warn(
+ "Measure: horizontal length of padding and margin is bigger than "
+ "available length.");
+ coerced_space_size.width = max_width;
+ }
+ content_requirement.max_width = max_width - coerced_space_size.width;
}
- if (coerced_margin_padding_size.height > available_size.height) {
- log::Warn(
- "Measure: vertical length of padding and margin is bigger than "
- "available length.");
- coerced_margin_padding_size.height = available_size.height;
+
+ if (!requirement.max_height.IsInfinate()) {
+ const auto max_height = requirement.max_height.GetLength();
+ if (coerced_space_size.height > max_height) {
+ log::Warn(
+ "Measure: horizontal length of padding and margin is bigger than "
+ "available length.");
+ coerced_space_size.height = max_height;
+ }
+ content_requirement.max_height = max_height - coerced_space_size.height;
}
- const auto coerced_content_available_size =
- available_size - coerced_margin_padding_size;
- const auto actual_content_size =
- OnMeasureContent(coerced_content_available_size);
+ const auto content_size = OnMeasureContent(content_requirement);
- SetPreferredSize(coerced_margin_padding_size + actual_content_size);
+ return coerced_space_size + content_size;
}
-void RenderObject::OnLayoutCore(const Rect& rect) {
- Size margin_padding_size{
- margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(),
- margin_.GetVerticalTotal() + padding_.GetVerticalTotal()};
- const auto content_available_size = rect.GetSize() - margin_padding_size;
- auto coerced_content_available_size = content_available_size;
+void RenderObject::OnLayoutCore(const Size& size) {
+ Size space_size{margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(),
+ margin_.GetVerticalTotal() + padding_.GetVerticalTotal()};
- if (coerced_content_available_size.width < 0) {
+ auto content_size = size - space_size;
+
+ if (content_size.width < 0) {
log::Warn(
"Layout: horizontal length of padding and margin is bigger than "
"available length.");
- coerced_content_available_size.width = 0;
+ content_size.width = 0;
}
- if (coerced_content_available_size.height < 0) {
+ if (content_size.height < 0) {
log::Warn(
"Layout: vertical length of padding and margin is bigger than "
"available length.");
- coerced_content_available_size.height = 0;
+ content_size.height = 0;
}
- OnLayoutContent(Rect{margin_.left + padding_.left, margin_.top + padding_.top,
- coerced_content_available_size.width,
- coerced_content_available_size.height});
+ Point lefttop{margin_.left + padding_.left, margin_.top + padding_.top};
+ if (lefttop.x > size.width) {
+ lefttop.x = size.width;
+ }
+ if (lefttop.y > size.height) {
+ lefttop.y = size.height;
+ }
+
+ const Rect content_rect{lefttop, content_size};
+
+ OnLayoutContent(content_rect);
}
void RenderObject::OnAfterLayout() {}
diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp
index 33e9e407..f1df9d8b 100644
--- a/src/ui/render/ScrollRenderObject.cpp
+++ b/src/ui/render/ScrollRenderObject.cpp
@@ -1 +1,78 @@
#include "cru/ui/render/ScrollRenderObject.hpp"
+
+#include "cru/platform/graph/Painter.hpp"
+#include "cru/platform/graph/util/Painter.hpp"
+
+#include <algorithm>
+
+namespace cru::ui::render {
+namespace {
+// This method assumes margin offset is already considered.
+// It promises that it won't return negetive value.
+Point CalculateChildOffsetOfScroll(const Point& scroll_offset,
+ const Size& content_area_size,
+ const Thickness& padding,
+ const Size& child_size) {
+ Point result(scroll_offset);
+
+ Point max_scroll(child_size - content_area_size);
+ max_scroll.x = std::max(max_scroll.x, 0.f);
+ max_scroll.y = std::max(max_scroll.y, 0.f);
+
+ auto coerce = [](float& n, float max) {
+ if (n < 0)
+ n = 0;
+ else if (n > max)
+ n = max;
+ };
+
+ coerce(result.x, scroll_offset.x);
+ coerce(result.y, scroll_offset.y);
+
+ result.x += padding.left;
+ result.y += padding.top;
+
+ return result;
+}
+} // namespace
+
+void ScrollRenderObject::Draw(platform::graph::IPainter* painter) {
+ for (auto child : this->GetChildren()) {
+ painter->PushLayer(this->GetPaddingRect());
+ const auto true_offset =
+ CalculateChildOffsetOfScroll(scroll_offset_, GetContentRect().GetSize(),
+ GetPadding(), child->GetSize());
+ platform::graph::util::WithTransform(
+ painter, Matrix::Translation(true_offset.x, true_offset.y),
+ [child](platform::graph::IPainter* p) { child->Draw(p); });
+ painter->PopLayer();
+ }
+}
+
+RenderObject* ScrollRenderObject::HitTest(const Point& point) {
+ const auto rect = GetPaddingRect();
+ return rect.IsPointInside(point) ? this : nullptr;
+}
+
+void ScrollRenderObject::SetScrollOffset(const Point& offset) {
+ scroll_offset_ = offset;
+ InvalidatePaint();
+}
+
+Size ScrollRenderObject::OnMeasureContent(
+ const MeasureRequirement& requirement) {
+ CRU_UNUSED(requirement);
+ // TODO!
+ // const auto& children = GetChildren();
+ // if (children.empty()) return Size{};
+ // const auto child = children.front();
+ // child->Measure(MeasureRequirement::Infinate());
+ // const auto preferred_size = child->GetMeasuredSize();
+ return Size{};
+} // namespace cru::ui::render
+
+void ScrollRenderObject::OnLayoutContent(const Rect& content_rect) {
+ CRU_UNUSED(content_rect);
+ // TODO!
+}
+} // namespace cru::ui::render
diff --git a/src/ui/render/StackLayoutRenderObject.cpp b/src/ui/render/StackLayoutRenderObject.cpp
index d08e136d..7dce1a83 100644
--- a/src/ui/render/StackLayoutRenderObject.cpp
+++ b/src/ui/render/StackLayoutRenderObject.cpp
@@ -3,13 +3,14 @@
#include <algorithm>
namespace cru::ui::render {
-Size StackLayoutRenderObject::OnMeasureContent(const Size& available_size) {
+Size StackLayoutRenderObject::OnMeasureContent(
+ const MeasureRequirement& requirement) {
auto size = Size{};
for (const auto child : GetChildren()) {
- child->Measure(available_size);
- const auto& preferred_size = child->GetPreferredSize();
- size.width = std::max(size.width, preferred_size.width);
- size.height = std::max(size.height, preferred_size.height);
+ 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;
}
@@ -26,7 +27,7 @@ void StackLayoutRenderObject::OnLayoutContent(const Rect& rect) {
case internal::align_end:
return start_point + total_length - content_length;
default:
- return 0;
+ return start_point;
}
};
@@ -36,7 +37,7 @@ void StackLayoutRenderObject::OnLayoutContent(const Rect& rect) {
for (int i = 0; i < count; i++) {
const auto layout_data = GetChildLayoutData(i);
const auto child = children[i];
- const auto& size = child->GetPreferredSize();
+ const auto& size = child->GetMeasuredSize();
child->Layout(
Rect{calculate_anchor(static_cast<int>(layout_data->horizontal),
rect.left, rect.width, size.width),
diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp
index 05acd086..ececfbc2 100644
--- a/src/ui/render/TextRenderObject.cpp
+++ b/src/ui/render/TextRenderObject.cpp
@@ -4,8 +4,10 @@
#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>
namespace cru::ui::render {
TextRenderObject::TextRenderObject(
@@ -158,10 +160,10 @@ RenderObject* TextRenderObject::HitTest(const Point& point) {
return padding_rect.IsPointInside(point) ? this : nullptr;
}
-Size TextRenderObject::OnMeasureContent(const Size& available_size) {
- text_layout_->SetMaxWidth(available_size.width);
- text_layout_->SetMaxHeight(available_size.height);
- return text_layout_->GetTextBounds().GetSize();
+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());
}
void TextRenderObject::OnLayoutContent(const Rect& content_rect) {
diff --git a/src/ui/render/WindowRenderObject.cpp b/src/ui/render/WindowRenderObject.cpp
index cd1f806f..28afe01d 100644
--- a/src/ui/render/WindowRenderObject.cpp
+++ b/src/ui/render/WindowRenderObject.cpp
@@ -34,9 +34,14 @@ RenderObject* WindowRenderObject::HitTest(const Point& point) {
return Rect{Point{}, GetSize()}.IsPointInside(point) ? this : nullptr;
}
-Size WindowRenderObject::OnMeasureContent(const Size& available_size) {
- if (const auto child = GetChild()) child->Measure(available_size);
- return available_size;
+Size WindowRenderObject::OnMeasureContent(
+ const MeasureRequirement& requirement) {
+ if (const auto child = GetChild()) {
+ child->Measure(requirement);
+ return child->GetMeasuredSize();
+ } else {
+ return Size{};
+ }
}
void WindowRenderObject::OnLayoutContent(const Rect& content_rect) {