From ad8accd8b9f3908c3da012bf624cc1847c8f6592 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 7 Feb 2024 17:02:47 +0800 Subject: [PATCH 1/8] segmented Signed-off-by: Tianer Zhou Change-Id: I0a0a4b6294eb05a566465f0fe1768c62c320aa55 --- .../core/components_ng/pattern/BUILD.gn | 2 + .../waterflow/water_flow_layout_algorithm.cpp | 130 +++--------- .../waterflow/water_flow_layout_algorithm.h | 2 - .../waterflow/water_flow_layout_info.cpp | 95 +++++++-- .../waterflow/water_flow_layout_info.h | 64 +++++- .../waterflow/water_flow_layout_utils.cpp | 120 +++++++++++ .../waterflow/water_flow_layout_utils.h | 38 ++++ .../pattern/waterflow/water_flow_pattern.cpp | 15 +- .../pattern/waterflow/water_flow_pattern.h | 2 +- .../waterflow/water_flow_segmented_layout.cpp | 199 ++++++++++++++++++ .../waterflow/water_flow_segmented_layout.h | 80 +++++++ .../core/pipeline/mock_pipeline_context.cpp | 1 - test/unittest/BUILD.gn | 2 + test/unittest/core/pattern/waterflow/BUILD.gn | 5 +- .../pattern/waterflow/water_flow_item_maps.h | 56 +++++ .../waterflow/water_flow_layout_info_test.cpp | 54 +++++ .../pattern/waterflow/water_flow_test_ng.cpp | 32 +-- 17 files changed, 736 insertions(+), 161 deletions(-) create mode 100644 frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp create mode 100644 frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h create mode 100644 frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp create mode 100644 frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h create mode 100644 test/unittest/core/pattern/waterflow/water_flow_item_maps.h create mode 100644 test/unittest/core/pattern/waterflow/water_flow_layout_info_test.cpp diff --git a/frameworks/core/components_ng/pattern/BUILD.gn b/frameworks/core/components_ng/pattern/BUILD.gn index 902e80b100a..1eb2a1f392f 100644 --- a/frameworks/core/components_ng/pattern/BUILD.gn +++ b/frameworks/core/components_ng/pattern/BUILD.gn @@ -512,6 +512,8 @@ build_component_ng("pattern_ng") { "waterflow/water_flow_model_ng.cpp", "waterflow/water_flow_paint_method.cpp", "waterflow/water_flow_pattern.cpp", + "waterflow/water_flow_segmented_layout.cpp", + "waterflow/water_flow_layout_utils.cpp", ] external_deps = [] diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp index e02049d0acb..99b9785e633 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp @@ -19,34 +19,15 @@ #include "core/components_ng/base/frame_node.h" #include "core/components_ng/layout/layout_wrapper.h" #include "core/components_ng/pattern/grid/grid_utils.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/pattern/waterflow/water_flow_item_layout_property.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_utils.h" #include "core/components_ng/property/measure_utils.h" #include "core/components_ng/property/templates_parser.h" namespace OHOS::Ace::NG { namespace { const std::string UNIT_AUTO = "auto"; -std::string PreParseArgs(const std::string& args) -{ - if (args.empty() || args.find(UNIT_AUTO) == std::string::npos) { - return args; - } - std::string rowsArgs; - std::vector strs; - StringUtils::StringSplitter(args, ' ', strs); - std::string current; - size_t rowArgSize = strs.size(); - for (size_t i = 0; i < rowArgSize; ++i) { - current = strs[i]; - // "auto" means 1fr in waterflow - if (strs[i] == std::string(UNIT_AUTO)) { - current = "1fr"; - } - rowsArgs += ' ' + current; - } - return rowsArgs; -} } // namespace float WaterFlowLayoutAlgorithm::ComputeCrossPosition(int32_t crossIndex) const @@ -79,9 +60,9 @@ void WaterFlowLayoutAlgorithm::InitialItemsCrossSize( std::vector crossLens; std::pair, bool> cross; if (axis_ == Axis::VERTICAL) { - cross = ParseTemplateArgs(PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount); + cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount); } else { - cross = ParseTemplateArgs(PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount); + cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount); } crossLens = cross.first; if (crossLens.empty()) { @@ -92,7 +73,7 @@ void WaterFlowLayoutAlgorithm::InitialItemsCrossSize( } // cross count changed by auto-fill and cross size change - if (!layoutInfo_.waterFlowItems_.empty() && crossLens.size() != layoutInfo_.waterFlowItems_.size()) { + if (!layoutInfo_.items_[0].empty() && crossLens.size() != layoutInfo_.items_[0].size()) { layoutInfo_.Reset(); } @@ -100,7 +81,7 @@ void WaterFlowLayoutAlgorithm::InitialItemsCrossSize( for (const auto& len : crossLens) { itemsCrossSize_.try_emplace(index, len); itemsCrossPosition_.try_emplace(index, ComputeCrossPosition(index)); - layoutInfo_.waterFlowItems_.try_emplace(index, std::map>()); + layoutInfo_.items_[0].try_emplace(index, std::map>()); ++index; } } @@ -133,7 +114,7 @@ void WaterFlowLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) if (crossIndex == -1) { // jump to out of cache } else { - JumpToTargetAlign(layoutInfo_.waterFlowItems_[crossIndex][layoutInfo_.jumpIndex_]); + JumpToTargetAlign(layoutInfo_.items_[0][crossIndex][layoutInfo_.jumpIndex_]); layoutInfo_.align_ = ScrollAlign::START; layoutInfo_.jumpIndex_ = EMPTY_JUMP_INDEX; } @@ -173,13 +154,14 @@ void WaterFlowLayoutAlgorithm::MeasureForAnimation(LayoutWrapper* layoutWrapper) if (!itemWrapper) { layoutInfo_.targetIndex_.reset(); break; - } - itemWrapper->Measure(CreateChildConstraint(position.crossIndex, layoutProperty, itemWrapper)); + } + itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint( + { itemsCrossSize_.at(position.crossIndex), mainSize_, axis_ }, layoutProperty, itemWrapper)); auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize(); auto itemHeight = GetMainAxisSize(itemSize, axis_); - auto item = layoutInfo_.waterFlowItems_[position.crossIndex].find(currentIndex); - if (item == layoutInfo_.waterFlowItems_[position.crossIndex].end()) { - layoutInfo_.waterFlowItems_[position.crossIndex][currentIndex] = + auto item = layoutInfo_.items_[0][position.crossIndex].find(currentIndex); + if (item == layoutInfo_.items_[0][position.crossIndex].end()) { + layoutInfo_.items_[0][position.crossIndex][currentIndex] = std::make_pair(position.startMainPos, itemHeight); } else { if (item->second.second != itemHeight) { @@ -207,7 +189,7 @@ void WaterFlowLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); layoutInfo_.UpdateStartIndex(); auto firstIndex = layoutInfo_.endIndex_; - for (const auto& mainPositions : layoutInfo_.waterFlowItems_) { + for (const auto& mainPositions : layoutInfo_.items_[0]) { for (const auto& item : mainPositions.second) { if (item.first < layoutInfo_.startIndex_ || item.first > layoutInfo_.endIndex_) { continue; @@ -259,72 +241,6 @@ void WaterFlowLayoutAlgorithm::LayoutFooter(LayoutWrapper* layoutWrapper, const } } -LayoutConstraintF WaterFlowLayoutAlgorithm::CreateChildConstraint(int32_t crossIndex, - const RefPtr& layoutProperty, const RefPtr& childLayoutWrapper) -{ - auto itemConstraint = layoutProperty->CreateChildConstraint(); - if (itemsCrossSize_.find(crossIndex) == itemsCrossSize_.end()) { - return itemConstraint; - } - auto itemMainSize = mainSize_; - auto itemCrossSize = itemsCrossSize_.at(crossIndex); - auto itemIdealSize = - axis_ == Axis::VERTICAL ? SizeF(itemCrossSize, itemMainSize) : SizeF(itemMainSize, itemCrossSize); - - itemConstraint.maxSize = itemIdealSize; - itemConstraint.maxSize.SetMainSize(Infinity(), axis_); - itemConstraint.percentReference = itemIdealSize; - - CHECK_NULL_RETURN(layoutProperty->HasItemLayoutConstraint(), itemConstraint); - - OptionalSizeF childMinSize; - OptionalSizeF childMaxSize; - // Waterflow ItemLayoutConstraint - auto itemMinSize = layoutProperty->GetItemMinSize(); - if (itemMinSize.has_value()) { - childMinSize = ConvertToOptionalSize( - itemMinSize.value(), layoutProperty->GetLayoutConstraint()->scaleProperty, itemIdealSize); - } - auto itemMaxSize = layoutProperty->GetItemMaxSize(); - if (itemMaxSize.has_value()) { - childMaxSize = ConvertToOptionalSize( - itemMaxSize.value(), layoutProperty->GetLayoutConstraint()->scaleProperty, itemIdealSize); - } - - if (childMaxSize.AtLeastOneValid()) { - itemConstraint.maxSize.UpdateSizeWhenSmaller(childMaxSize.ConvertToSizeT()); - } - if (childMinSize.AtLeastOneValid()) { - itemConstraint.minSize.UpdateSizeWhenLarger(childMinSize.ConvertToSizeT()); - } - - // FlowItem layoutConstraint - CHECK_NULL_RETURN(childLayoutWrapper, itemConstraint); - auto childLayoutProperty = - AceType::DynamicCast(childLayoutWrapper->GetLayoutProperty()); - CHECK_NULL_RETURN(childLayoutProperty, itemConstraint); - if (childLayoutProperty->HasLayoutConstraint()) { - if (childLayoutProperty->GetMaxSize().has_value()) { - itemConstraint.UpdateMaxSizeWithCheck(ConvertToSize(childLayoutProperty->GetMaxSize().value(), - itemConstraint.scaleProperty, itemConstraint.percentReference)); - } - if (childLayoutProperty->GetMinSize().has_value()) { - itemConstraint.UpdateMinSizeWithCheck(ConvertToSize(childLayoutProperty->GetMinSize().value(), - itemConstraint.scaleProperty, itemConstraint.percentReference)); - } - } - - childLayoutProperty->ResetCalcMinSize(); - childLayoutProperty->ResetCalcMaxSize(); - - childLayoutProperty->UpdateItemCalcMaxSize(CalcSize(CalcLength(itemConstraint.maxSize.Width(), DimensionUnit::PX), - CalcLength(itemConstraint.maxSize.Height(), DimensionUnit::PX))); - childLayoutProperty->UpdateItemCalcMinSize(CalcSize(CalcLength(itemConstraint.minSize.Width(), DimensionUnit::PX), - CalcLength(itemConstraint.minSize.Height(), DimensionUnit::PX))); - - return itemConstraint; -} - FlowItemPosition WaterFlowLayoutAlgorithm::GetItemPosition(int32_t index) { auto crossIndex = layoutInfo_.GetCrossIndex(index); @@ -332,7 +248,7 @@ FlowItemPosition WaterFlowLayoutAlgorithm::GetItemPosition(int32_t index) if (crossIndex != -1) { return { crossIndex, layoutInfo_.GetStartMainPos(crossIndex, index) }; } - auto itemIndex = layoutInfo_.GetCrossIndexForNextItem(); + auto itemIndex = layoutInfo_.GetCrossIndexForNextItem(layoutInfo_.GetSegment(index)); if (itemIndex.lastItemIndex < 0) { return { itemIndex.crossIndex, 0.0f }; } @@ -360,12 +276,14 @@ void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layou if (!itemWrapper) { break; } - itemWrapper->Measure(CreateChildConstraint(position.crossIndex, layoutProperty, itemWrapper)); + + itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint( + { itemsCrossSize_.at(position.crossIndex), mainSize_, axis_ }, layoutProperty, itemWrapper)); auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize(); auto itemHeight = GetMainAxisSize(itemSize, axis_); - auto item = layoutInfo_.waterFlowItems_[position.crossIndex].find(currentIndex); - if (item == layoutInfo_.waterFlowItems_[position.crossIndex].end()) { - layoutInfo_.waterFlowItems_[position.crossIndex][currentIndex] = + auto item = layoutInfo_.items_[0][position.crossIndex].find(currentIndex); + if (item == layoutInfo_.items_[0][position.crossIndex].end()) { + layoutInfo_.items_[0][position.crossIndex][currentIndex] = std::make_pair(position.startMainPos, itemHeight); } else { if (item->second.second != itemHeight) { @@ -374,7 +292,7 @@ void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layou } } if (layoutInfo_.jumpIndex_ == currentIndex) { - JumpToTargetAlign(layoutInfo_.waterFlowItems_[position.crossIndex][currentIndex]); + JumpToTargetAlign(layoutInfo_.items_[0][position.crossIndex][currentIndex]); layoutInfo_.currentOffset_ += layoutInfo_.restoreOffset_; // restoreOffSet only be used once layoutInfo_.restoreOffset_ = 0.0f; @@ -425,7 +343,9 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L for (auto i = oldStart; i >= layoutInfo_.startIndex_; i--) { auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(GetChildIndexWithFooter(i)); auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - itemWrapper->Measure(CreateChildConstraint(layoutInfo_.GetCrossIndex(i), layoutProperty, itemWrapper)); + float crossSize = itemsCrossSize_.at(layoutInfo_.GetCrossIndex(i)); + itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint( + { crossSize, mainSize_, axis_ }, layoutProperty, itemWrapper)); } } else { layoutInfo_.offsetEnd_ = false; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h index d7962bcda1b..2452d4d0d4c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h @@ -46,8 +46,6 @@ private: void MeasureForAnimation(LayoutWrapper* layoutWrapper); void FillViewport(float mainSize, LayoutWrapper* layoutWrapper); void ModifyCurrentOffsetWhenReachEnd(float mainSize, LayoutWrapper* layoutWrapper); - LayoutConstraintF CreateChildConstraint(int32_t crossIndex, const RefPtr& layoutProperty, - const RefPtr& childLayoutWrapper); float ComputeCrossPosition(int32_t crossIndex) const; void InitialItemsCrossSize( const RefPtr& layoutProperty, const SizeF& frameSize, int32_t childrenCount); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index b35cb665a35..7bed488b2d5 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -18,9 +18,9 @@ #include namespace OHOS::Ace::NG { -int32_t WaterFlowLayoutInfo::GetCrossIndex(int32_t itemIndex) +int32_t WaterFlowLayoutInfo::GetCrossIndex(int32_t itemIndex) const { - for (const auto& crossItems : waterFlowItems_) { + for (const auto& crossItems : items_[GetSegment(itemIndex)]) { auto iter = crossItems.second.find(itemIndex); if (iter != crossItems.second.end()) { return crossItems.first; @@ -31,7 +31,7 @@ int32_t WaterFlowLayoutInfo::GetCrossIndex(int32_t itemIndex) void WaterFlowLayoutInfo::UpdateStartIndex() { - auto nextPosition = GetCrossIndexForNextItem(); + auto nextPosition = GetCrossIndexForNextItem(GetSegment(endIndex_)); auto mainHeight = GetMainHeight(nextPosition.crossIndex, nextPosition.lastItemIndex); // need more items for currentOffset_ if (LessOrEqual(currentOffset_ + mainHeight, 0.0f)) { @@ -39,7 +39,7 @@ void WaterFlowLayoutInfo::UpdateStartIndex() } int32_t tempStartIndex = -1; - for (const auto& crossItems : waterFlowItems_) { + for (const auto& crossItems : items_[GetSegment(tempStartIndex)]) { for (const auto& iter : crossItems.second) { if (GreatNotEqual(iter.second.first + iter.second.second + currentOffset_, 0.0f)) { tempStartIndex = tempStartIndex != -1 ? std::min(tempStartIndex, iter.first) : iter.first; @@ -60,7 +60,7 @@ int32_t WaterFlowLayoutInfo::GetEndIndexByOffset(float offset) const { int32_t endIndex = 0; bool found = false; - for (const auto& crossItems : waterFlowItems_) { + for (const auto& crossItems : items_[GetSegment(endIndex)]) { for (const auto& iter : crossItems.second) { if (GreatNotEqual(iter.second.first + iter.second.second + offset, 0)) { endIndex = std::max(endIndex, iter.first); @@ -75,7 +75,7 @@ int32_t WaterFlowLayoutInfo::GetEndIndexByOffset(float offset) const float WaterFlowLayoutInfo::GetMaxMainHeight() const { float result = 0.0f; - for (const auto& crossItems : waterFlowItems_) { + for (const auto& crossItems : *items_.rbegin()) { if (crossItems.second.empty()) { continue; } @@ -96,11 +96,11 @@ float WaterFlowLayoutInfo::GetContentHeight() const return NearZero(maxHeight_) ? GetMaxMainHeight() : maxHeight_; } -float WaterFlowLayoutInfo::GetMainHeight(int32_t crossIndex, int32_t itemIndex) +float WaterFlowLayoutInfo::GetMainHeight(int32_t crossIndex, int32_t itemIndex) const { float result = 0.0f; - auto cross = waterFlowItems_.find(crossIndex); - if (cross == waterFlowItems_.end()) { + auto cross = items_[GetSegment(itemIndex)].find(crossIndex); + if (cross == items_[GetSegment(itemIndex)].end()) { return result; } auto item = cross->second.find(itemIndex); @@ -111,11 +111,11 @@ float WaterFlowLayoutInfo::GetMainHeight(int32_t crossIndex, int32_t itemIndex) return result; } -float WaterFlowLayoutInfo::GetStartMainPos(int32_t crossIndex, int32_t itemIndex) +float WaterFlowLayoutInfo::GetStartMainPos(int32_t crossIndex, int32_t itemIndex) const { float result = 0.0f; - auto cross = waterFlowItems_.find(crossIndex); - if (cross == waterFlowItems_.end()) { + auto cross = items_[GetSegment(itemIndex)].find(crossIndex); + if (cross == items_[GetSegment(itemIndex)].end()) { return result; } auto item = cross->second.find(itemIndex); @@ -126,10 +126,10 @@ float WaterFlowLayoutInfo::GetStartMainPos(int32_t crossIndex, int32_t itemIndex return result; } -bool WaterFlowLayoutInfo::IsAllCrossReachend(float mainSize) const +bool WaterFlowLayoutInfo::IsAllCrossReachEnd(float mainSize) const { bool result = true; - for (const auto& crossItems : waterFlowItems_) { + for (const auto& crossItems : *items_.rbegin()) { if (crossItems.second.empty()) { result = false; break; @@ -144,13 +144,13 @@ bool WaterFlowLayoutInfo::IsAllCrossReachend(float mainSize) const return result; } -FlowItemIndex WaterFlowLayoutInfo::GetCrossIndexForNextItem() const +FlowItemIndex WaterFlowLayoutInfo::GetCrossIndexForNextItem(int32_t segmentIdx) const { FlowItemIndex position = { 0, -1 }; auto minHeight = -1.0f; - auto crossSize = static_cast(waterFlowItems_.size()); + auto crossSize = static_cast(items_[segmentIdx].size()); for (int32_t i = 0; i < crossSize; ++i) { - const auto& crossItems = waterFlowItems_.at(i); + const auto& crossItems = items_[segmentIdx].at(i); if (crossItems.empty()) { position.crossIndex = i; position.lastItemIndex = -1; @@ -189,7 +189,9 @@ void WaterFlowLayoutInfo::Reset() startIndex_ = 0; endIndex_ = 0; targetIndex_.reset(); - waterFlowItems_.clear(); + for (auto& map : items_) { + map.clear(); + } } void WaterFlowLayoutInfo::Reset(int32_t resetFrom) @@ -202,13 +204,13 @@ void WaterFlowLayoutInfo::Reset(int32_t resetFrom) int32_t WaterFlowLayoutInfo::GetCrossCount() const { - return static_cast(waterFlowItems_.size()); + return static_cast(items_[0].size()); } int32_t WaterFlowLayoutInfo::GetMainCount() const { int32_t maxMainCount = 0; - for (const auto& crossItems : waterFlowItems_) { + for (const auto& crossItems : items_[0]) { if (crossItems.second.empty()) { continue; } @@ -223,7 +225,7 @@ int32_t WaterFlowLayoutInfo::GetMainCount() const void WaterFlowLayoutInfo::ClearCacheAfterIndex(int32_t currentIndex) { - for (auto& crossItems : waterFlowItems_) { + for (auto& crossItems : items_[GetSegment(currentIndex)]) { if (crossItems.second.empty()) { continue; } @@ -233,6 +235,9 @@ void WaterFlowLayoutInfo::ClearCacheAfterIndex(int32_t currentIndex) }); crossItems.second.erase(clearFrom, crossItems.second.end()); } + for (int32_t i = GetSegment(currentIndex) + 1; i < items_.size(); ++i) { + items_[i].clear(); + } } bool WaterFlowLayoutInfo::ReachStart(float prevOffset, bool firstLayout) const @@ -249,4 +254,52 @@ bool WaterFlowLayoutInfo::ReachEnd(float prevOffset) const auto scrollUpToReachEnd = LessNotEqual(prevOffset, minOffset) && GreatOrEqual(currentOffset_, minOffset); return scrollDownToReachEnd || scrollUpToReachEnd; } + +int32_t WaterFlowLayoutInfo::GetSegment(int32_t itemIdx) const +{ + if (segmentTails_.empty()) { + return 0; + } + auto cache = segmentCache_.find(itemIdx); + if (cache != segmentCache_.end()) { + return cache->second; + } + + auto it = std::lower_bound(segmentTails_.begin(), segmentTails_.end(), itemIdx); + if (it == segmentTails_.end()) { + return *segmentTails_.rbegin(); + } + int32_t idx = it - segmentTails_.begin(); + segmentCache_[itemIdx] = idx; + return idx; +} + +int32_t WaterFlowLayoutInfo::FastSolveStartIndex() const +{ + auto it = std::upper_bound(endPosArray_.begin(), endPosArray_.end(), -currentOffset_, + [](float value, const std::pair& info) { return value < info.first; }); + if (it == endPosArray_.end()) { + return 0; + } + return it->second; +} + +int32_t WaterFlowLayoutInfo::FastSolveEndIndex(float mainSize) const +{ + auto it = std::lower_bound(itemInfos_.begin(), itemInfos_.end(), mainSize - currentOffset_, + [](const std::pair& info, float value) { return info.second.first < value; }); + + if (it == itemInfos_.end()) { + return childrenCount_ - 1; + } + return it->first - 1; +} + +void WaterFlowLayoutInfo::AddItemToCache(int32_t idx, float startPos, float height) +{ + itemInfos_[idx] = { startPos, height }; + if (endPosArray_.empty() || LessNotEqual(endPosArray_.back().first, startPos + height)) { + endPosArray_.emplace_back( startPos + height, idx ); + } +} } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index c3af154dac5..94c5bad07a6 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -39,15 +39,23 @@ constexpr int32_t EMPTY_JUMP_INDEX = -2; class WaterFlowLayoutInfo { public: - int32_t GetCrossIndex(int32_t itemIndex); + int32_t GetCrossIndex(int32_t itemIndex) const; void UpdateStartIndex(); int32_t GetEndIndexByOffset(float offset) const; float GetMaxMainHeight() const; float GetContentHeight() const; - bool IsAllCrossReachend(float mainSize) const; - FlowItemIndex GetCrossIndexForNextItem() const; - float GetMainHeight(int32_t crossIndex, int32_t itemIndex); - float GetStartMainPos(int32_t crossIndex, int32_t itemIndex); + bool IsAllCrossReachEnd(float mainSize) const; + + /** + * @brief Get the next available cross index to place a new item. + * + * @param segmentIdx index of the WaterFlow segment. + * @return FlowItemIndex + */ + FlowItemIndex GetCrossIndexForNextItem(int32_t segmentIdx) const; + + float GetMainHeight(int32_t crossIndex, int32_t itemIndex) const; + float GetStartMainPos(int32_t crossIndex, int32_t itemIndex) const; void Reset(); void Reset(int32_t resetFrom); int32_t GetCrossCount() const; @@ -57,6 +65,25 @@ public: bool ReachStart(float prevOffset, bool firstLayout) const; bool ReachEnd(float prevOffset) const; + int32_t GetSegment(int32_t itemIdx) const; + + void AddItemToCache(int32_t idx, float startPos, float height); + + /** + * @brief FInd the first item inside viewport in log_n time using endPosReverseMap_. + * + * @return index of the item. + */ + int32_t FastSolveStartIndex() const; + + /** + * @brief Find the last item inside viewport in log_n time using itemInfos_. + * + * @param mainSize main-axis length of viewport. + * @return index of the item. + */ + int32_t FastSolveEndIndex(float mainSize) const; + float currentOffset_ = 0.0f; float prevOffset_ = 0.0f; float lastMainSize_ = 0.0f; @@ -66,9 +93,9 @@ public: float storedOffset_ = 0.0f; float restoreOffset_ = 0.0f; - bool itemEnd_ = false; bool itemStart_ = false; - bool offsetEnd_ = false; + bool itemEnd_ = false; // last item is partially in viewport + bool offsetEnd_ = false; // last item's bottom is in viewport int32_t jumpIndex_ = EMPTY_JUMP_INDEX; @@ -83,12 +110,29 @@ public: int32_t firstIndex_ = 0; std::optional targetIndex_; - // Map structure: [crossIndex, [index, (mainOffset, itemMainSize)]], - std::map>> waterFlowItems_; + // (mainOffset, itemMainSize) + using ItemInfo = std::pair; + // Map structure: [crossIndex, [index, ItemInfo]], + using ItemMap = std::map>; + + std::vector items_ { ItemMap() }; + // quick access to item info, ignores crossIndex and segments + std::map itemInfos_; + + /** + * @brief pair = { item bottom position, item index }. + * A strictly increasing array of item endPos to speed up startIndex solver. + * Only add to this map when a new endPos is greater than the last one in array. + */ + std::vector> endPosArray_; + + std::vector segmentTails_; + // K: item index; V: corresponding segment index + mutable std::unordered_map segmentCache_; void PrintWaterFlowItems() const { - for (const auto& [key1, map1] : waterFlowItems_) { + for (const auto& [key1, map1] : items_[0]) { std::stringstream ss; ss << key1 << ": {"; for (const auto& [key2, pair] : map1) { diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp new file mode 100644 index 00000000000..f8fd49d864b --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h" + +#include "base/utils/string_utils.h" +#include "core/components_ng/layout/layout_wrapper.h" +#include "core/components_ng/pattern/waterflow/water_flow_item_layout_property.h" +namespace OHOS::Ace::NG { +namespace { +const std::string UNIT_AUTO = "auto"; +} +std::string WaterFlowLayoutUtils::PreParseArgs(const std::string& args) +{ + if (args.empty() || args.find(UNIT_AUTO) == std::string::npos) { + return args; + } + std::string rowsArgs; + std::vector strs; + StringUtils::StringSplitter(args, ' ', strs); + std::string current; + size_t rowArgSize = strs.size(); + for (size_t i = 0; i < rowArgSize; ++i) { + current = strs[i]; + // "auto" means 1fr in waterflow + if (strs[i] == std::string(UNIT_AUTO)) { + current = "1fr"; + } + rowsArgs += ' ' + current; + } + return rowsArgs; +} + +FlowItemPosition WaterFlowLayoutUtils::GetItemPosition(const WaterFlowLayoutInfo& info, int32_t index, float mainGap) +{ + auto crossIndex = info.GetCrossIndex(index); + // already in layoutInfo + if (crossIndex != -1) { + return { crossIndex, info.GetStartMainPos(crossIndex, index) }; + } + auto itemIndex = info.GetCrossIndexForNextItem(info.GetSegment(index)); + if (itemIndex.lastItemIndex < 0) { + return { itemIndex.crossIndex, 0.0f }; + } + auto mainHeight = info.GetMainHeight(itemIndex.crossIndex, itemIndex.lastItemIndex); + return { itemIndex.crossIndex, mainHeight + mainGap }; +} + +LayoutConstraintF WaterFlowLayoutUtils::CreateChildConstraint( + const ConstraintParams& params, const RefPtr& props, const RefPtr& child) +{ + auto itemConstraint = props->CreateChildConstraint(); + auto itemMainSize = params.mainSize; + auto itemIdealSize = + params.axis == Axis::VERTICAL ? SizeF(params.crossSize, itemMainSize) : SizeF(itemMainSize, params.crossSize); + + itemConstraint.maxSize = itemIdealSize; + itemConstraint.maxSize.SetMainSize(Infinity(), params.axis); + itemConstraint.percentReference = itemIdealSize; + + CHECK_NULL_RETURN(props->HasItemLayoutConstraint(), itemConstraint); + + OptionalSizeF childMinSize; + OptionalSizeF childMaxSize; + // Waterflow ItemLayoutConstraint + auto itemMinSize = props->GetItemMinSize(); + if (itemMinSize.has_value()) { + childMinSize = + ConvertToOptionalSize(itemMinSize.value(), props->GetLayoutConstraint()->scaleProperty, itemIdealSize); + } + auto itemMaxSize = props->GetItemMaxSize(); + if (itemMaxSize.has_value()) { + childMaxSize = + ConvertToOptionalSize(itemMaxSize.value(), props->GetLayoutConstraint()->scaleProperty, itemIdealSize); + } + + if (childMaxSize.AtLeastOneValid()) { + itemConstraint.maxSize.UpdateSizeWhenSmaller(childMaxSize.ConvertToSizeT()); + } + if (childMinSize.AtLeastOneValid()) { + itemConstraint.minSize.UpdateSizeWhenLarger(childMinSize.ConvertToSizeT()); + } + + // FlowItem layoutConstraint + CHECK_NULL_RETURN(child, itemConstraint); + auto childLayoutProperty = AceType::DynamicCast(child->GetLayoutProperty()); + CHECK_NULL_RETURN(childLayoutProperty, itemConstraint); + if (childLayoutProperty->HasLayoutConstraint()) { + if (childLayoutProperty->GetMaxSize().has_value()) { + itemConstraint.UpdateMaxSizeWithCheck(ConvertToSize(childLayoutProperty->GetMaxSize().value(), + itemConstraint.scaleProperty, itemConstraint.percentReference)); + } + if (childLayoutProperty->GetMinSize().has_value()) { + itemConstraint.UpdateMinSizeWithCheck(ConvertToSize(childLayoutProperty->GetMinSize().value(), + itemConstraint.scaleProperty, itemConstraint.percentReference)); + } + } + + childLayoutProperty->ResetCalcMinSize(); + childLayoutProperty->ResetCalcMaxSize(); + + childLayoutProperty->UpdateItemCalcMaxSize(CalcSize(CalcLength(itemConstraint.maxSize.Width(), DimensionUnit::PX), + CalcLength(itemConstraint.maxSize.Height(), DimensionUnit::PX))); + childLayoutProperty->UpdateItemCalcMinSize(CalcSize(CalcLength(itemConstraint.minSize.Width(), DimensionUnit::PX), + CalcLength(itemConstraint.minSize.Height(), DimensionUnit::PX))); + + return itemConstraint; +} +} // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h new file mode 100644 index 00000000000..7bc6ee5e28a --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_UTILS_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_UTILS_H +#include + +#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" +#include "core/components_ng/property/layout_constraint.h" +namespace OHOS::Ace::NG { +class WaterFlowLayoutUtils { +public: + static std::string PreParseArgs(const std::string& args); + static FlowItemPosition GetItemPosition(const WaterFlowLayoutInfo& info, int32_t index, float mainGap); + + struct ConstraintParams { + float crossSize = 0.0f; + float mainSize = 0.0f; + Axis axis = Axis::VERTICAL; + }; + static LayoutConstraintF CreateChildConstraint(const ConstraintParams& params, + const RefPtr& props, const RefPtr& child); +}; +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_UTILS_H diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 5bfec75e31e..5ea808d49fa 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -70,6 +70,13 @@ bool WaterFlowPattern::UpdateCurrentOffset(float delta, int32_t source) if (layoutInfo_.offsetEnd_ && delta < 0) { return false; } + if (GreatNotEqual(delta, 0.0f)) { + delta = std::min(delta, -layoutInfo_.currentOffset_); + } else if (layoutInfo_.itemEnd_ && !layoutInfo_.endPosArray_.empty()) { + float distanceToEnd = + layoutInfo_.endPosArray_.back().first - (GetMainContentSize() - layoutInfo_.currentOffset_); + delta = std::max(delta, distanceToEnd); + } } FireOnWillScroll(-delta); layoutInfo_.prevOffset_ = layoutInfo_.currentOffset_; @@ -242,7 +249,7 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir layoutInfo_ = std::move(layoutInfo); if (targetIndex_.has_value()) { - ScrollToTargrtIndex(targetIndex_.value()); + ScrollToTargetIndex(targetIndex_.value()); targetIndex_.reset(); } layoutInfo_.UpdateStartIndex(); @@ -255,7 +262,7 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir return NeedRender(); } -bool WaterFlowPattern::ScrollToTargrtIndex(int32_t index) +bool WaterFlowPattern::ScrollToTargetIndex(int32_t index) { if (index == LAST_ITEM) { auto host = GetHost(); @@ -267,7 +274,7 @@ bool WaterFlowPattern::ScrollToTargrtIndex(int32_t index) if (crossIndex == -1) { return false; } - auto item = layoutInfo_.waterFlowItems_[crossIndex][index]; + auto item = layoutInfo_.items_[layoutInfo_.GetSegment(index)].at(crossIndex).at(index); float targetPosition = 0.0; ScrollAlign align = layoutInfo_.align_; switch (align) { @@ -426,7 +433,7 @@ void WaterFlowPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign ali StopAnimate(); if ((index >= 0) || (index == LAST_ITEM)) { if (smooth) { - if (!ScrollToTargrtIndex(index)) { + if (!ScrollToTargetIndex(index)) { targetIndex_ = index; auto host = GetHost(); CHECK_NULL_VOID(host); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index 94f783ec7d8..5ed8eca0691 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -140,7 +140,7 @@ private: SizeF GetContentSize() const; void MarkDirtyNodeSelf(); void OnScrollEndCallback() override; - bool ScrollToTargrtIndex(int32_t index); + bool ScrollToTargetIndex(int32_t index); bool NeedRender(); std::optional targetIndex_; WaterFlowLayoutInfo layoutInfo_; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp new file mode 100644 index 00000000000..9b66a085b6b --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" + +#include "base/geometry/ng/offset_t.h" +#include "core/components_ng/layout/layout_wrapper.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_utils.h" +#include "core/components_ng/property/templates_parser.h" + +namespace OHOS::Ace::NG { +void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) +{ + wrapper_ = wrapper; + auto props = DynamicCast(wrapper->GetLayoutProperty()); + axis_ = props->GetAxis(); + auto [idealSize, matchChildren] = PreMeasureSelf(); + + info_.childrenCount_ = wrapper_->GetTotalChildCount(); + + Init(idealSize); + mainSize_ = GetMainAxisSize(idealSize, axis_); + + // independent reset / jump procedure + + // independent offset (scrolling) procedure + bool forward = info_.currentOffset_ - info_.prevOffset_ <= 0.0f; + if (forward) { + Fill(); + } + + info_.startIndex_ = info_.FastSolveStartIndex(); + info_.endIndex_ = info_.FastSolveEndIndex(mainSize_); + + info_.itemStart_ = GreatOrEqual(info_.currentOffset_, 0.0f); + info_.itemEnd_ = info_.endIndex_ == info_.childrenCount_ - 1; + info_.offsetEnd_ = info_.itemEnd_ && mainSize_ - info_.currentOffset_ >= info_.endPosArray_.back().first; + + if (matchChildren) { + PostMeasureSelf(idealSize); + } + info_.lastMainSize_ = mainSize_; + + wrapper_->SetCacheCount(props->GetCachedCountValue(1)); +} + +void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper) +{ + wrapper_ = wrapper; + + wrapper_->RemoveAllChildInRenderTree(); + + auto size = wrapper_->GetGeometryNode()->GetFrameSize(); + auto padding = wrapper_->GetLayoutProperty()->CreatePaddingAndBorder(); + MinusPaddingToSize(padding, size); + auto initialOffset = OffsetF(padding.left.value_or(0.0f), padding.top.value_or(0.0f)); + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + info_.UpdateStartIndex(); + int32_t firstSegment = info_.GetSegment(info_.startIndex_); + int32_t lastSegment = info_.GetSegment(info_.endIndex_); + for (int32_t seg = firstSegment; seg <= lastSegment; ++seg) { + LayoutSegment(seg, initialOffset, props->IsReverse()); + } +} + +void WaterFlowSegmentedLayout::Init(const SizeF& frameSize) +{ + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + itemsCrossSize_.clear(); + auto rowsTemplate = props->GetRowsTemplate().value_or("1fr"); + auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr"); + axis_ = props->GetAxis(); + auto scale = props->GetLayoutConstraint()->scaleProperty; + auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0); + auto columnsGap = + ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0); + mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap; + crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap; + + // parse crossSize for different segments + +// auto crossSize = frameSize.CrossSize(axis_); +// std::vector crossLens; +// std::pair, bool> cross; +// if (axis_ == Axis::VERTICAL) { +// cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount); +// } else { +// cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount); +// } +// crossLens = cross.first; +// if (crossLens.empty()) { +// crossLens.push_back(crossSize); +// } +// if (cross.second) { +// crossGap_ = 0; +// } + +// // cross count changed by auto-fill and cross size change +// if (!info_.items_[0].empty() && crossLens.size() != info_.items_[0].size()) { +// info_.Reset(); +// } + +// int32_t index = 0; +// // for (const auto& len : crossLens) { +// // itemsCrossSize_.try_emplace(index, len); +// // itemsCrossPosition_.try_emplace(index, ComputeCrossPosition(index)); +// // info_.items_[0].try_emplace(index, std::map>()); +// // ++index; +// // } +} + +void WaterFlowSegmentedLayout::Fill() +{ + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + + int32_t idx = ++info_.endIndex_; + int32_t segment = info_.GetSegment(idx); + auto position = WaterFlowLayoutUtils::GetItemPosition(info_, idx, mainGap_); + while (LessNotEqual(position.startMainPos + info_.currentOffset_, mainSize_)) { + auto itemWrapper = wrapper_->GetOrCreateChildByIndex(idx); + if (!itemWrapper) { + break; + } + itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint( + { itemsCrossSize_[segment][position.crossIndex], mainSize_, axis_ }, props, itemWrapper)); + + auto itemHeight = GetMainAxisSize(itemWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); + info_.items_[segment][position.crossIndex][idx] = { position.startMainPos, itemHeight }; + info_.AddItemToCache(idx, position.startMainPos, itemHeight); + + // prepare next item + segment = info_.GetSegment(++idx); + position = WaterFlowLayoutUtils::GetItemPosition(info_, idx, mainGap_); + } +} + +std::pair WaterFlowSegmentedLayout::PreMeasureSelf() +{ + auto props = wrapper_->GetLayoutProperty(); + auto size = CreateIdealSize(props->GetLayoutConstraint().value(), axis_, props->GetMeasureType(), true); + auto matchChildren = GreaterOrEqualToInfinity(GetMainAxisSize(size, axis_)); + if (!matchChildren) { + wrapper_->GetGeometryNode()->SetFrameSize(size); + } + MinusPaddingToSize(props->CreatePaddingAndBorder(), size); + wrapper_->GetGeometryNode()->SetContentSize(size); + return { size, matchChildren }; +} + +void WaterFlowSegmentedLayout::PostMeasureSelf(SizeF size) +{ + mainSize_ = info_.GetMaxMainHeight(); + size.SetMainSize(mainSize_, axis_); + auto props = wrapper_->GetLayoutProperty(); + AddPaddingToSize(props->CreatePaddingAndBorder(), size); + wrapper_->GetGeometryNode()->SetFrameSize(size); +} + +void WaterFlowSegmentedLayout::LayoutSegment(int32_t segment, const OffsetF& padding, bool isReverse) +{ + for (const auto& mainPositions : info_.items_[segment]) { + for (const auto& item : mainPositions.second) { + if (item.first < info_.startIndex_ || item.first > info_.endIndex_) { + continue; + } + + auto crossOffset = itemsCrossPosition_[segment][mainPositions.first]; + auto mainOffset = item.second.first + info_.currentOffset_; + if (isReverse) { + mainOffset = mainSize_ - item.second.second - mainOffset; + } + + OffsetF offset = + (axis_ == Axis::VERTICAL) ? OffsetF(crossOffset, mainOffset) : OffsetF(mainOffset, crossOffset); + auto wrapper = wrapper_->GetOrCreateChildByIndex(item.first); + wrapper->GetGeometryNode()->SetMarginFrameOffset(offset + padding); + wrapper->Layout(); + + // recode restore info + if (item.first == info_.startIndex_) { + info_.storedOffset_ = mainOffset; + } + } + } +} +} // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h new file mode 100644 index 00000000000..ee05bd6d320 --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SEGMENTED_LAYOUT_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SEGMENTED_LAYOUT_H + +#include "core/components_ng/layout/layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" + +namespace OHOS::Ace::NG { +class ACE_EXPORT WaterFlowSegmentedLayout : public LayoutAlgorithm { + DECLARE_ACE_TYPE(WaterFlowSegmentedLayout, LayoutAlgorithm); + +public: + explicit WaterFlowSegmentedLayout(WaterFlowLayoutInfo layoutInfo) : info_(std::move(layoutInfo)) {} + ~WaterFlowSegmentedLayout() override = default; + + void Measure(LayoutWrapper* layoutWrapper) override; + + void Layout(LayoutWrapper* layoutWrapper) override; + + WaterFlowLayoutInfo GetLayoutInfo() + { + return std::move(info_); + } + +private: + void Init(const SizeF& frameSize); + + /** + * @brief Measure self before measuring children. + * + * @return [idealSize given by parent, whether measure is successful (need to adapt to children size if not)]. + */ + std::pair PreMeasureSelf(); + + /** + * @brief Measure self after measuring children. Only when pre-measure failed. + * + * @param size ideal content size from parent. + */ + void PostMeasureSelf(SizeF size); + + void SolveLayoutRange(); + + /** + * @brief Fills the viewport with new items when scrolling downwards. + * WaterFlow only supports forward layout because the position of a new item always depends on previous items. + * + */ + void Fill(); + + void LayoutSegment(int32_t segment, const OffsetF& padding, bool isReverse); + + LayoutWrapper* wrapper_ {}; + + std::vector> itemsCrossSize_; + std::vector> itemsCrossPosition_; + Axis axis_ = Axis::VERTICAL; + + float mainGap_ = 0.0f; + float crossGap_ = 0.0f; + float mainSize_ = 0.0f; + WaterFlowLayoutInfo info_; +}; +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SEGMENTED_LAYOUT_H diff --git a/test/mock/core/pipeline/mock_pipeline_context.cpp b/test/mock/core/pipeline/mock_pipeline_context.cpp index f8f6931b150..3e2c850b0c1 100644 --- a/test/mock/core/pipeline/mock_pipeline_context.cpp +++ b/test/mock/core/pipeline/mock_pipeline_context.cpp @@ -347,7 +347,6 @@ void PipelineContext::AddSyncGeometryNodeTask(std::function&& task) task(); } } - void PipelineContext::FlushSyncGeometryNodeTasks() {} void PipelineContext::AddAfterRenderTask(std::function&& task) {} diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index 42f2401622c..8d55968332a 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -985,9 +985,11 @@ ohos_source_set("ace_components_pattern") { "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.cpp", + "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_paint_method.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp", + "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp", "$ace_root/frameworks/core/components_ng/pattern/xcomponent/xcomponent_controller_ng.cpp", "$ace_root/frameworks/core/components_ng/pattern/xcomponent/xcomponent_ext_surface_callback_client.cpp", "$ace_root/frameworks/core/components_ng/pattern/xcomponent/xcomponent_layout_algorithm.cpp", diff --git a/test/unittest/core/pattern/waterflow/BUILD.gn b/test/unittest/core/pattern/waterflow/BUILD.gn index 48bb1053325..35977b77a7a 100644 --- a/test/unittest/core/pattern/waterflow/BUILD.gn +++ b/test/unittest/core/pattern/waterflow/BUILD.gn @@ -15,5 +15,8 @@ import("//foundation/arkui/ace_engine/test/unittest/ace_unittest.gni") ace_unittest("water_flow_test_ng") { type = "new" - sources = [ "water_flow_test_ng.cpp" ] + sources = [ + "water_flow_layout_info_test.cpp", + "water_flow_test_ng.cpp", + ] } diff --git a/test/unittest/core/pattern/waterflow/water_flow_item_maps.h b/test/unittest/core/pattern/waterflow/water_flow_item_maps.h new file mode 100644 index 00000000000..be85a8f6314 --- /dev/null +++ b/test/unittest/core/pattern/waterflow/water_flow_item_maps.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" + +namespace OHOS::Ace::NG { +decltype(WaterFlowLayoutInfo::items_) ITEM_MAP_1 = { + { + // segment 0 + { 0, { { 0, { 0.0f, 50.0f } }, { 4, { 55.0f, 20.0f } } } }, // Column 0 + { 1, { { 1, { 0.0f, 30.0f } } } }, // Column 1 + { 2, { { 2, { 0.0f, 40.0f } } } }, // Column 2 + { 3, { { 3, { 0.0f, 60.0f } } } }, // Column 3 + }, + // segment 1 + {}, + // segment 2 + { + { 0, { { 5, { 80.0f, 50.0f } } } }, // Column 0 + { 1, { { 6, { 80.0f, 30.0f } }, { 8, { 111.0f, 2.0f } }, { 9, { 114.0f, 20.0f } } } }, // Column 1 + { 2, { { 7, { 80.0f, 40.0f } } } }, // Column 2 + } +}; + +std::map ITEM_INFO_1 = { + { 0, { 0.0f, 50.0f } }, + { 1, { 0.0f, 30.0f } }, + { 2, { 0.0f, 40.0f } }, + { 3, { 0.0f, 60.0f } }, + { 4, { 55.0f, 20.0f } }, + { 5, { 80.0f, 50.0f } }, + { 6, { 80.0f, 30.0f } }, + { 7, { 80.0f, 40.0f } }, + { 8, { 111.0f, 2.0f } }, + { 9, { 114.0f, 20.0f } }, +}; + +std::vector> REVERSE_POS_MAP_1 = { + { 50.0f, 0 }, + { 60.0f, 3 }, + { 75.0f, 4 }, + { 130.0f, 5 }, +}; +} // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_layout_info_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_layout_info_test.cpp new file mode 100644 index 00000000000..0ccf2fa7e73 --- /dev/null +++ b/test/unittest/core/pattern/waterflow/water_flow_layout_info_test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#define protected public +#define private public +#include "test/unittest/core/pattern/test_ng.h" +#include "test/unittest/core/pattern/waterflow/water_flow_item_maps.h" + +#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" +#undef private +#undef protected + +using namespace testing; +using namespace testing::ext; +namespace OHOS::Ace::NG { +class WaterFlowLayoutInfoTest : public TestNG {}; + +/** + * @tc.name: GetCrossIndexForNextItem001 + * @tc.desc: Test functions in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowLayoutInfoTest, GetCrossIndexForNextItem001, TestSize.Level1) +{ + WaterFlowLayoutInfo info; + info.items_ = ITEM_MAP_1; + + auto res = info.GetCrossIndexForNextItem(0); + EXPECT_EQ(res.crossIndex, 1); + EXPECT_EQ(res.lastItemIndex, 1); + + res = info.GetCrossIndexForNextItem(1); + EXPECT_EQ(res.crossIndex, 0); + EXPECT_EQ(res.lastItemIndex, -1); + + res = info.GetCrossIndexForNextItem(2); + EXPECT_EQ(res.crossIndex, 1); + EXPECT_EQ(res.lastItemIndex, 5); +} +} // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 63837b63ff2..2ede3833393 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -1180,12 +1180,12 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest001, TestSize.Level1) }); /** - * @tc.steps: Test IsAllCrossReachend function + * @tc.steps: Test IsAllCrossReachEnd function * @tc.expected: step1. Check whether the return value is correct. */ - auto reached = pattern_->layoutInfo_.IsAllCrossReachend(ITEM_HEIGHT); + auto reached = pattern_->layoutInfo_.IsAllCrossReachEnd(ITEM_HEIGHT); EXPECT_TRUE(reached); - reached = pattern_->layoutInfo_.IsAllCrossReachend(WATERFLOW_HEIGHT); + reached = pattern_->layoutInfo_.IsAllCrossReachEnd(WATERFLOW_HEIGHT); EXPECT_TRUE(reached); /** @@ -1410,8 +1410,8 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest002, TestSize.Level1) * @tc.steps: Test GetStartMainPos and GetMainHeight * @tc.expected: step2. Check whether the return value is correct. */ - int32_t crossIndex = pattern_->layoutInfo_.waterFlowItems_.rbegin()->first; - int32_t itemIndex = pattern_->layoutInfo_.waterFlowItems_.rbegin()->second.rbegin()->first; + int32_t crossIndex = pattern_->layoutInfo_.items_[0].rbegin()->first; + int32_t itemIndex = pattern_->layoutInfo_.items_[0].rbegin()->second.rbegin()->first; EXPECT_EQ(pattern_->layoutInfo_.GetStartMainPos(crossIndex + 1, itemIndex), 0.0f); EXPECT_EQ(pattern_->layoutInfo_.GetMainHeight(crossIndex + 1, itemIndex), 0.0f); @@ -1435,20 +1435,20 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest003, TestSize.Level1) * @tc.steps: Test GetMainCount function * @tc.expected: step2. Check whether the size is correct. */ - std::size_t waterFlowItemsSize = pattern_->layoutInfo_.waterFlowItems_.size(); + std::size_t waterFlowItemsSize = pattern_->layoutInfo_.items_[0].size(); int32_t mainCount = pattern_->layoutInfo_.GetMainCount(); - int32_t index = pattern_->layoutInfo_.waterFlowItems_.rbegin()->first; - pattern_->layoutInfo_.waterFlowItems_[index + 1] = std::map>(); - EXPECT_EQ(pattern_->layoutInfo_.waterFlowItems_.size(), waterFlowItemsSize + 1); + int32_t index = pattern_->layoutInfo_.items_[0].rbegin()->first; + pattern_->layoutInfo_.items_[0][index + 1] = std::map>(); + EXPECT_EQ(pattern_->layoutInfo_.items_[0].size(), waterFlowItemsSize + 1); EXPECT_EQ(pattern_->layoutInfo_.GetMainCount(), mainCount); - auto lastItem = pattern_->layoutInfo_.waterFlowItems_.begin()->second.rbegin(); + auto lastItem = pattern_->layoutInfo_.items_[0].begin()->second.rbegin(); float mainSize = lastItem->second.first + lastItem->second.second - 1.0f; - EXPECT_FALSE(pattern_->layoutInfo_.IsAllCrossReachend(mainSize)); + EXPECT_FALSE(pattern_->layoutInfo_.IsAllCrossReachEnd(mainSize)); pattern_->layoutInfo_.ClearCacheAfterIndex(index + 1); - EXPECT_EQ(pattern_->layoutInfo_.waterFlowItems_.size(), waterFlowItemsSize + 1); + EXPECT_EQ(pattern_->layoutInfo_.items_[0].size(), waterFlowItemsSize + 1); } /** @@ -1492,16 +1492,16 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest005, TestSize.Level1) * @tc.expected: step2. Check whether the return value is correct. */ float maxMainHeight = pattern_->layoutInfo_.GetMaxMainHeight(); - int32_t crossIndex = pattern_->layoutInfo_.waterFlowItems_.rbegin()->first; - pattern_->layoutInfo_.waterFlowItems_[crossIndex + 1][0] = std::pair(1.0f, maxMainHeight); + int32_t crossIndex = pattern_->layoutInfo_.items_[0].rbegin()->first; + pattern_->layoutInfo_.items_[0][crossIndex + 1][0] = std::pair(1.0f, maxMainHeight); EXPECT_EQ(pattern_->layoutInfo_.GetMaxMainHeight(), maxMainHeight + 1.0f); /** * @tc.steps: Test GetCrossIndexForNextItem function * @tc.expected: step3. Check whether the return value is correct. */ - pattern_->layoutInfo_.waterFlowItems_[crossIndex + 1][1] = std::pair(0.0f, 0.0f); - FlowItemIndex position = pattern_->layoutInfo_.GetCrossIndexForNextItem(); + pattern_->layoutInfo_.items_[0][crossIndex + 1][1] = std::pair(0.0f, 0.0f); + FlowItemIndex position = pattern_->layoutInfo_.GetCrossIndexForNextItem(0); EXPECT_EQ(position.crossIndex, crossIndex + 1); EXPECT_EQ(position.lastItemIndex, 1); } -- Gitee From 40e4f06f46dfbd8ebbca5cba0bad40514a308bb6 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Sun, 18 Feb 2024 17:16:52 +0800 Subject: [PATCH 2/8] add test Signed-off-by: Tianer Zhou Change-Id: I39c95f259f824bf1add60b30fa0848356d9a7cc7 --- test/unittest/core/pattern/waterflow/BUILD.gn | 1 + .../pattern/waterflow/water_flow_item_maps.h | 34 ++-- .../waterflow/water_flow_layout_info_test.cpp | 159 +++++++++++++++++- .../water_flow_segment_layout_test.cpp | 71 ++++++++ .../pattern/waterflow/water_flow_test_ng.cpp | 37 ++-- .../pattern/waterflow/water_flow_test_ng.h | 66 ++++++++ 6 files changed, 322 insertions(+), 46 deletions(-) create mode 100644 test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp create mode 100644 test/unittest/core/pattern/waterflow/water_flow_test_ng.h diff --git a/test/unittest/core/pattern/waterflow/BUILD.gn b/test/unittest/core/pattern/waterflow/BUILD.gn index 35977b77a7a..329cef9e78c 100644 --- a/test/unittest/core/pattern/waterflow/BUILD.gn +++ b/test/unittest/core/pattern/waterflow/BUILD.gn @@ -17,6 +17,7 @@ ace_unittest("water_flow_test_ng") { type = "new" sources = [ "water_flow_layout_info_test.cpp", + "water_flow_segment_layout_test.cpp", "water_flow_test_ng.cpp", ] } diff --git a/test/unittest/core/pattern/waterflow/water_flow_item_maps.h b/test/unittest/core/pattern/waterflow/water_flow_item_maps.h index be85a8f6314..1d639287fc0 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_item_maps.h +++ b/test/unittest/core/pattern/waterflow/water_flow_item_maps.h @@ -16,11 +16,11 @@ #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" namespace OHOS::Ace::NG { -decltype(WaterFlowLayoutInfo::items_) ITEM_MAP_1 = { +const decltype(WaterFlowLayoutInfo::items_) ITEM_MAP_1 = { { // segment 0 - { 0, { { 0, { 0.0f, 50.0f } }, { 4, { 55.0f, 20.0f } } } }, // Column 0 - { 1, { { 1, { 0.0f, 30.0f } } } }, // Column 1 + { 0, { { 0, { 0.0f, 50.0f } } } }, // Column 0 + { 1, { { 1, { 0.0f, 30.0f } }, { 4, { 35.0f, 20.0f } } } }, // Column 1 { 2, { { 2, { 0.0f, 40.0f } } } }, // Column 2 { 3, { { 3, { 0.0f, 60.0f } } } }, // Column 3 }, @@ -28,29 +28,31 @@ decltype(WaterFlowLayoutInfo::items_) ITEM_MAP_1 = { {}, // segment 2 { - { 0, { { 5, { 80.0f, 50.0f } } } }, // Column 0 - { 1, { { 6, { 80.0f, 30.0f } }, { 8, { 111.0f, 2.0f } }, { 9, { 114.0f, 20.0f } } } }, // Column 1 - { 2, { { 7, { 80.0f, 40.0f } } } }, // Column 2 + { 0, { { 5, { 65.0f, 50.0f } } } }, // Column 0 + { 1, { { 6, { 65.0f, 30.0f } }, { 8, { 96.0f, 2.0f } }, { 9, { 99.0f, 20.0f } } } }, // Column 1 + { 2, { { 7, { 65.0f, 40.0f } } } }, // Column 2 } }; -std::map ITEM_INFO_1 = { +const std::map ITEM_INFO_1 = { { 0, { 0.0f, 50.0f } }, { 1, { 0.0f, 30.0f } }, { 2, { 0.0f, 40.0f } }, { 3, { 0.0f, 60.0f } }, - { 4, { 55.0f, 20.0f } }, - { 5, { 80.0f, 50.0f } }, - { 6, { 80.0f, 30.0f } }, - { 7, { 80.0f, 40.0f } }, - { 8, { 111.0f, 2.0f } }, - { 9, { 114.0f, 20.0f } }, + { 4, { 35.0f, 20.0f } }, + { 5, { 65.0f, 50.0f } }, + { 6, { 65.0f, 30.0f } }, + { 7, { 65.0f, 40.0f } }, + { 8, { 96.0f, 2.0f } }, + { 9, { 99.0f, 20.0f } }, }; -std::vector> REVERSE_POS_MAP_1 = { +const std::vector> END_POS_ARRAY_1 = { { 50.0f, 0 }, { 60.0f, 3 }, - { 75.0f, 4 }, - { 130.0f, 5 }, + { 115.0f, 5 }, + { 119.0f, 9 }, }; + +const std::vector SEGMENT_TAILS_1 = { 4, 4, 9 }; } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_layout_info_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_layout_info_test.cpp index 0ccf2fa7e73..39ad713c6fb 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_layout_info_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_layout_info_test.cpp @@ -15,6 +15,8 @@ #include "gtest/gtest.h" +#include "core/components_ng/property/measure_property.h" + #define protected public #define private public #include "test/unittest/core/pattern/test_ng.h" @@ -40,15 +42,164 @@ HWTEST_F(WaterFlowLayoutInfoTest, GetCrossIndexForNextItem001, TestSize.Level1) info.items_ = ITEM_MAP_1; auto res = info.GetCrossIndexForNextItem(0); - EXPECT_EQ(res.crossIndex, 1); - EXPECT_EQ(res.lastItemIndex, 1); + EXPECT_EQ(res.crossIndex, 2); + EXPECT_EQ(res.lastItemIndex, 2); res = info.GetCrossIndexForNextItem(1); EXPECT_EQ(res.crossIndex, 0); EXPECT_EQ(res.lastItemIndex, -1); res = info.GetCrossIndexForNextItem(2); - EXPECT_EQ(res.crossIndex, 1); - EXPECT_EQ(res.lastItemIndex, 5); + EXPECT_EQ(res.crossIndex, 2); + EXPECT_EQ(res.lastItemIndex, 7); +} + +/** + * @tc.name: FastSolveStartIndex001 + * @tc.desc: Test FastSolveStartIndex in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowLayoutInfoTest, FastSolveStartIndex001, TestSize.Level1) +{ + WaterFlowLayoutInfo info; + EXPECT_EQ(info.FastSolveStartIndex(), 0); + + info.items_ = ITEM_MAP_1; + info.endPosArray_ = END_POS_ARRAY_1; + info.itemInfos_ = ITEM_INFO_1; + + info.currentOffset_ = -40.0f; + EXPECT_EQ(info.FastSolveStartIndex(), 0); + + info.currentOffset_ = -90.0f; + EXPECT_EQ(info.FastSolveStartIndex(), 5); + + info.currentOffset_ = -55.0f; + EXPECT_EQ(info.FastSolveStartIndex(), 3); + + info.currentOffset_ = -20.0f; + EXPECT_EQ(info.FastSolveStartIndex(), 0); + + info.currentOffset_ = -115.0f; + EXPECT_EQ(info.FastSolveStartIndex(), 9); +} + +/** + * @tc.name: FastSolveEndIndex001 + * @tc.desc: Test FastSolveEndIndex in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowLayoutInfoTest, FastSolveEndIndex001, TestSize.Level1) +{ + WaterFlowLayoutInfo info; + EXPECT_EQ(info.FastSolveEndIndex(50.0f), -1); + + info.items_ = ITEM_MAP_1; + info.endPosArray_ = END_POS_ARRAY_1; + info.itemInfos_ = ITEM_INFO_1; + info.childrenCount_ = 10; + + info.currentOffset_ = -40.0f; + EXPECT_EQ(info.FastSolveEndIndex(50.0f), 7); + EXPECT_EQ(info.FastSolveEndIndex(10.0f), 4); + + info.currentOffset_ = -90.0f; + EXPECT_EQ(info.FastSolveEndIndex(50.0f), 9); + + info.currentOffset_ = -55.0f; + EXPECT_EQ(info.FastSolveEndIndex(10.0f), 4); + EXPECT_EQ(info.FastSolveEndIndex(55.0f), 9); + + info.currentOffset_ = 0.0f; + EXPECT_EQ(info.FastSolveEndIndex(35.0f), 3); +} + +/** + * @tc.name: GetSegment001 + * @tc.desc: Test GetSegment in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowLayoutInfoTest, GetSegment001, TestSize.Level1) +{ + WaterFlowLayoutInfo info; + info.childrenCount_ = 20; + EXPECT_EQ(info.GetSegment(5), 0); + EXPECT_EQ(info.GetSegment(0), 0); + EXPECT_EQ(info.GetSegment(10), 0); + + info.segmentTails_ = { 5, 13, 18, 19 }; + for (int i = 0; i <= 2; ++i) { + if (i == 2) { + EXPECT_EQ(info.segmentCache_.size(), 6); + EXPECT_EQ(info.segmentCache_.at(15), 2); + info.segmentCache_.clear(); + } + // test cache on the second iteration + EXPECT_EQ(info.GetSegment(2), 0); + EXPECT_EQ(info.GetSegment(3), 0); + EXPECT_EQ(info.GetSegment(8), 1); + EXPECT_EQ(info.GetSegment(15), 2); + EXPECT_EQ(info.GetSegment(18), 2); + EXPECT_EQ(info.GetSegment(19), 3); + } +} + +/** + * @tc.name: GetSegment002 + * @tc.desc: Test GetSegment in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowLayoutInfoTest, GetSegment002, TestSize.Level1) +{ + WaterFlowLayoutInfo info; + + info.segmentTails_ = SEGMENT_TAILS_1; + EXPECT_EQ(info.GetSegment(0), 0); + EXPECT_EQ(info.GetSegment(4), 0); + EXPECT_EQ(info.GetSegment(5), 2); + EXPECT_EQ(info.GetSegment(9), 2); + EXPECT_EQ(info.GetSegment(15), 2); +} + +/** + * @tc.name: SetNextSegmentStartPos001 + * @tc.desc: Test SetNextSegmentStartPos in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowLayoutInfoTest, SetNextSegmentStartPos001, TestSize.Level1) +{ + WaterFlowLayoutInfo info; + + info.segmentTails_ = { 3, 5, 5, 10 }; + info.segmentStartPos_ = { 5.0f }; + + std::vector margins = { PaddingPropertyF { .top = 5.0f, .bottom = 1.0f }, + PaddingPropertyF { .top = 10.0f, .bottom = 5.0f }, PaddingPropertyF { .top = 1.0f, .bottom = 5.0f }, + PaddingPropertyF { .bottom = 5.0f } }; + + info.endPosArray_ = { { 100.0f, 0 } }; + info.SetNextSegmentStartPos(margins, 2); + const std::vector CMP_0 = { 5.0f }; + EXPECT_EQ(info.segmentStartPos_, CMP_0); + + info.endPosArray_ = { { 100.0f, 0 }, { 120.0f, 3 } }; + const std::vector CMP_1 = { 5.0f, 131.0f }; + for (int i = 0; i <= 1; ++i) { + info.SetNextSegmentStartPos(margins, 3); + EXPECT_EQ(info.segmentStartPos_, CMP_1); + } + + info.endPosArray_ = { { 100.0f, 0 }, { 120.0f, 3 }, { 150.0f, 4 } }; + const std::vector CMP_2 = { 5.0f, 131.0f, 156.0f, 161.0f }; + for (int i = 0; i <= 1; ++i) { + info.SetNextSegmentStartPos(margins, 5); + EXPECT_EQ(info.segmentStartPos_, CMP_2); + } + + info.SetNextSegmentStartPos(margins, 6); + EXPECT_EQ(info.segmentStartPos_, CMP_2); + + info.SetNextSegmentStartPos(margins, 10); + EXPECT_EQ(info.segmentStartPos_, CMP_2); } } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp new file mode 100644 index 00000000000..8cf997a13ed --- /dev/null +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "test/unittest/core/pattern/waterflow/water_flow_item_maps.h" +#include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" + +#include "core/components_ng/property/measure_property.h" + +#define protected public +#define private public +#include "frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" +#undef private +#undef protected + +namespace OHOS::Ace::NG { +class WaterFlowSegmentTest : public WaterFlowTestNg {}; + +/** + * @tc.name: Fill001 + * @tc.desc: Test SegmentedLayout::Fill. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, Fill001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + CreateItemWithHeight(50.0f); + CreateItemWithHeight(30.0f); + CreateItemWithHeight(40.0f); + CreateItemWithHeight(60.0f); + CreateItemWithHeight(20.0f); + CreateItemWithHeight(50.0f); + CreateItemWithHeight(30.0f); + CreateItemWithHeight(40.0f); + CreateItemWithHeight(2.0f); + CreateItemWithHeight(20.0f); + }); + + auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + algo->wrapper_ = AceType::RawPtr(frameNode_); + algo->mainSize_ = 2000.0f; + algo->itemsCrossSize_ = { { 50.0f, 50.0f, 50.0f, 50.0f }, {}, { 70.0f, 70.0f, 70.0f } }; + algo->mainGaps_ = { 5.0f, 0.0f, 1.0f }; + algo->margins_ = { {}, {}, PaddingPropertyF { .top = 5.0f } }; + + auto& info = algo->info_; + + info.items_.resize(3); + for (int i = 0; i < 3; ++i) { + info.items_[0][i] = {}; + info.items_[2][i] = {}; + } + info.items_[0][3] = {}; + + info.segmentTails_ = SEGMENT_TAILS_1; + + algo->Fill(); + EXPECT_EQ(info.items_, ITEM_MAP_1); +} +} // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 2ede3833393..8e4b7740f13 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -30,6 +30,7 @@ #include "test/mock/core/pipeline/mock_pipeline_context.h" #include "test/mock/core/render/mock_render_context.h" #include "test/unittest/core/pattern/test_ng.h" +#include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" #include "base/geometry/dimension.h" #include "base/geometry/ng/size_t.h" @@ -72,31 +73,6 @@ constexpr float ITEM_HEIGHT = WATERFLOW_HEIGHT / VIEW_LINE_NUMBER; constexpr float BIG_ITEM_HEIGHT = ITEM_HEIGHT * 2; } // namespace -class WaterFlowTestNg : public TestNG { -protected: - static void SetUpTestSuite(); - static void TearDownTestSuite(); - void SetUp() override; - void TearDown() override; - void GetInstance(); - - void Create(const std::function& callback = nullptr); - void CreateWithItem(const std::function& callback = nullptr); - static void CreateItem(int32_t number = 10); - void UpdateCurrentOffset(float offset, int32_t source = SCROLL_FROM_UPDATE); - void MouseSelect(Offset start, Offset end); - void MouseSelectRelease(); - static std::function GetDefaultHeaderBuilder(); - - AssertionResult IsEqualTotalOffset(float expectOffset); - - RefPtr frameNode_; - RefPtr pattern_; - RefPtr eventHub_; - RefPtr layoutProperty_; - RefPtr accessibilityProperty_; -}; - void WaterFlowTestNg::SetUpTestSuite() { TestNG::SetUpTestSuite(); @@ -176,6 +152,15 @@ void WaterFlowTestNg::CreateItem(int32_t number) } } +void WaterFlowTestNg::CreateItemWithHeight(float height) +{ + WaterFlowItemModelNG waterFlowItemModel; + waterFlowItemModel.Create(); + ViewAbstract::SetWidth(CalcLength(FILL_LENGTH)); + ViewAbstract::SetHeight(CalcLength(Dimension(height))); + ViewStackProcessor::GetInstance()->Pop(); +} + void WaterFlowTestNg::UpdateCurrentOffset(float offset, int32_t source) { pattern_->UpdateCurrentOffset(offset, source); @@ -1472,7 +1457,7 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest004, TestSize.Level1) EXPECT_EQ(pattern_->layoutInfo_.endIndex_, resetFrom); pattern_->layoutInfo_.Reset(resetFrom - 1); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_.endIndex_, -1); } /** diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.h b/test/unittest/core/pattern/waterflow/water_flow_test_ng.h new file mode 100644 index 00000000000..1ddefddb80e --- /dev/null +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "gtest/gtest.h" + +#define protected public +#define private public +#include "test/unittest/core/pattern/test_ng.h" + +#include "base/geometry/offset.h" +#include "core/components_ng/pattern/waterflow/water_flow_accessibility_property.h" +#include "core/components_ng/pattern/waterflow/water_flow_event_hub.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" +#include "core/components_ng/pattern/waterflow/water_flow_model_ng.h" +#include "core/components_ng/pattern/waterflow/water_flow_pattern.h" +#undef private +#undef protected + +namespace { +using namespace testing; +using namespace testing::ext; +} // namespace + +namespace OHOS::Ace::NG { +class WaterFlowTestNg : public TestNG { +protected: + static void SetUpTestSuite(); + static void TearDownTestSuite(); + void SetUp() override; + void TearDown() override; + void GetInstance(); + + void Create(const std::function& callback = nullptr); + void CreateWithItem(const std::function& callback = nullptr); + static void CreateItem(int32_t number = 10); + static void CreateItemWithHeight(float height); + void UpdateCurrentOffset(float offset, int32_t source = SCROLL_FROM_UPDATE); + void MouseSelect(Offset start, Offset end); + void MouseSelectRelease(); + static std::function GetDefaultHeaderBuilder(); + + AssertionResult IsEqualTotalOffset(float expectOffset); + + RefPtr frameNode_; + RefPtr pattern_; + RefPtr eventHub_; + RefPtr layoutProperty_; + RefPtr accessibilityProperty_; +}; +} // namespace OHOS::Ace::NG \ No newline at end of file -- Gitee From 5fd80f2e5ee9724e1c4ddc43b7b1e75d79bb9a14 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Sun, 18 Feb 2024 17:17:18 +0800 Subject: [PATCH 3/8] fix segment margin Signed-off-by: Tianer Zhou Change-Id: I9a8cb64b634ee683265a5ba30bea1a1f3b6f067a --- .../waterflow/water_flow_layout_info.cpp | 31 +++++-- .../waterflow/water_flow_layout_info.h | 19 ++++- .../waterflow/water_flow_layout_utils.cpp | 5 +- .../waterflow/water_flow_segmented_layout.cpp | 80 ++++++++++--------- .../waterflow/water_flow_segmented_layout.h | 6 +- 5 files changed, 91 insertions(+), 50 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 7bed488b2d5..0051da936a8 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -98,9 +98,11 @@ float WaterFlowLayoutInfo::GetContentHeight() const float WaterFlowLayoutInfo::GetMainHeight(int32_t crossIndex, int32_t itemIndex) const { - float result = 0.0f; - auto cross = items_[GetSegment(itemIndex)].find(crossIndex); - if (cross == items_[GetSegment(itemIndex)].end()) { + auto seg = GetSegment(itemIndex); + float result = segmentStartPos_[seg]; + + auto cross = items_[seg].find(crossIndex); + if (cross == items_[seg].end()) { return result; } auto item = cross->second.find(itemIndex); @@ -187,7 +189,7 @@ void WaterFlowLayoutInfo::Reset() jumpIndex_ = EMPTY_JUMP_INDEX; startIndex_ = 0; - endIndex_ = 0; + endIndex_ = -1; targetIndex_.reset(); for (auto& map : items_) { map.clear(); @@ -267,7 +269,7 @@ int32_t WaterFlowLayoutInfo::GetSegment(int32_t itemIdx) const auto it = std::lower_bound(segmentTails_.begin(), segmentTails_.end(), itemIdx); if (it == segmentTails_.end()) { - return *segmentTails_.rbegin(); + return segmentTails_.size() - 1; } int32_t idx = it - segmentTails_.begin(); segmentCache_[itemIdx] = idx; @@ -299,7 +301,24 @@ void WaterFlowLayoutInfo::AddItemToCache(int32_t idx, float startPos, float heig { itemInfos_[idx] = { startPos, height }; if (endPosArray_.empty() || LessNotEqual(endPosArray_.back().first, startPos + height)) { - endPosArray_.emplace_back( startPos + height, idx ); + endPosArray_.emplace_back(startPos + height, idx); + } +} + +void WaterFlowLayoutInfo::SetNextSegmentStartPos( + const std::vector& margins, int32_t itemIdx) +{ + int32_t segment = GetSegment(itemIdx); + if (segmentStartPos_.size() > segment + 1) { + return; + } + + float nextStartPos = endPosArray_.back().first; + while (segment < segmentTails_.size() - 1 && itemIdx == segmentTails_[segment]) { + // use while loop to skip empty segments + nextStartPos += margins[segment].bottom.value_or(0.0f) + margins[segment + 1].top.value_or(0.0f); + segmentStartPos_.push_back(nextStartPos); + ++segment; } } } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index 94c5bad07a6..442077206ea 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -23,6 +23,7 @@ #include "base/utils/utils.h" #include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/property/measure_property.h" namespace OHOS::Ace::NG { struct FlowItemIndex { @@ -84,6 +85,14 @@ public: */ int32_t FastSolveEndIndex(float mainSize) const; + /** + * @brief Calculate and set the start position of next segment after filling the tail item of the current segment. + * + * @param margins margin of each segment. + * @param itemIdx index of the current flow item. + */ + void SetNextSegmentStartPos(const std::vector& margins, int32_t itemIdx); + float currentOffset_ = 0.0f; float prevOffset_ = 0.0f; float lastMainSize_ = 0.0f; @@ -94,7 +103,7 @@ public: float restoreOffset_ = 0.0f; bool itemStart_ = false; - bool itemEnd_ = false; // last item is partially in viewport + bool itemEnd_ = false; // last item is partially in viewport bool offsetEnd_ = false; // last item's bottom is in viewport int32_t jumpIndex_ = EMPTY_JUMP_INDEX; @@ -102,7 +111,7 @@ public: ScrollAlign align_ = ScrollAlign::START; int32_t startIndex_ = 0; - int32_t endIndex_ = 0; + int32_t endIndex_ = -1; int32_t footerIndex_ = -1; int32_t childrenCount_ = 0; @@ -116,6 +125,7 @@ public: using ItemMap = std::map>; std::vector items_ { ItemMap() }; + // quick access to item info, ignores crossIndex and segments std::map itemInfos_; @@ -126,7 +136,12 @@ public: */ std::vector> endPosArray_; + // Stores the tail item index of each segment. std::vector segmentTails_; + + // Stores the start position of each segment. + std::vector segmentStartPos_ = { 0.0f }; + // K: item index; V: corresponding segment index mutable std::unordered_map segmentCache_; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp index f8fd49d864b..330eee1bbba 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp @@ -49,9 +49,10 @@ FlowItemPosition WaterFlowLayoutUtils::GetItemPosition(const WaterFlowLayoutInfo if (crossIndex != -1) { return { crossIndex, info.GetStartMainPos(crossIndex, index) }; } - auto itemIndex = info.GetCrossIndexForNextItem(info.GetSegment(index)); + int32_t segment = info.GetSegment(index); + auto itemIndex = info.GetCrossIndexForNextItem(segment); if (itemIndex.lastItemIndex < 0) { - return { itemIndex.crossIndex, 0.0f }; + return { itemIndex.crossIndex, info.segmentStartPos_[segment] }; } auto mainHeight = info.GetMainHeight(itemIndex.crossIndex, itemIndex.lastItemIndex); return { itemIndex.crossIndex, mainHeight + mainGap }; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index 9b66a085b6b..4e651591a02 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -37,7 +37,7 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) // independent reset / jump procedure // independent offset (scrolling) procedure - bool forward = info_.currentOffset_ - info_.prevOffset_ <= 0.0f; + bool forward = LessOrEqual(info_.currentOffset_ - info_.prevOffset_, 0.0f); if (forward) { Fill(); } @@ -83,43 +83,43 @@ void WaterFlowSegmentedLayout::Init(const SizeF& frameSize) auto rowsTemplate = props->GetRowsTemplate().value_or("1fr"); auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr"); axis_ = props->GetAxis(); - auto scale = props->GetLayoutConstraint()->scaleProperty; - auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0); - auto columnsGap = - ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0); - mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap; - crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap; + // auto scale = props->GetLayoutConstraint()->scaleProperty; + // auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0); + // auto columnsGap = + // ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0); + // mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap; + // crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap; // parse crossSize for different segments - -// auto crossSize = frameSize.CrossSize(axis_); -// std::vector crossLens; -// std::pair, bool> cross; -// if (axis_ == Axis::VERTICAL) { -// cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount); -// } else { -// cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount); -// } -// crossLens = cross.first; -// if (crossLens.empty()) { -// crossLens.push_back(crossSize); -// } -// if (cross.second) { -// crossGap_ = 0; -// } - -// // cross count changed by auto-fill and cross size change -// if (!info_.items_[0].empty() && crossLens.size() != info_.items_[0].size()) { -// info_.Reset(); -// } - -// int32_t index = 0; -// // for (const auto& len : crossLens) { -// // itemsCrossSize_.try_emplace(index, len); -// // itemsCrossPosition_.try_emplace(index, ComputeCrossPosition(index)); -// // info_.items_[0].try_emplace(index, std::map>()); -// // ++index; -// // } + + // auto crossSize = frameSize.CrossSize(axis_); + // std::vector crossLens; + // std::pair, bool> cross; + // if (axis_ == Axis::VERTICAL) { + // cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount); + // } else { + // cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount); + // } + // crossLens = cross.first; + // if (crossLens.empty()) { + // crossLens.push_back(crossSize); + // } + // if (cross.second) { + // crossGap_ = 0; + // } + + // // cross count changed by auto-fill and cross size change + // if (!info_.items_[0].empty() && crossLens.size() != info_.items_[0].size()) { + // info_.Reset(); + // } + + // int32_t index = 0; + // // for (const auto& len : crossLens) { + // // itemsCrossSize_.try_emplace(index, len); + // // itemsCrossPosition_.try_emplace(index, ComputeCrossPosition(index)); + // // info_.items_[0].try_emplace(index, std::map>()); + // // ++index; + // // } } void WaterFlowSegmentedLayout::Fill() @@ -128,7 +128,7 @@ void WaterFlowSegmentedLayout::Fill() int32_t idx = ++info_.endIndex_; int32_t segment = info_.GetSegment(idx); - auto position = WaterFlowLayoutUtils::GetItemPosition(info_, idx, mainGap_); + auto position = WaterFlowLayoutUtils::GetItemPosition(info_, idx, mainGaps_[segment]); while (LessNotEqual(position.startMainPos + info_.currentOffset_, mainSize_)) { auto itemWrapper = wrapper_->GetOrCreateChildByIndex(idx); if (!itemWrapper) { @@ -141,9 +141,13 @@ void WaterFlowSegmentedLayout::Fill() info_.items_[segment][position.crossIndex][idx] = { position.startMainPos, itemHeight }; info_.AddItemToCache(idx, position.startMainPos, itemHeight); + if (idx == info_.segmentTails_[segment]) { + info_.SetNextSegmentStartPos(margins_, idx); + } + // prepare next item segment = info_.GetSegment(++idx); - position = WaterFlowLayoutUtils::GetItemPosition(info_, idx, mainGap_); + position = WaterFlowLayoutUtils::GetItemPosition(info_, idx, mainGaps_[segment]); } } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h index ee05bd6d320..0b0263a4cb1 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h @@ -19,6 +19,7 @@ #include "core/components_ng/layout/layout_algorithm.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" +#include "core/components_ng/property/measure_property.h" namespace OHOS::Ace::NG { class ACE_EXPORT WaterFlowSegmentedLayout : public LayoutAlgorithm { @@ -69,10 +70,11 @@ private: std::vector> itemsCrossSize_; std::vector> itemsCrossPosition_; + std::vector margins_; // margin of each segment Axis axis_ = Axis::VERTICAL; - float mainGap_ = 0.0f; - float crossGap_ = 0.0f; + std::vector mainGaps_; + std::vector crossGaps_; float mainSize_ = 0.0f; WaterFlowLayoutInfo info_; }; -- Gitee From b0e071116a682840cdb9615dec0efc8e6ac2525f Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 19 Feb 2024 09:46:03 +0800 Subject: [PATCH 4/8] implement init Signed-off-by: Tianer Zhou Change-Id: I8bc82e3de6bdbf6bcb4b1aebbac36103f3cd8798 --- .../waterflow/water_flow_segmented_layout.cpp | 108 ++++++++++++------ .../waterflow/water_flow_segmented_layout.h | 2 + 2 files changed, 72 insertions(+), 38 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index 4e651591a02..a3a893d47f3 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -80,46 +80,78 @@ void WaterFlowSegmentedLayout::Init(const SizeF& frameSize) { auto props = DynamicCast(wrapper_->GetLayoutProperty()); itemsCrossSize_.clear(); + axis_ = props->GetAxis(); + info_.childrenCount_ = wrapper_->GetTotalChildCount(); + RegularInit(frameSize); + InitFooter(frameSize.CrossSize(axis_)); + + // if (!segment) { + + // } +} + +void WaterFlowSegmentedLayout::RegularInit(const SizeF& frameSize) +{ + auto props = DynamicCast(wrapper_->GetLayoutProperty()); auto rowsTemplate = props->GetRowsTemplate().value_or("1fr"); auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr"); - axis_ = props->GetAxis(); - // auto scale = props->GetLayoutConstraint()->scaleProperty; - // auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0); - // auto columnsGap = - // ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0); - // mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap; - // crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap; - - // parse crossSize for different segments - - // auto crossSize = frameSize.CrossSize(axis_); - // std::vector crossLens; - // std::pair, bool> cross; - // if (axis_ == Axis::VERTICAL) { - // cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount); - // } else { - // cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount); - // } - // crossLens = cross.first; - // if (crossLens.empty()) { - // crossLens.push_back(crossSize); - // } - // if (cross.second) { - // crossGap_ = 0; - // } - - // // cross count changed by auto-fill and cross size change - // if (!info_.items_[0].empty() && crossLens.size() != info_.items_[0].size()) { - // info_.Reset(); - // } - - // int32_t index = 0; - // // for (const auto& len : crossLens) { - // // itemsCrossSize_.try_emplace(index, len); - // // itemsCrossPosition_.try_emplace(index, ComputeCrossPosition(index)); - // // info_.items_[0].try_emplace(index, std::map>()); - // // ++index; - // // } + auto scale = props->GetLayoutConstraint()->scaleProperty; + auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0); + auto columnsGap = ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0); + mainGaps_ = { axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap }; + crossGaps_ = { axis_ == Axis::VERTICAL ? columnsGap : rowsGap }; + + auto crossSize = frameSize.CrossSize(axis_); + std::vector crossLens; + std::pair, bool> cross; + if (axis_ == Axis::VERTICAL) { + cross = ParseTemplateArgs( + WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGaps_[0], info_.childrenCount_); + } else { + cross = ParseTemplateArgs( + WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGaps_[0], info_.childrenCount_); + } + crossLens = cross.first; + if (crossLens.empty()) { + crossLens.push_back(crossSize); + } + if (cross.second) { + crossGaps_ = { 0 }; + } + + // cross count changed by auto-fill and cross size change + if (!info_.items_[0].empty() && crossLens.size() != info_.items_[0].size()) { + info_.Reset(); + } + + itemsCrossPosition_.resize(1); + itemsCrossSize_.resize(1); + margins_.resize(1); + + int32_t index = 0; + float pos = 0.0f; + for (const auto& len : crossLens) { + itemsCrossSize_[0].push_back(len); + itemsCrossPosition_[0].push_back(pos); + pos += len + crossGaps_[0]; + + info_.items_[0].try_emplace(index, std::map>()); + ++index; + } + // childrenCount - 2 if has footer + info_.segmentTails_ = { info_.childrenCount_ - 1 }; +} + +void WaterFlowSegmentedLayout::InitFooter(float crossSize) +{ + crossGaps_.push_back(0.0f); + mainGaps_.push_back(0.0f); + margins_.emplace_back(); + itemsCrossPosition_.push_back({ 0.0f }); + itemsCrossSize_.push_back({ crossSize }); + info_.items_.emplace_back(); + info_.items_.back().try_emplace(0); + info_.segmentTails_.push_back(info_.childrenCount_ - 1); } void WaterFlowSegmentedLayout::Fill() diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h index 0b0263a4cb1..563068575d7 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h @@ -40,6 +40,8 @@ public: private: void Init(const SizeF& frameSize); + void RegularInit(const SizeF& frameSize); + void InitFooter(float width); /** * @brief Measure self before measuring children. -- Gitee From 8be5ed4c6bf1a3f38c798ab099ce22a894096c3b Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 19 Feb 2024 10:59:23 +0800 Subject: [PATCH 5/8] fix footer Signed-off-by: Tianer Zhou Change-Id: I9142fe3ba874df1b9d58e1d6927401c3f7431eaf --- .../waterflow/water_flow_layout_algorithm.h | 13 ++++++++++--- .../pattern/waterflow/water_flow_pattern.cpp | 2 +- .../waterflow/water_flow_segmented_layout.cpp | 18 ++++++++++++++---- .../waterflow/water_flow_segmented_layout.h | 11 +++++++---- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h index 2452d4d0d4c..637a08c9a3f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h @@ -21,8 +21,15 @@ #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" namespace OHOS::Ace::NG { -class ACE_EXPORT WaterFlowLayoutAlgorithm : public LayoutAlgorithm { - DECLARE_ACE_TYPE(WaterFlowLayoutAlgorithm, LayoutAlgorithm); +class WaterFlowLayoutBase : public LayoutAlgorithm { + DECLARE_ACE_TYPE(WaterFlowLayoutBase, LayoutAlgorithm); + +public: + virtual WaterFlowLayoutInfo GetLayoutInfo() = 0; +}; + +class ACE_EXPORT WaterFlowLayoutAlgorithm : public WaterFlowLayoutBase { + DECLARE_ACE_TYPE(WaterFlowLayoutAlgorithm, WaterFlowLayoutBase); public: explicit WaterFlowLayoutAlgorithm(WaterFlowLayoutInfo layoutInfo) : layoutInfo_(std::move(layoutInfo)) {} @@ -32,7 +39,7 @@ public: void Layout(LayoutWrapper* layoutWrapper) override; - WaterFlowLayoutInfo GetLayoutInfo() + WaterFlowLayoutInfo GetLayoutInfo() override { return std::move(layoutInfo_); } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 5ea808d49fa..3515623a80c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -214,7 +214,7 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir } auto layoutAlgorithmWrapper = dirty->GetLayoutAlgorithm(); CHECK_NULL_RETURN(layoutAlgorithmWrapper, false); - auto layoutAlgorithm = DynamicCast(layoutAlgorithmWrapper->GetLayoutAlgorithm()); + auto layoutAlgorithm = DynamicCast(layoutAlgorithmWrapper->GetLayoutAlgorithm()); CHECK_NULL_RETURN(layoutAlgorithm, false); auto layoutInfo = layoutAlgorithm->GetLayoutInfo(); auto host = GetHost(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index a3a893d47f3..a5fabdd8d34 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -20,6 +20,7 @@ #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_utils.h" #include "core/components_ng/property/templates_parser.h" +#include "core/components_ng/base/frame_node.h" namespace OHOS::Ace::NG { void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) @@ -83,8 +84,9 @@ void WaterFlowSegmentedLayout::Init(const SizeF& frameSize) axis_ = props->GetAxis(); info_.childrenCount_ = wrapper_->GetTotalChildCount(); RegularInit(frameSize); - InitFooter(frameSize.CrossSize(axis_)); - + if (info_.footerIndex_ >= 0) { + InitFooter(frameSize.CrossSize(axis_)); + } // if (!segment) { // } @@ -138,12 +140,20 @@ void WaterFlowSegmentedLayout::RegularInit(const SizeF& frameSize) info_.items_[0].try_emplace(index, std::map>()); ++index; } - // childrenCount - 2 if has footer - info_.segmentTails_ = { info_.childrenCount_ - 1 }; + info_.segmentTails_ = { (info_.footerIndex_ >= 0) ? info_.childrenCount_ - 2 : info_.childrenCount_ - 1 }; } void WaterFlowSegmentedLayout::InitFooter(float crossSize) { + if (info_.footerIndex_ != info_.childrenCount_ - 1) { + // re-insert at the end + auto footer = wrapper_->GetOrCreateChildByIndex(info_.footerIndex_); + auto waterFlow = wrapper_->GetHostNode(); + waterFlow->RemoveChildAtIndex(info_.footerIndex_); + footer->GetHostNode()->MountToParent(waterFlow); + info_.footerIndex_ = info_.childrenCount_ - 1; + } + crossGaps_.push_back(0.0f); mainGaps_.push_back(0.0f); margins_.emplace_back(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h index 563068575d7..ae25394ab51 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h @@ -17,13 +17,14 @@ #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SEGMENTED_LAYOUT_H #include "core/components_ng/layout/layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/property/measure_property.h" namespace OHOS::Ace::NG { -class ACE_EXPORT WaterFlowSegmentedLayout : public LayoutAlgorithm { - DECLARE_ACE_TYPE(WaterFlowSegmentedLayout, LayoutAlgorithm); +class WaterFlowSegmentedLayout : public WaterFlowLayoutBase { + DECLARE_ACE_TYPE(WaterFlowSegmentedLayout, WaterFlowLayoutBase); public: explicit WaterFlowSegmentedLayout(WaterFlowLayoutInfo layoutInfo) : info_(std::move(layoutInfo)) {} @@ -33,7 +34,7 @@ public: void Layout(LayoutWrapper* layoutWrapper) override; - WaterFlowLayoutInfo GetLayoutInfo() + WaterFlowLayoutInfo GetLayoutInfo() override { return std::move(info_); } @@ -41,7 +42,7 @@ public: private: void Init(const SizeF& frameSize); void RegularInit(const SizeF& frameSize); - void InitFooter(float width); + void InitFooter(float crossSize); /** * @brief Measure self before measuring children. @@ -79,6 +80,8 @@ private: std::vector crossGaps_; float mainSize_ = 0.0f; WaterFlowLayoutInfo info_; + + ACE_DISALLOW_COPY_AND_MOVE(WaterFlowSegmentedLayout); }; } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SEGMENTED_LAYOUT_H -- Gitee From 0e0a62a162b46b158e3d20f94aae809e587abaef Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 19 Feb 2024 15:41:33 +0800 Subject: [PATCH 6/8] fix upward scrolling Signed-off-by: Tianer Zhou Change-Id: I88c3c2b9c5e34265dab58eb952a8baf1cffefe88 --- .../waterflow/water_flow_layout_algorithm.cpp | 4 +- .../waterflow/water_flow_layout_algorithm.h | 5 +- .../waterflow/water_flow_layout_info.cpp | 25 ++++++++- .../waterflow/water_flow_layout_info.h | 9 ++++ .../pattern/waterflow/water_flow_pattern.cpp | 7 +-- .../waterflow/water_flow_segmented_layout.cpp | 52 ++++++++++++------- .../waterflow/water_flow_segmented_layout.h | 31 ++++++++++- .../pattern/waterflow/water_flow_test_ng.cpp | 2 +- 8 files changed, 103 insertions(+), 32 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp index 99b9785e633..e04f67ae00d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp @@ -317,7 +317,7 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L { auto maxItemHeight = layoutInfo_.GetMaxMainHeight(); if (layoutInfo_.footerIndex_ >= 0) { - footerMainSize_ = MeasuerFooter(layoutWrapper); + footerMainSize_ = MeasureFooter(layoutWrapper); maxItemHeight += footerMainSize_; } layoutInfo_.maxHeight_ = maxItemHeight; @@ -352,7 +352,7 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L } } -float WaterFlowLayoutAlgorithm::MeasuerFooter(LayoutWrapper* layoutWrapper) +float WaterFlowLayoutAlgorithm::MeasureFooter(LayoutWrapper* layoutWrapper) { auto footer = layoutWrapper->GetOrCreateChildByIndex(layoutInfo_.footerIndex_); auto layoutProperty = layoutWrapper->GetLayoutProperty(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h index 637a08c9a3f..982fb9dbf81 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h @@ -26,6 +26,7 @@ class WaterFlowLayoutBase : public LayoutAlgorithm { public: virtual WaterFlowLayoutInfo GetLayoutInfo() = 0; + virtual void SetCanOverScroll(bool canOverScroll) = 0; }; class ACE_EXPORT WaterFlowLayoutAlgorithm : public WaterFlowLayoutBase { @@ -43,7 +44,7 @@ public: { return std::move(layoutInfo_); } - void SetCanOverScroll(bool canOverScroll) + void SetCanOverScroll(bool canOverScroll) override { canOverScroll_ = canOverScroll; } @@ -60,7 +61,7 @@ private: { return index + layoutInfo_.footerIndex_ + 1; } - float MeasuerFooter(LayoutWrapper* layoutWrapper); + float MeasureFooter(LayoutWrapper* layoutWrapper); void LayoutFooter(LayoutWrapper* layoutWrapper, const OffsetF& childFrameOffset, bool reverse); void JumpToTargetAlign(const std::pair& item); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 0051da936a8..51ffa3c0eb8 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -74,6 +74,9 @@ int32_t WaterFlowLayoutInfo::GetEndIndexByOffset(float offset) const float WaterFlowLayoutInfo::GetMaxMainHeight() const { + if (items_.empty()) { + return 0.0f; + } float result = 0.0f; for (const auto& crossItems : *items_.rbegin()) { if (crossItems.second.empty()) { @@ -288,11 +291,15 @@ int32_t WaterFlowLayoutInfo::FastSolveStartIndex() const int32_t WaterFlowLayoutInfo::FastSolveEndIndex(float mainSize) const { + if (itemInfos_.empty()) { + return -1; + } + auto it = std::lower_bound(itemInfos_.begin(), itemInfos_.end(), mainSize - currentOffset_, [](const std::pair& info, float value) { return info.second.first < value; }); if (it == itemInfos_.end()) { - return childrenCount_ - 1; + return itemInfos_.rbegin()->first; } return it->first - 1; } @@ -321,4 +328,20 @@ void WaterFlowLayoutInfo::SetNextSegmentStartPos( ++segment; } } + +void WaterFlowLayoutInfo::Sync(float mainSize, float bottomMargin, bool overScroll) +{ + endIndex_ = FastSolveEndIndex(mainSize); + + maxHeight_ = GetMaxMainHeight() + bottomMargin; + + itemStart_ = GreatOrEqual(currentOffset_, 0.0f); + itemEnd_ = endIndex_ >= 0 && endIndex_ == childrenCount_ - 1; + offsetEnd_ = itemEnd_ && GreatOrEqual(mainSize - currentOffset_, maxHeight_); + if (offsetEnd_ && !overScroll) { + currentOffset_ = -maxHeight_ + mainSize; + } + + startIndex_ = FastSolveStartIndex(); +} } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index 442077206ea..af3407cd39c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -93,6 +93,15 @@ public: */ void SetNextSegmentStartPos(const std::vector& margins, int32_t itemIdx); + /** + * @brief Update member variables after measure. + * + * @param mainSize waterFlow length on the main axis. + * @param bottomMargin of the last FlowItem segment. + * @param overScroll whether overScroll is allowed. Might adjust offset if not. + */ + void Sync(float mainSize, float bottomMargin, bool overScroll); + float currentOffset_ = 0.0f; float prevOffset_ = 0.0f; float lastMainSize_ = 0.0f; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 3515623a80c..81301c23df8 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -19,6 +19,7 @@ #include "core/components/scroll/scroll_controller_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" #include "core/components_ng/pattern/waterflow/water_flow_paint_method.h" +#include "core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" namespace OHOS::Ace::NG { SizeF WaterFlowPattern::GetContentSize() const @@ -72,10 +73,6 @@ bool WaterFlowPattern::UpdateCurrentOffset(float delta, int32_t source) } if (GreatNotEqual(delta, 0.0f)) { delta = std::min(delta, -layoutInfo_.currentOffset_); - } else if (layoutInfo_.itemEnd_ && !layoutInfo_.endPosArray_.empty()) { - float distanceToEnd = - layoutInfo_.endPosArray_.back().first - (GetMainContentSize() - layoutInfo_.currentOffset_); - delta = std::max(delta, distanceToEnd); } } FireOnWillScroll(-delta); @@ -163,7 +160,7 @@ RefPtr WaterFlowPattern::CreateLayoutAlgorithm() if (targetIndex_.has_value()) { layoutInfo_.targetIndex_ = targetIndex_; } - auto algorithm = AceType::MakeRefPtr(layoutInfo_); + auto algorithm = AceType::MakeRefPtr(layoutInfo_); algorithm->SetCanOverScroll(CanOverScroll(GetScrollSource())); return algorithm; } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index a5fabdd8d34..ca58cfc34d1 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -16,11 +16,11 @@ #include "core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" #include "base/geometry/ng/offset_t.h" +#include "core/components_ng/base/frame_node.h" #include "core/components_ng/layout/layout_wrapper.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_utils.h" #include "core/components_ng/property/templates_parser.h" -#include "core/components_ng/base/frame_node.h" namespace OHOS::Ace::NG { void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) @@ -30,25 +30,10 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) axis_ = props->GetAxis(); auto [idealSize, matchChildren] = PreMeasureSelf(); - info_.childrenCount_ = wrapper_->GetTotalChildCount(); - Init(idealSize); mainSize_ = GetMainAxisSize(idealSize, axis_); - // independent reset / jump procedure - - // independent offset (scrolling) procedure - bool forward = LessOrEqual(info_.currentOffset_ - info_.prevOffset_, 0.0f); - if (forward) { - Fill(); - } - - info_.startIndex_ = info_.FastSolveStartIndex(); - info_.endIndex_ = info_.FastSolveEndIndex(mainSize_); - - info_.itemStart_ = GreatOrEqual(info_.currentOffset_, 0.0f); - info_.itemEnd_ = info_.endIndex_ == info_.childrenCount_ - 1; - info_.offsetEnd_ = info_.itemEnd_ && mainSize_ - info_.currentOffset_ >= info_.endPosArray_.back().first; + MeasureOnOffset(); if (matchChildren) { PostMeasureSelf(idealSize); @@ -79,10 +64,10 @@ void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper) void WaterFlowSegmentedLayout::Init(const SizeF& frameSize) { + info_.childrenCount_ = wrapper_->GetTotalChildCount(); auto props = DynamicCast(wrapper_->GetLayoutProperty()); itemsCrossSize_.clear(); axis_ = props->GetAxis(); - info_.childrenCount_ = wrapper_->GetTotalChildCount(); RegularInit(frameSize); if (info_.footerIndex_ >= 0) { InitFooter(frameSize.CrossSize(axis_)); @@ -151,6 +136,7 @@ void WaterFlowSegmentedLayout::InitFooter(float crossSize) auto waterFlow = wrapper_->GetHostNode(); waterFlow->RemoveChildAtIndex(info_.footerIndex_); footer->GetHostNode()->MountToParent(waterFlow); + footer->SetActive(false); info_.footerIndex_ = info_.childrenCount_ - 1; } @@ -164,6 +150,22 @@ void WaterFlowSegmentedLayout::InitFooter(float crossSize) info_.segmentTails_.push_back(info_.childrenCount_ - 1); } +void WaterFlowSegmentedLayout::MeasureOnOffset() +{ + bool forward = LessOrEqual(info_.currentOffset_ - info_.prevOffset_, 0.0f); + if (forward) { + Fill(); + } + + int32_t oldStart = info_.startIndex_; + info_.Sync(mainSize_, margins_.back().bottom.value_or(0.0f), overScroll_); + + if (!forward) { + // measure appearing items when scrolling upwards + MeasureItems(info_.startIndex_, oldStart); + } +} + void WaterFlowSegmentedLayout::Fill() { auto props = DynamicCast(wrapper_->GetLayoutProperty()); @@ -193,6 +195,18 @@ void WaterFlowSegmentedLayout::Fill() } } +void WaterFlowSegmentedLayout::MeasureItems(int32_t startIdx, int32_t endIdx) +{ + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + for (int32_t i = startIdx; i < endIdx; ++i) { + int32_t segment = info_.GetSegment(i); + auto position = WaterFlowLayoutUtils::GetItemPosition(info_, i, mainGaps_[segment]); + auto itemWrapper = wrapper_->GetOrCreateChildByIndex(i); + itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint( + { itemsCrossSize_[segment][position.crossIndex], mainSize_, axis_ }, props, itemWrapper)); + } +} + std::pair WaterFlowSegmentedLayout::PreMeasureSelf() { auto props = wrapper_->GetLayoutProperty(); @@ -208,7 +222,7 @@ std::pair WaterFlowSegmentedLayout::PreMeasureSelf() void WaterFlowSegmentedLayout::PostMeasureSelf(SizeF size) { - mainSize_ = info_.GetMaxMainHeight(); + mainSize_ = info_.maxHeight_; size.SetMainSize(mainSize_, axis_); auto props = wrapper_->GetLayoutProperty(); AddPaddingToSize(props->CreatePaddingAndBorder(), size); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h index ae25394ab51..20781e12320 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h @@ -39,8 +39,23 @@ public: return std::move(info_); } + void SetCanOverScroll(bool value) override { + overScroll_ = value; + } + private: + /** + * @brief Initialize member variables from LayoutProperty. + * + * @param frameSize + */ void Init(const SizeF& frameSize); + + /** + * @brief init regular WaterFlow with a single segment. + * + * @param frameSize + */ void RegularInit(const SizeF& frameSize); void InitFooter(float crossSize); @@ -58,14 +73,23 @@ private: */ void PostMeasureSelf(SizeF size); - void SolveLayoutRange(); + void MeasureOnOffset(); /** * @brief Fills the viewport with new items when scrolling downwards. - * WaterFlow only supports forward layout because the position of a new item always depends on previous items. + * WaterFlow's item map only supports forward layout because the position of a new item always depends on previous items. * */ void Fill(); + + /** + * @brief Helper to measure FlowItems. + * Assumes the Item map is already filled and only call Measure on children in range [start, end). + * + * @param startIdx + * @param endIdx (exclusive) + */ + void MeasureItems(int32_t startIdx, int32_t endIdx); void LayoutSegment(int32_t segment, const OffsetF& padding, bool isReverse); @@ -81,6 +105,9 @@ private: float mainSize_ = 0.0f; WaterFlowLayoutInfo info_; + // true if WaterFlow can be overScrolled + bool overScroll_ = false; + ACE_DISALLOW_COPY_AND_MOVE(WaterFlowSegmentedLayout); }; } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 8e4b7740f13..a6107575e53 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -950,7 +950,7 @@ HWTEST_F(WaterFlowTestNg, WaterFlowFooterTest001, TestSize.Level1) * @tc.steps: step1. Run footer func. * @tc.expected: The return_value is correct. */ - auto footerRect = GetChildRect(frameNode_, 0); + auto footerRect = GetChildRect(frameNode_, pattern_->layoutInfo_.footerIndex_); EXPECT_FLOAT_EQ(footerRect.GetY(), 200.f); } -- Gitee From 8346aa0d116b00ab9b1bf5efb1a0104bc1cf136d Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 19 Feb 2024 19:42:17 +0800 Subject: [PATCH 7/8] fix layout and add test Signed-off-by: Tianer Zhou Change-Id: I3698fff33d0b1f454fa4f9768eab10af9fb137cf --- .../core/components_ng/pattern/BUILD.gn | 2 +- .../waterflow/water_flow_layout_algorithm.cpp | 8 +- .../waterflow/water_flow_layout_info.cpp | 20 +- .../waterflow/water_flow_layout_info.h | 40 ++-- .../pattern/waterflow/water_flow_pattern.cpp | 2 +- .../waterflow/water_flow_segmented_layout.cpp | 68 +++---- .../waterflow/water_flow_segmented_layout.h | 30 +-- .../core/pipeline/mock_pipeline_context.cpp | 1 + .../pattern/waterflow/water_flow_item_maps.h | 69 +++++-- .../water_flow_segment_layout_test.cpp | 181 +++++++++++++++++- .../pattern/waterflow/water_flow_test_ng.cpp | 6 +- .../pattern/waterflow/water_flow_test_ng.h | 15 +- 12 files changed, 337 insertions(+), 105 deletions(-) diff --git a/frameworks/core/components_ng/pattern/BUILD.gn b/frameworks/core/components_ng/pattern/BUILD.gn index 1eb2a1f392f..ff5e2642ced 100644 --- a/frameworks/core/components_ng/pattern/BUILD.gn +++ b/frameworks/core/components_ng/pattern/BUILD.gn @@ -509,11 +509,11 @@ build_component_ng("pattern_ng") { "waterflow/water_flow_layout_algorithm.cpp", "waterflow/water_flow_layout_info.cpp", "waterflow/water_flow_layout_property.cpp", + "waterflow/water_flow_layout_utils.cpp", "waterflow/water_flow_model_ng.cpp", "waterflow/water_flow_paint_method.cpp", "waterflow/water_flow_pattern.cpp", "waterflow/water_flow_segmented_layout.cpp", - "waterflow/water_flow_layout_utils.cpp", ] external_deps = [] diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp index e04f67ae00d..657fed221c6 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp @@ -60,9 +60,11 @@ void WaterFlowLayoutAlgorithm::InitialItemsCrossSize( std::vector crossLens; std::pair, bool> cross; if (axis_ == Axis::VERTICAL) { - cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount); + cross = + ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount); } else { - cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount); + cross = + ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount); } crossLens = cross.first; if (crossLens.empty()) { @@ -154,7 +156,7 @@ void WaterFlowLayoutAlgorithm::MeasureForAnimation(LayoutWrapper* layoutWrapper) if (!itemWrapper) { layoutInfo_.targetIndex_.reset(); break; - } + } itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint( { itemsCrossSize_.at(position.crossIndex), mainSize_, axis_ }, layoutProperty, itemWrapper)); auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 51ffa3c0eb8..2af1c8f76b2 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -296,24 +296,27 @@ int32_t WaterFlowLayoutInfo::FastSolveEndIndex(float mainSize) const } auto it = std::lower_bound(itemInfos_.begin(), itemInfos_.end(), mainSize - currentOffset_, - [](const std::pair& info, float value) { return info.second.first < value; }); + [](const ItemInfo& info, float value) { return info.mainOffset < value; }); if (it == itemInfos_.end()) { - return itemInfos_.rbegin()->first; + return itemInfos_.size() - 1; } - return it->first - 1; + return std::distance(itemInfos_.begin(), it) - 1; } -void WaterFlowLayoutInfo::AddItemToCache(int32_t idx, float startPos, float height) +void WaterFlowLayoutInfo::RecordItem(int32_t idx, int32_t crossIdx, float startPos, float height) { - itemInfos_[idx] = { startPos, height }; + if (itemInfos_.size() != idx) { + return; + } + items_[GetSegment(idx)][crossIdx][idx] = { startPos, height }; + itemInfos_.emplace_back(crossIdx, startPos, height); if (endPosArray_.empty() || LessNotEqual(endPosArray_.back().first, startPos + height)) { endPosArray_.emplace_back(startPos + height, idx); } } -void WaterFlowLayoutInfo::SetNextSegmentStartPos( - const std::vector& margins, int32_t itemIdx) +void WaterFlowLayoutInfo::SetNextSegmentStartPos(const std::vector& margins, int32_t itemIdx) { int32_t segment = GetSegment(itemIdx); if (segmentStartPos_.size() > segment + 1) { @@ -338,8 +341,9 @@ void WaterFlowLayoutInfo::Sync(float mainSize, float bottomMargin, bool overScro itemStart_ = GreatOrEqual(currentOffset_, 0.0f); itemEnd_ = endIndex_ >= 0 && endIndex_ == childrenCount_ - 1; offsetEnd_ = itemEnd_ && GreatOrEqual(mainSize - currentOffset_, maxHeight_); + // adjust offset when it can't overScroll if (offsetEnd_ && !overScroll) { - currentOffset_ = -maxHeight_ + mainSize; + currentOffset_ = std::min(-maxHeight_ + mainSize, 0.0f); } startIndex_ = FastSolveStartIndex(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index af3407cd39c..53a427d6053 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -49,9 +49,9 @@ public: /** * @brief Get the next available cross index to place a new item. - * + * * @param segmentIdx index of the WaterFlow segment. - * @return FlowItemIndex + * @return FlowItemIndex */ FlowItemIndex GetCrossIndexForNextItem(int32_t segmentIdx) const; @@ -68,18 +68,18 @@ public: int32_t GetSegment(int32_t itemIdx) const; - void AddItemToCache(int32_t idx, float startPos, float height); + void RecordItem(int32_t idx, int32_t crossIdx, float startPos, float height); /** * @brief FInd the first item inside viewport in log_n time using endPosReverseMap_. - * - * @return index of the item. + * + * @return index of the starting item. */ int32_t FastSolveStartIndex() const; /** * @brief Find the last item inside viewport in log_n time using itemInfos_. - * + * * @param mainSize main-axis length of viewport. * @return index of the item. */ @@ -87,7 +87,7 @@ public: /** * @brief Calculate and set the start position of next segment after filling the tail item of the current segment. - * + * * @param margins margin of each segment. * @param itemIdx index of the current flow item. */ @@ -95,7 +95,7 @@ public: /** * @brief Update member variables after measure. - * + * * @param mainSize waterFlow length on the main axis. * @param bottomMargin of the last FlowItem segment. * @param overScroll whether overScroll is allowed. Might adjust offset if not. @@ -128,15 +128,14 @@ public: int32_t firstIndex_ = 0; std::optional targetIndex_; - // (mainOffset, itemMainSize) - using ItemInfo = std::pair; - // Map structure: [crossIndex, [index, ItemInfo]], - using ItemMap = std::map>; + // Map structure: [crossIndex, [index, {mainOffset, itemMainSize}]], + using ItemMap = std::map>>; std::vector items_ { ItemMap() }; - // quick access to item info, ignores crossIndex and segments - std::map itemInfos_; + struct ItemInfo; + // quick access to FlowItem by index + std::vector itemInfos_; /** * @brief pair = { item bottom position, item index }. @@ -170,5 +169,18 @@ public: } } }; + +struct WaterFlowLayoutInfo::ItemInfo { + ItemInfo(int32_t cross, float offset, float size) : crossIdx(cross), mainOffset(offset), mainSize(size) {} + bool operator==(const ItemInfo& other) const + { + return crossIdx == other.crossIdx && mainOffset == other.mainOffset && mainSize == other.mainSize; + } + + int32_t crossIdx; + float mainOffset; + float mainSize; +}; + } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_H \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 81301c23df8..64c9c17231f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -160,7 +160,7 @@ RefPtr WaterFlowPattern::CreateLayoutAlgorithm() if (targetIndex_.has_value()) { layoutInfo_.targetIndex_ = targetIndex_; } - auto algorithm = AceType::MakeRefPtr(layoutInfo_); + auto algorithm = AceType::MakeRefPtr(layoutInfo_); algorithm->SetCanOverScroll(CanOverScroll(GetScrollSource())); return algorithm; } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index ca58cfc34d1..b5393028dad 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -49,16 +49,12 @@ void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper) wrapper_->RemoveAllChildInRenderTree(); - auto size = wrapper_->GetGeometryNode()->GetFrameSize(); auto padding = wrapper_->GetLayoutProperty()->CreatePaddingAndBorder(); - MinusPaddingToSize(padding, size); auto initialOffset = OffsetF(padding.left.value_or(0.0f), padding.top.value_or(0.0f)); auto props = DynamicCast(wrapper_->GetLayoutProperty()); - info_.UpdateStartIndex(); - int32_t firstSegment = info_.GetSegment(info_.startIndex_); - int32_t lastSegment = info_.GetSegment(info_.endIndex_); - for (int32_t seg = firstSegment; seg <= lastSegment; ++seg) { - LayoutSegment(seg, initialOffset, props->IsReverse()); + bool isReverse = props->IsReverse(); + for (int32_t i = info_.startIndex_; i <= info_.endIndex_; ++i) { + LayoutItem(i, initialOffset, isReverse); } } @@ -66,15 +62,11 @@ void WaterFlowSegmentedLayout::Init(const SizeF& frameSize) { info_.childrenCount_ = wrapper_->GetTotalChildCount(); auto props = DynamicCast(wrapper_->GetLayoutProperty()); - itemsCrossSize_.clear(); axis_ = props->GetAxis(); RegularInit(frameSize); if (info_.footerIndex_ >= 0) { InitFooter(frameSize.CrossSize(axis_)); } - // if (!segment) { - - // } } void WaterFlowSegmentedLayout::RegularInit(const SizeF& frameSize) @@ -106,11 +98,6 @@ void WaterFlowSegmentedLayout::RegularInit(const SizeF& frameSize) crossGaps_ = { 0 }; } - // cross count changed by auto-fill and cross size change - if (!info_.items_[0].empty() && crossLens.size() != info_.items_[0].size()) { - info_.Reset(); - } - itemsCrossPosition_.resize(1); itemsCrossSize_.resize(1); margins_.resize(1); @@ -138,6 +125,9 @@ void WaterFlowSegmentedLayout::InitFooter(float crossSize) footer->GetHostNode()->MountToParent(waterFlow); footer->SetActive(false); info_.footerIndex_ = info_.childrenCount_ - 1; + + info_.items_.emplace_back(); + info_.items_.back().try_emplace(0); } crossGaps_.push_back(0.0f); @@ -145,8 +135,7 @@ void WaterFlowSegmentedLayout::InitFooter(float crossSize) margins_.emplace_back(); itemsCrossPosition_.push_back({ 0.0f }); itemsCrossSize_.push_back({ crossSize }); - info_.items_.emplace_back(); - info_.items_.back().try_emplace(0); + info_.segmentTails_.push_back(info_.childrenCount_ - 1); } @@ -182,8 +171,7 @@ void WaterFlowSegmentedLayout::Fill() { itemsCrossSize_[segment][position.crossIndex], mainSize_, axis_ }, props, itemWrapper)); auto itemHeight = GetMainAxisSize(itemWrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); - info_.items_[segment][position.crossIndex][idx] = { position.startMainPos, itemHeight }; - info_.AddItemToCache(idx, position.startMainPos, itemHeight); + info_.RecordItem(idx, position.crossIndex, position.startMainPos, itemHeight); if (idx == info_.segmentTails_[segment]) { info_.SetNextSegmentStartPos(margins_, idx); @@ -229,31 +217,23 @@ void WaterFlowSegmentedLayout::PostMeasureSelf(SizeF size) wrapper_->GetGeometryNode()->SetFrameSize(size); } -void WaterFlowSegmentedLayout::LayoutSegment(int32_t segment, const OffsetF& padding, bool isReverse) +void WaterFlowSegmentedLayout::LayoutItem(int32_t idx, const OffsetF& padding, bool isReverse) { - for (const auto& mainPositions : info_.items_[segment]) { - for (const auto& item : mainPositions.second) { - if (item.first < info_.startIndex_ || item.first > info_.endIndex_) { - continue; - } - - auto crossOffset = itemsCrossPosition_[segment][mainPositions.first]; - auto mainOffset = item.second.first + info_.currentOffset_; - if (isReverse) { - mainOffset = mainSize_ - item.second.second - mainOffset; - } - - OffsetF offset = - (axis_ == Axis::VERTICAL) ? OffsetF(crossOffset, mainOffset) : OffsetF(mainOffset, crossOffset); - auto wrapper = wrapper_->GetOrCreateChildByIndex(item.first); - wrapper->GetGeometryNode()->SetMarginFrameOffset(offset + padding); - wrapper->Layout(); - - // recode restore info - if (item.first == info_.startIndex_) { - info_.storedOffset_ = mainOffset; - } - } + const auto& item = info_.itemInfos_[idx]; + auto crossOffset = itemsCrossPosition_[info_.GetSegment(idx)][item.crossIdx]; + auto mainOffset = item.mainOffset + info_.currentOffset_; + if (isReverse) { + mainOffset = mainSize_ - item.mainSize - mainOffset; + } + + OffsetF offset = (axis_ == Axis::VERTICAL) ? OffsetF(crossOffset, mainOffset) : OffsetF(mainOffset, crossOffset); + auto wrapper = wrapper_->GetOrCreateChildByIndex(idx); + wrapper->GetGeometryNode()->SetMarginFrameOffset(offset + padding); + wrapper->Layout(); + + // recode restore info + if (idx == info_.startIndex_) { + info_.storedOffset_ = mainOffset; } } } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h index 20781e12320..815d03dde41 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h @@ -19,7 +19,6 @@ #include "core/components_ng/layout/layout_algorithm.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/property/measure_property.h" namespace OHOS::Ace::NG { @@ -39,21 +38,22 @@ public: return std::move(info_); } - void SetCanOverScroll(bool value) override { + void SetCanOverScroll(bool value) override + { overScroll_ = value; } private: /** * @brief Initialize member variables from LayoutProperty. - * - * @param frameSize + * + * @param frameSize of WaterFlow component. */ void Init(const SizeF& frameSize); /** * @brief init regular WaterFlow with a single segment. - * + * * @param frameSize */ void RegularInit(const SizeF& frameSize); @@ -77,21 +77,29 @@ private: /** * @brief Fills the viewport with new items when scrolling downwards. - * WaterFlow's item map only supports forward layout because the position of a new item always depends on previous items. - * + * + * WaterFlow's item map only supports orderly forward layout, + * because the position of a new item always depends on previous items. */ void Fill(); - + /** * @brief Helper to measure FlowItems. * Assumes the Item map is already filled and only call Measure on children in range [start, end). - * - * @param startIdx + * + * @param startIdx FlowItem index. * @param endIdx (exclusive) */ void MeasureItems(int32_t startIdx, int32_t endIdx); - void LayoutSegment(int32_t segment, const OffsetF& padding, bool isReverse); + /** + * @brief Layout a FlowItem at [idx]. + * + * @param idx FlowItem index. + * @param padding top-left padding of WaterFlow component. + * @param isReverse need to layout in reverse. + */ + void LayoutItem(int32_t idx, const OffsetF& padding, bool isReverse); LayoutWrapper* wrapper_ {}; diff --git a/test/mock/core/pipeline/mock_pipeline_context.cpp b/test/mock/core/pipeline/mock_pipeline_context.cpp index 3e2c850b0c1..f8f6931b150 100644 --- a/test/mock/core/pipeline/mock_pipeline_context.cpp +++ b/test/mock/core/pipeline/mock_pipeline_context.cpp @@ -347,6 +347,7 @@ void PipelineContext::AddSyncGeometryNodeTask(std::function&& task) task(); } } + void PipelineContext::FlushSyncGeometryNodeTasks() {} void PipelineContext::AddAfterRenderTask(std::function&& task) {} diff --git a/test/unittest/core/pattern/waterflow/water_flow_item_maps.h b/test/unittest/core/pattern/waterflow/water_flow_item_maps.h index 1d639287fc0..3fdaae33eeb 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_item_maps.h +++ b/test/unittest/core/pattern/waterflow/water_flow_item_maps.h @@ -13,6 +13,9 @@ * limitations under the License. */ +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_TEST_PATTERN_WATER_FLOW_ITEM_MAPS_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_TEST_PATTERN_WATER_FLOW_ITEM_MAPS_H + #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" namespace OHOS::Ace::NG { @@ -34,17 +37,17 @@ const decltype(WaterFlowLayoutInfo::items_) ITEM_MAP_1 = { } }; -const std::map ITEM_INFO_1 = { - { 0, { 0.0f, 50.0f } }, - { 1, { 0.0f, 30.0f } }, - { 2, { 0.0f, 40.0f } }, - { 3, { 0.0f, 60.0f } }, - { 4, { 35.0f, 20.0f } }, - { 5, { 65.0f, 50.0f } }, - { 6, { 65.0f, 30.0f } }, - { 7, { 65.0f, 40.0f } }, - { 8, { 96.0f, 2.0f } }, - { 9, { 99.0f, 20.0f } }, +const decltype(WaterFlowLayoutInfo::itemInfos_) ITEM_INFO_1 = { + { 0, 0.0f, 50.0f }, + { 1, 0.0f, 30.0f }, + { 2, 0.0f, 40.0f }, + { 3, 0.0f, 60.0f }, + { 1, 35.0f, 20.0f }, + { 0, 65.0f, 50.0f }, + { 1, 65.0f, 30.0f }, + { 2, 65.0f, 40.0f }, + { 1, 96.0f, 2.0f }, + { 1, 99.0f, 20.0f }, }; const std::vector> END_POS_ARRAY_1 = { @@ -55,4 +58,46 @@ const std::vector> END_POS_ARRAY_1 = { }; const std::vector SEGMENT_TAILS_1 = { 4, 4, 9 }; -} // namespace OHOS::Ace::NG \ No newline at end of file + +const decltype(WaterFlowLayoutInfo::items_) ITEM_MAP_2 = { + { + { 0, { { 0, { 0, 100 } }, { 4, { 101, 100 } }, { 8, { 202, 100 } } } }, + { 1, { { 1, { 0, 200 } }, { 6, { 201, 100 } }, { 9, { 302, 200 } } } }, + { 2, { { 2, { 0, 100 } }, { 5, { 101, 200 } } } }, // Column 2 + { 3, { { 3, { 0, 200 } }, { 7, { 201, 200 } } } } // Column 3 + }, + { { 0, { { 10, { 502, 50 } } } } } // footer +}; + +const decltype(WaterFlowLayoutInfo::itemInfos_) ITEM_INFO_2 = { + { 0, 0.0f, 100.0f }, + { 1, 0.0f, 200.0f }, + { 2, 0.0f, 100.0f }, + { 3, 0.0f, 200.0f }, + { 0, 101.0f, 100.0f }, + { 2, 101.0f, 200.0f }, + { 1, 201.0f, 100.0f }, + { 3, 201.0f, 200.0f }, + { 0, 202.0f, 100.0f }, + { 1, 302.0f, 200.0f }, + { 0, 502.0F, 50.0f }, +}; + +const std::vector> END_POS_ARRAY_2 = { + { 100.0f, 0 }, + { 200.0f, 1 }, + { 201.0f, 4 }, + { 301.0f, 5 }, + { 401.0f, 7 }, + { 502.0f, 9 }, + { 552.0f, 10 }, +}; + +const std::vector SEGMENT_TAILS_2 = { 9, 10 }; +const std::vector SEGMENT_START_POS_2 = { 0.0f, 502.0f }; + +const std::vector SEGMENT_TAILS_3 = { 99, 100 }; + +} // namespace OHOS::Ace::NG + +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_TEST_PATTERN_WATER_FLOW_ITEM_MAPS_H diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp index 8cf997a13ed..a2bb1068108 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -45,7 +45,7 @@ HWTEST_F(WaterFlowSegmentTest, Fill001, TestSize.Level1) CreateItemWithHeight(40.0f); CreateItemWithHeight(2.0f); CreateItemWithHeight(20.0f); - }); + }, false); auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); algo->wrapper_ = AceType::RawPtr(frameNode_); @@ -68,4 +68,183 @@ HWTEST_F(WaterFlowSegmentTest, Fill001, TestSize.Level1) algo->Fill(); EXPECT_EQ(info.items_, ITEM_MAP_1); } + +/** + * @tc.name: MeasureOnOffset001 + * @tc.desc: Test SegmentedLayout::MeasureOnOffset. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, MeasureOnOffset001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr 1fr 1fr"); + model.SetColumnsGap(Dimension(5.0f)); + model.SetRowsGap(Dimension(1.0f)); + auto footer = GetDefaultHeaderBuilder(); + model.SetFooter(std::move(footer)); + CreateItem(10); + ViewStackProcessor::GetInstance()->Pop(); + }, false); + + LayoutConstraintF constraint { .maxSize = { 480.0f, 800.0f }, .percentReference = { 480.0f, 800.0f } }; + layoutProperty_->layoutConstraint_ = constraint; + layoutProperty_->contentConstraint_ = constraint; + + auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto& info = algo->info_; + + info.footerIndex_ = 0; + + for (int i = 0; i < 2; ++i) { + algo->Measure(AceType::RawPtr(frameNode_)); + + EXPECT_EQ(info.childrenCount_, 11); + EXPECT_EQ(info.items_, ITEM_MAP_2); + EXPECT_EQ(info.itemInfos_, ITEM_INFO_2); + EXPECT_EQ(info.segmentTails_, SEGMENT_TAILS_2); + EXPECT_EQ(info.endPosArray_, END_POS_ARRAY_2); + EXPECT_EQ(info.segmentStartPos_, SEGMENT_START_POS_2); + } + + info.prevOffset_ = 0.0f; + info.currentOffset_ = -100.0f; + algo->Measure(AceType::RawPtr(frameNode_)); + EXPECT_EQ(info.currentOffset_, 0.0f); + EXPECT_EQ(info.startIndex_, 0); + EXPECT_EQ(info.endIndex_, 10); + + algo->overScroll_ = true; + info.prevOffset_ = 0.0f; + info.currentOffset_ = -200.0f; + algo->Measure(AceType::RawPtr(frameNode_)); + EXPECT_EQ(info.currentOffset_, -200.0f); + EXPECT_EQ(info.startIndex_, 4); + EXPECT_EQ(info.endIndex_, 10); +} + +/** + * @tc.name: Layout001 + * @tc.desc: Test SegmentedLayout::Layout. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, Layout001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr 1fr 1fr"); + model.SetColumnsGap(Dimension(5.0f)); + model.SetRowsGap(Dimension(1.0f)); + auto footer = GetDefaultHeaderBuilder(); + model.SetFooter(std::move(footer)); + CreateItem(10); + ViewStackProcessor::GetInstance()->Pop(); + }, false); + + LayoutConstraintF constraint { .maxSize = { 480.0f, 800.0f }, .percentReference = { 480.0f, 800.0f } }; + layoutProperty_->layoutConstraint_ = constraint; + layoutProperty_->contentConstraint_ = constraint; + + auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto& info = algo->info_; + + info.footerIndex_ = 0; + + algo->Measure(AceType::RawPtr(frameNode_)); + const std::vector> crossPos = { { 0.0f, 121.25f, 242.5f, 363.75f }, { 0.0f } }; + EXPECT_EQ(algo->itemsCrossPosition_, crossPos); + algo->Layout(AceType::RawPtr(frameNode_)); + EXPECT_EQ(GetChildOffset(frameNode_, 0), OffsetF(0.0f, 0.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 1), OffsetF(121.25f, 0.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 2), OffsetF(242.5f, 0.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 3), OffsetF(363.75f, 0.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 4), OffsetF(0.0f, 101.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 5), OffsetF(242.5f, 101.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 6), OffsetF(121.25f, 201.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 7), OffsetF(363.75f, 201.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 8), OffsetF(0.0f, 202.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 9), OffsetF(121.25f, 302.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 10), OffsetF(0.0f, 502.0f)); + + info.prevOffset_ = 0.0f; + info.currentOffset_ = -100.0f; + algo->overScroll_ = true; + algo->Measure(AceType::RawPtr(frameNode_)); + algo->Layout(AceType::RawPtr(frameNode_)); + EXPECT_EQ(info.startIndex_, 1); + EXPECT_EQ(info.endIndex_, 10); + EXPECT_EQ(GetChildOffset(frameNode_, 1), OffsetF(121.25f, -100.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 2), OffsetF(242.5f, -100.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 3), OffsetF(363.75f, -100.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 4), OffsetF(0.0f, 1.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 5), OffsetF(242.5f, 1.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 6), OffsetF(121.25f, 101.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 7), OffsetF(363.75f, 101.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 8), OffsetF(0.0f, 102.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 9), OffsetF(121.25f, 202.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 10), OffsetF(0.0f, 402.0f)); +} + +/** + * @tc.name: MeasureOnOffset002 + * @tc.desc: Test SegmentedLayout::MeasureOnOffset. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, MeasureOnOffset002, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr 1fr 1fr 1fr"); + model.SetColumnsGap(Dimension(5.0f)); + model.SetRowsGap(Dimension(1.0f)); + auto footer = GetDefaultHeaderBuilder(); + model.SetFooter(std::move(footer)); + CreateItem(100); + ViewStackProcessor::GetInstance()->Pop(); + }, false); + + LayoutConstraintF constraint { .maxSize = { 480.0f, 800.0f }, .percentReference = { 480.0f, 800.0f } }; + layoutProperty_->layoutConstraint_ = constraint; + layoutProperty_->contentConstraint_ = constraint; + + auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto& info = algo->info_; + + info.footerIndex_ = 0; + + for (int i = 0; i < 2; ++i) { + algo->Measure(AceType::RawPtr(frameNode_)); + + EXPECT_EQ(info.childrenCount_, 101); + EXPECT_EQ(info.items_.size(), 2); + EXPECT_EQ(info.startIndex_, 0); + EXPECT_EQ(info.endIndex_, 27); + EXPECT_EQ(info.segmentTails_, SEGMENT_TAILS_3); + } + + info.prevOffset_ = 0.0f; + info.currentOffset_ = -100.0f; + algo->Measure(AceType::RawPtr(frameNode_)); + EXPECT_EQ(info.currentOffset_, -100.0f); + EXPECT_EQ(info.startIndex_, 1); + EXPECT_EQ(info.endIndex_, 30); + + info.prevOffset_ = -100.0f; + info.currentOffset_ = -500.0f; + algo->Measure(AceType::RawPtr(frameNode_)); + EXPECT_EQ(info.currentOffset_, -500.0f); + EXPECT_EQ(info.startIndex_, 11); + EXPECT_EQ(info.endIndex_, 44); + + const auto itemMap = info.items_; + const auto itemInfo = info.itemInfos_; + const auto endPosArr = info.endPosArray_; + + info.prevOffset_ = -500.0f; + info.currentOffset_ = -300.0f; + algo->Measure(AceType::RawPtr(frameNode_)); + EXPECT_EQ(info.items_, itemMap); + EXPECT_EQ(info.itemInfos_, itemInfo); + EXPECT_EQ(info.endPosArray_, endPosArr); + EXPECT_EQ(info.startIndex_, 5); + EXPECT_EQ(info.endIndex_, 37); + EXPECT_EQ(info.segmentStartPos_, std::vector { 0.0f }); +} } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index a6107575e53..78c9ed15f10 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -109,7 +109,7 @@ void WaterFlowTestNg::GetInstance() accessibilityProperty_ = frameNode_->GetAccessibilityProperty(); } -void WaterFlowTestNg::Create(const std::function& callback) +void WaterFlowTestNg::Create(const std::function& callback, bool flushLayout) { WaterFlowModelNG model; RefPtr positionController = model.CreateScrollController(); @@ -122,7 +122,9 @@ void WaterFlowTestNg::Create(const std::function& callba callback(model); } GetInstance(); - FlushLayoutTask(frameNode_); + if (flushLayout) { + FlushLayoutTask(frameNode_); + } } void WaterFlowTestNg::CreateWithItem(const std::function& callback) diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.h b/test/unittest/core/pattern/waterflow/water_flow_test_ng.h index 1ddefddb80e..11975923fe5 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.h +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.h @@ -13,6 +13,9 @@ * limitations under the License. */ +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_TEST_PATTERN_WATER_FLOW_TEST_NG_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_TEST_PATTERN_WATER_FLOW_TEST_NG_H + #include #include #include @@ -32,11 +35,6 @@ #undef private #undef protected -namespace { -using namespace testing; -using namespace testing::ext; -} // namespace - namespace OHOS::Ace::NG { class WaterFlowTestNg : public TestNG { protected: @@ -46,9 +44,9 @@ protected: void TearDown() override; void GetInstance(); - void Create(const std::function& callback = nullptr); + void Create(const std::function& callback = nullptr, bool flushLayout = true); void CreateWithItem(const std::function& callback = nullptr); - static void CreateItem(int32_t number = 10); + static void CreateItem(int32_t number = 10); static void CreateItemWithHeight(float height); void UpdateCurrentOffset(float offset, int32_t source = SCROLL_FROM_UPDATE); void MouseSelect(Offset start, Offset end); @@ -63,4 +61,5 @@ protected: RefPtr layoutProperty_; RefPtr accessibilityProperty_; }; -} // namespace OHOS::Ace::NG \ No newline at end of file +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_TEST_PATTERN_WATER_FLOW_TEST_NG_H -- Gitee From 0e418130cd0e457b81cd17ff39d2f1b1ed3b6aa1 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 26 Feb 2024 19:27:17 +0800 Subject: [PATCH 8/8] fix code check Signed-off-by: Tianer Zhou Change-Id: I8b5429dfd1470e37193f10da62e49ada72d74eb2 --- .../pattern/waterflow/water_flow_layout_algorithm.cpp | 3 +-- .../pattern/waterflow/water_flow_layout_info.cpp | 1 - .../pattern/waterflow/water_flow_segmented_layout.h | 6 +++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp index 657fed221c6..17c43d2838d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp @@ -302,8 +302,7 @@ void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layou layoutInfo_.jumpIndex_ = EMPTY_JUMP_INDEX; layoutInfo_.itemStart_ = false; } - currentIndex++; - position = GetItemPosition(currentIndex); + position = GetItemPosition(++currentIndex); } layoutInfo_.endIndex_ = currentIndex - 1; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 2af1c8f76b2..5517ed47cf9 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -297,7 +297,6 @@ int32_t WaterFlowLayoutInfo::FastSolveEndIndex(float mainSize) const auto it = std::lower_bound(itemInfos_.begin(), itemInfos_.end(), mainSize - currentOffset_, [](const ItemInfo& info, float value) { return info.mainOffset < value; }); - if (it == itemInfos_.end()) { return itemInfos_.size() - 1; } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h index 815d03dde41..3e15862c9bc 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h @@ -61,14 +61,14 @@ private: /** * @brief Measure self before measuring children. - * + * * @return [idealSize given by parent, whether measure is successful (need to adapt to children size if not)]. */ std::pair PreMeasureSelf(); /** * @brief Measure self after measuring children. Only when pre-measure failed. - * + * * @param size ideal content size from parent. */ void PostMeasureSelf(SizeF size); @@ -94,7 +94,7 @@ private: /** * @brief Layout a FlowItem at [idx]. - * + * * @param idx FlowItem index. * @param padding top-left padding of WaterFlow component. * @param isReverse need to layout in reverse. -- Gitee