diff options
-rw-r--r-- | include/cru/ui/render/FlexLayoutRenderObject.hpp | 9 | ||||
-rw-r--r-- | include/cru/ui/render/LayoutHelper.hpp | 13 | ||||
-rw-r--r-- | include/cru/ui/render/StackLayoutRenderObject.hpp | 19 | ||||
-rw-r--r-- | src/ui/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/ui/render/FlexLayoutRenderObject.cpp | 77 | ||||
-rw-r--r-- | src/ui/render/LayoutHelper.cpp | 36 | ||||
-rw-r--r-- | src/ui/render/StackLayoutRenderObject.cpp | 85 |
7 files changed, 151 insertions, 90 deletions
diff --git a/include/cru/ui/render/FlexLayoutRenderObject.hpp b/include/cru/ui/render/FlexLayoutRenderObject.hpp index 83d1e268..4b1e079d 100644 --- a/include/cru/ui/render/FlexLayoutRenderObject.hpp +++ b/include/cru/ui/render/FlexLayoutRenderObject.hpp @@ -3,8 +3,9 @@ namespace cru::ui::render { // Measure Logic (v0.1): -// 1. Layout all children with unspecified(infinate) max main axis length. If -// max cross axis length of parent is specified, then it is passed to children. +// Cross axis measure logic is the same as stack layout. +// +// 1. Layout all children with unspecified(infinate) max main axis length. // // 2. Add up main axis length of children to get total main length. // @@ -72,10 +73,6 @@ namespace cru::ui::render { // (if specified), then coerce the length to the min value but not report error // and just fill the rest space with blank. // -// 5. Result cross axis length is the max cross axis length of all children. If -// min cross axis length is specified and result length is smaller than it, then -// result length is coerced to it. -// class FlexLayoutRenderObject : public LayoutRenderObject<FlexChildLayoutData> { public: FlexLayoutRenderObject() = default; diff --git a/include/cru/ui/render/LayoutHelper.hpp b/include/cru/ui/render/LayoutHelper.hpp new file mode 100644 index 00000000..8536c451 --- /dev/null +++ b/include/cru/ui/render/LayoutHelper.hpp @@ -0,0 +1,13 @@ +#pragma once +#include "Base.hpp" + +#include "MeasureRequirement.hpp" + +namespace cru::ui::render { +float CalculateAnchorByAlignment(Alignment alignment, float start_point, + float content_length, float child_length); + +MeasureLength StackLayoutCalculateChildMaxLength( + MeasureLength parent_preferred_size, MeasureLength parent_max_size, + MeasureLength child_min_size, std::string_view exceeds_message); +} // namespace cru::ui::render diff --git a/include/cru/ui/render/StackLayoutRenderObject.hpp b/include/cru/ui/render/StackLayoutRenderObject.hpp index e89e5346..f6b4ffb7 100644 --- a/include/cru/ui/render/StackLayoutRenderObject.hpp +++ b/include/cru/ui/render/StackLayoutRenderObject.hpp @@ -3,7 +3,24 @@ namespace cru::ui::render { // Measure Logic: -// +// Following rules are applied both horizontally and vertically. +// +// 1. Measure each children with min size not specified and max size as +// following rules: +// +// 1.1. If parent's preferred size is set and it is not less than child's min +// size, then it is used as child's max size. +// +// 1.2. Or if parent's max size is set, then it is used as child's max size. +// If it is less than child's min size, then log an warning about that. +// +// 2. Result size is children's max size. +// +// 3. If preferred size is specified and result size is smaller than it, coerce +// result size to preferred size. +// +// 4. If result size is smaller than min size (if specified), coerce result size +// to min size. class StackLayoutRenderObject : public LayoutRenderObject<StackChildLayoutData> { public: diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 9043974e..954dacb0 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(cru_ui STATIC render/BorderRenderObject.cpp render/CanvasRenderObject.cpp render/FlexLayoutRenderObject.cpp + render/LayoutHelper.cpp render/RenderObject.cpp render/ScrollRenderObject.cpp render/StackLayoutRenderObject.cpp @@ -51,6 +52,7 @@ target_sources(cru_ui PUBLIC ${CRU_UI_INCLUDE_DIR}/render/BorderRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/CanvasRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/FlexLayoutRenderObject.hpp + ${CRU_UI_INCLUDE_DIR}/render/LayoutHelper.hpp ${CRU_UI_INCLUDE_DIR}/render/LayoutRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/MeasureRequirement.hpp ${CRU_UI_INCLUDE_DIR}/render/RenderObject.hpp diff --git a/src/ui/render/FlexLayoutRenderObject.cpp b/src/ui/render/FlexLayoutRenderObject.cpp index 0afe6ef5..9e5139ff 100644 --- a/src/ui/render/FlexLayoutRenderObject.cpp +++ b/src/ui/render/FlexLayoutRenderObject.cpp @@ -2,6 +2,7 @@ #include "cru/common/Logger.hpp" #include "cru/platform/graph/util/Painter.hpp" +#include "cru/ui/render/LayoutHelper.hpp" #include <algorithm> #include <functional> @@ -101,11 +102,25 @@ Size FlexLayoutMeasureContentImpl( MeasureLength min_main_length = GetMain(requirement.min, direction_tag); MeasureLength min_cross_length = GetCross(requirement.min, direction_tag); - // step 1. + std::vector<MeasureLength> child_cross_measure_requirement; + child_cross_measure_requirement.reserve(children.size()); + for (auto child : children) { + child_cross_measure_requirement.push_back( + StackLayoutCalculateChildMaxLength( + preferred_cross_length, max_cross_length, + GetCross(child->GetMinSize(), direction_tag), + "StackLayoutRenderObject: Child's min cross size is bigger than " + "parent's max cross size.")); + } + + // step 1. + for (Index i = 0; i < child_count; i++) { + const auto child = children[i]; child->Measure(MeasureRequirement{CreateTSize<MeasureSize>( MeasureLength::NotSpecified(), - max_cross_length, direction_tag), + child_cross_measure_requirement[i], + direction_tag), MeasureSize::NotSpecified()}, MeasureSize::NotSpecified()); } @@ -193,13 +208,15 @@ Size FlexLayoutMeasureContentImpl( new_measure_length = 0.f; } - child->Measure(MeasureRequirement{CreateTSize<MeasureSize>( - new_measure_length, - max_cross_length, direction_tag), - MeasureSize::NotSpecified()}, - CreateTSize<MeasureSize>(new_measure_length, - MeasureLength::NotSpecified(), - direction_tag)); + child->Measure( + MeasureRequirement{ + CreateTSize<MeasureSize>(new_measure_length, + child_cross_measure_requirement[i], + direction_tag), + MeasureSize::NotSpecified()}, + CreateTSize<MeasureSize>(new_measure_length, + MeasureLength::NotSpecified(), + direction_tag)); const Size new_size = child->GetSize(); const float new_main_length = GetMain(new_size, direction_tag); @@ -256,7 +273,8 @@ Size FlexLayoutMeasureContentImpl( child->Measure( MeasureRequirement{ CreateTSize<MeasureSize>(MeasureLength::NotSpecified(), - max_cross_length, direction_tag), + child_cross_measure_requirement[i], + direction_tag), CreateTSize<MeasureSize>(new_measure_length, MeasureLength::NotSpecified(), direction_tag)}, @@ -303,10 +321,10 @@ Size FlexLayoutMeasureContentImpl( total_length = min_main_length.GetLengthOrUndefined(); } - if (min_main_length.IsSpecified() && - child_max_cross_length < min_main_length.GetLengthOrUndefined()) { - child_max_cross_length = min_main_length.GetLengthOrUndefined(); - } + child_max_cross_length = + std::max(preferred_cross_length.GetLengthOr0(), child_max_cross_length); + child_max_cross_length = + std::max(min_cross_length.GetLengthOr0(), child_max_cross_length); return CreateTSize<Size>(total_length, child_max_cross_length, direction_tag); } @@ -326,21 +344,6 @@ Size FlexLayoutRenderObject::OnMeasureContent( } void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { - auto calculate_cross_anchor = [](FlexCrossAlignment alignment, - float start_point, float content_length, - float child_length) -> float { - switch (alignment) { - case FlexCrossAlignment::Start: - return start_point; - case FlexCrossAlignment::Center: - return start_point + (content_length - child_length) / 2.0f; - case FlexCrossAlignment::End: - return start_point + content_length - child_length; - default: - return start_point; - } - }; - const auto& children = GetChildren(); const Index child_count = children.size(); @@ -354,8 +357,8 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { GetItemCrossAlign()); child->Layout( Point{content_rect.left + current_main_offset, - calculate_cross_anchor(cross_align, content_rect.top, - content_rect.height, size.height)}); + CalculateAnchorByAlignment(cross_align, content_rect.top, + content_rect.height, size.height)}); current_main_offset += size.width; } @@ -369,8 +372,8 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { GetItemCrossAlign()); child->Layout( Point{content_rect.GetRight() - current_main_offset, - calculate_cross_anchor(cross_align, content_rect.top, - content_rect.height, size.height)}); + CalculateAnchorByAlignment(cross_align, content_rect.top, + content_rect.height, size.height)}); current_main_offset += size.width; } } else if (direction_ == FlexDirection::Vertical) { @@ -383,8 +386,8 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { GetItemCrossAlign()); child->Layout( Point{content_rect.top + current_main_offset, - calculate_cross_anchor(cross_align, content_rect.left, - content_rect.width, size.width)}); + CalculateAnchorByAlignment(cross_align, content_rect.left, + content_rect.width, size.width)}); current_main_offset += size.height; } } else { @@ -397,8 +400,8 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { GetItemCrossAlign()); child->Layout( Point{content_rect.GetBottom() - current_main_offset, - calculate_cross_anchor(cross_align, content_rect.left, - content_rect.width, size.width)}); + CalculateAnchorByAlignment(cross_align, content_rect.left, + content_rect.width, size.width)}); current_main_offset += size.height; } } diff --git a/src/ui/render/LayoutHelper.cpp b/src/ui/render/LayoutHelper.cpp new file mode 100644 index 00000000..31cf5c08 --- /dev/null +++ b/src/ui/render/LayoutHelper.cpp @@ -0,0 +1,36 @@ +#include "cru/ui/render/LayoutHelper.hpp" + +#include "cru/common/Logger.hpp" + +namespace cru::ui::render { +float CalculateAnchorByAlignment(Alignment alignment, float start_point, + float content_length, float child_length) { + switch (alignment) { + case FlexCrossAlignment::Start: + return start_point; + case FlexCrossAlignment::Center: + return start_point + (content_length - child_length) / 2.0f; + case FlexCrossAlignment::End: + return start_point + content_length - child_length; + default: + return start_point; + } +} + +MeasureLength StackLayoutCalculateChildMaxLength( + MeasureLength parent_preferred_size, MeasureLength parent_max_size, + MeasureLength child_min_size, std::string_view exceeds_message) { + if (parent_max_size.GetLengthOrMax() < child_min_size.GetLengthOr0()) { + log::Warn(exceeds_message); + return parent_max_size; + } + + if (parent_preferred_size.IsSpecified() && + parent_preferred_size.GetLengthOrUndefined() >= + child_min_size.GetLengthOr0()) { + return parent_preferred_size; + } else { + return parent_max_size; + } +} +} // namespace cru::ui::render diff --git a/src/ui/render/StackLayoutRenderObject.cpp b/src/ui/render/StackLayoutRenderObject.cpp index b953ae7e..a6ed7708 100644 --- a/src/ui/render/StackLayoutRenderObject.cpp +++ b/src/ui/render/StackLayoutRenderObject.cpp @@ -1,60 +1,53 @@ #include "cru/ui/render/StackLayoutRenderObject.hpp" +#include "cru/common/Logger.hpp" +#include "cru/ui/render/LayoutHelper.hpp" + #include <algorithm> namespace cru::ui::render { Size StackLayoutRenderObject::OnMeasureContent( const MeasureRequirement& requirement, const MeasureSize& preferred_size) { - // TODO: Rewrite this. - CRU_UNUSED(requirement); - CRU_UNUSED(preferred_size); - throw std::runtime_error("Not implemented."); + Size max_size; + for (const auto child : GetChildren()) { + child->Measure( + MeasureRequirement{ + MeasureSize{StackLayoutCalculateChildMaxLength( + preferred_size.width, requirement.max.width, + child->GetMinSize().width, + "StackLayoutRenderObject: Child's min width is " + "bigger than parent's max width."), + StackLayoutCalculateChildMaxLength( + preferred_size.height, requirement.max.height, + child->GetMinSize().height, + "StackLayoutRenderObject: Child's min height is " + "bigger than parent's max height.")}, + MeasureSize::NotSpecified()}, + MeasureSize::NotSpecified()); + const auto size = child->GetSize(); + max_size.width = std::max(max_size.width, size.width); + max_size.height = std::max(max_size.height, size.height); + } + + max_size = Max(preferred_size.GetSizeOr0(), max_size); + max_size = Max(requirement.min.GetSizeOr0(), max_size); - // 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; + return max_size; } void StackLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { - // 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 count = GetChildCount(); - // const auto& children = GetChildren(); + 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}); - // } + for (int i = 0; i < count; i++) { + const auto child = children[i]; + const auto& layout_data = GetChildLayoutData(i); + const auto& size = child->GetSize(); + child->Layout(Point{ + CalculateAnchorByAlignment(layout_data.horizontal, content_rect.left, + content_rect.width, size.width), + CalculateAnchorByAlignment(layout_data.vertical, content_rect.top, + content_rect.height, size.height)}); + } } } // namespace cru::ui::render |