• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/waterflow/layout/top_down/water_flow_segmented_layout.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/utils/utils.h"
21 #include "core/components/scroll/scroll_controller_base.h"
22 #include "core/components_ng/base/frame_node.h"
23 #include "core/components_ng/layout/layout_wrapper.h"
24 #include "core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h"
25 #include "core/components_ng/pattern/waterflow/layout/water_flow_layout_utils.h"
26 #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h"
27 #include "core/components_ng/pattern/waterflow/water_flow_pattern.h"
28 #include "core/components_ng/pattern/waterflow/water_flow_sections.h"
29 #include "core/components_ng/property/calc_length.h"
30 #include "core/components_ng/property/measure_utils.h"
31 #include "core/components_ng/property/templates_parser.h"
32 
33 namespace OHOS::Ace::NG {
IsDataValid(const RefPtr<WaterFlowLayoutInfoBase> & info,int32_t childrenCnt)34 bool WaterFlowSegmentLayoutBase::IsDataValid(const RefPtr<WaterFlowLayoutInfoBase>& info, int32_t childrenCnt)
35 {
36     if (info->segmentTails_.empty()) {
37         TAG_LOGW(AceLogTag::ACE_WATERFLOW, "Section is empty.");
38         return false;
39     }
40     if (childrenCnt - 1 != info->segmentTails_.back()) {
41         TAG_LOGW(AceLogTag::ACE_WATERFLOW,
42             "Children count = %{public}d and doesn't match the number provided in Sections, which is %{public}d.",
43             childrenCnt, info->segmentTails_.back() + 1);
44         return false;
45     }
46     return true;
47 }
48 
Measure(LayoutWrapper * wrapper)49 void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper)
50 {
51     wrapper_ = wrapper;
52     info_->childrenCount_ = wrapper_->GetTotalChildCount();
53     sections_ = wrapper_->GetHostNode()->GetPattern<WaterFlowPattern>()->GetSections();
54     if (sections_ && !IsDataValid(info_, info_->childrenCount_)) {
55         return;
56     }
57 
58     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper->GetLayoutProperty());
59     info_->axis_ = axis_ = props->GetAxis();
60     auto [idealSize, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_);
61 
62     Init(idealSize);
63 
64     mainSize_ = GetMainAxisSize(idealSize, axis_);
65 
66     if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) {
67         MeasureOnJump(info_->jumpIndex_);
68         info_->jumpIndex_ = EMPTY_JUMP_INDEX;
69     } else if (info_->targetIndex_) {
70         MeasureToTarget(*info_->targetIndex_, std::nullopt);
71         info_->targetIndex_.reset();
72     } else {
73         MeasureOnOffset();
74     }
75 
76     if (matchChildren) {
77         PostMeasureSelf(idealSize);
78     }
79     info_->lastMainSize_ = mainSize_;
80 
81     wrapper_->SetCacheCount(props->GetCachedCountValue(info_->defCachedCount_));
82 }
83 
Layout(LayoutWrapper * wrapper)84 void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper)
85 {
86     if (sections_ && !IsDataValid(info_, info_->childrenCount_)) {
87         return;
88     }
89 
90     wrapper_ = wrapper;
91 
92     auto padding = wrapper_->GetLayoutProperty()->CreatePaddingAndBorder();
93     auto initialOffset = OffsetF(padding.left.value_or(0.0f), padding.top.value_or(0.0f));
94     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
95 
96     size_t segmentCnt = itemsCrossSize_.size();
97     std::vector<std::vector<float>> crossPos(segmentCnt);
98     auto crossSize = wrapper_->GetGeometryNode()->GetFrameSize().CrossSize(axis_);
99     auto layoutDirection = wrapper_->GetLayoutProperty()->GetNonAutoLayoutDirection();
100     auto isRtl = layoutDirection == TextDirection::RTL && axis_ == Axis::VERTICAL;
101     // prepare crossPos
102     for (size_t i = 0; i < segmentCnt; ++i) {
103         float pos = ((axis_ == Axis::VERTICAL) ? info_->margins_[i].left : info_->margins_[i].top).value_or(0.0f);
104         for (const auto& len : itemsCrossSize_[i]) {
105             crossPos[i].push_back(!isRtl ? pos : crossSize - pos - len);
106             pos += len + crossGaps_[i];
107         }
108     }
109 
110     const bool isReverse = props->IsReverse();
111     const int32_t cacheCount = props->GetCachedCountValue(info_->defCachedCount_);
112     if (!props->HasCachedCount()) {
113         info_->UpdateDefaultCachedCount();
114     }
115     const int32_t maxIdx = std::min(info_->endIndex_ + cacheCount, static_cast<int32_t>(info_->itemInfos_.size() - 1));
116     for (int32_t i = std::max(0, info_->startIndex_ - cacheCount); i <= maxIdx; ++i) {
117         LayoutItem(i, crossPos[info_->GetSegment(i)][info_->itemInfos_[i].crossIdx], initialOffset, isReverse);
118     }
119     wrapper_->SetActiveChildRange(
120         info_->NodeIdx(info_->startIndex_), info_->NodeIdx(info_->endIndex_), cacheCount, cacheCount);
121 
122     // for compatibility
123     info_->firstIndex_ = info_->startIndex_;
124 
125     PreloadItems(wrapper_, info_, cacheCount);
126 }
127 
128 namespace {
GetMeasuredHeight(const RefPtr<LayoutWrapper> & item,Axis axis)129 inline float GetMeasuredHeight(const RefPtr<LayoutWrapper>& item, Axis axis)
130 {
131     return GetMainAxisSize(item->GetGeometryNode()->GetMarginFrameSize(), axis);
132 }
133 /**
134  * @brief Prepares a jump to the current StartItem.
135  *
136  * @param info WaterFlowLayoutInfo
137  * @param  postJumpOffset set to current StartItem's offset relative to the viewport.
138  */
PrepareJump(const RefPtr<WaterFlowLayoutInfo> & info,std::optional<float> & postJumpOffset)139 void PrepareJump(const RefPtr<WaterFlowLayoutInfo>& info, std::optional<float>& postJumpOffset)
140 {
141     if (info->endIndex_ == -1 || info->jumpIndex_ != EMPTY_JUMP_INDEX) {
142         // implies that LayoutInfo has already been reset, no need to jump
143         return;
144     }
145     info->jumpIndex_ = std::min(info->startIndex_, info->childrenCount_ - 1);
146     info->align_ = ScrollAlign::START;
147     float itemOffset = (info->itemInfos_.size() <= static_cast<size_t>(info->startIndex_))
148                            ? info->storedOffset_
149                            : info->currentOffset_ + info->itemInfos_[info->startIndex_].mainOffset;
150 
151     postJumpOffset = itemOffset;
152 }
153 } // namespace
154 
CheckDirtyItem() const155 int32_t WaterFlowSegmentedLayout::CheckDirtyItem() const
156 {
157     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
158     for (int32_t i = info_->startIndex_; i <= info_->endIndex_; ++i) {
159         if (static_cast<int32_t>(info_->itemInfos_.size()) <= i) {
160             break;
161         }
162         float userDefHeight = WaterFlowLayoutUtils::GetUserDefHeight(sections_, info_->GetSegment(i), i);
163         if (NonNegative(userDefHeight)) {
164             continue;
165         }
166         auto child = MeasureItem(props, i, info_->itemInfos_[i].crossIdx, userDefHeight, false);
167         CHECK_NULL_BREAK(child);
168         if (!NearEqual(GetMeasuredHeight(child, axis_), info_->itemInfos_[i].mainSize)) {
169             return i;
170         }
171     }
172     return -1;
173 }
174 
Init(const SizeF & frameSize)175 void WaterFlowSegmentedLayout::Init(const SizeF& frameSize)
176 {
177     if (sections_) {
178         const auto& sections = sections_->GetSectionInfo();
179         if (info_->margins_.empty()) {
180             // empty margins_ implies a segment change
181             auto constraint = wrapper_->GetLayoutProperty()->GetLayoutConstraint();
182             PrepareJump(info_, postJumpOffset_);
183             info_->InitMargins(sections, constraint->scaleProperty, constraint->percentReference.Width());
184             info_->PrepareSegmentStartPos();
185         }
186         SegmentedInit(sections, info_->margins_, frameSize);
187     } else {
188         RegularInit(frameSize);
189         if (info_->footerIndex_ >= 0) {
190             InitFooter(frameSize.CrossSize(axis_));
191         }
192     }
193 
194     int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated();
195     if (updateIdx != -1) {
196         if (updateIdx <= info_->endIndex_) {
197             PrepareJump(info_, postJumpOffset_);
198         }
199         info_->ClearCacheAfterIndex(updateIdx - 1);
200     }
201 
202     const bool childDirty = wrapper_->GetLayoutProperty()->GetPropertyChangeFlag() & PROPERTY_UPDATE_BY_CHILD_REQUEST;
203     if (childDirty) {
204         const int32_t res = CheckDirtyItem();
205         if (res != -1) {
206             PrepareJump(info_, postJumpOffset_);
207             info_->ClearCacheAfterIndex(res - 1);
208             return;
209         }
210     }
211 
212     if (!wrapper_->IsConstraintNoChanged()) {
213         PrepareJump(info_, postJumpOffset_);
214     }
215 }
216 
SegmentedInit(const std::vector<WaterFlowSections::Section> & options,const std::vector<PaddingPropertyF> & margins,const SizeF & frameSize)217 void WaterFlowSegmentLayoutBase::SegmentedInit(const std::vector<WaterFlowSections::Section>& options,
218     const std::vector<PaddingPropertyF>& margins, const SizeF& frameSize)
219 {
220     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
221     auto scale = props->GetLayoutConstraint()->scaleProperty;
222     size_t n = options.size();
223     crossGaps_.resize(n);
224     mainGaps_.resize(n);
225     itemsCrossSize_.resize(n);
226     for (size_t i = 0; i < n; ++i) {
227         auto rowGap = options[i].rowsGap.value_or(props->GetRowsGap().value_or(0.0_vp));
228         auto columnGap = options[i].columnsGap.value_or(props->GetColumnsGap().value_or(0.0_vp));
229         mainGaps_[i] = ConvertToPx(rowGap, scale, frameSize.Height()).value_or(0.0f);
230         crossGaps_[i] = ConvertToPx(columnGap, scale, frameSize.Width()).value_or(0.0f);
231         if (axis_ == Axis::HORIZONTAL) {
232             std::swap(crossGaps_[i], mainGaps_[i]);
233         }
234 
235         const auto& margin = margins[i];
236         float crossSize = frameSize.CrossSize(axis_) - (axis_ == Axis::VERTICAL ? margin.Width() : margin.Height());
237         int32_t crossCnt = options[i].crossCount.value_or(1);
238         itemsCrossSize_[i].resize(crossCnt);
239         if (crossCnt == 0) {
240             continue;
241         }
242         float itemSize = (crossSize + crossGaps_[i]) / crossCnt - crossGaps_[i];
243         for (int32_t cross = 0; cross < crossCnt; ++cross) {
244             itemsCrossSize_[i][cross] = itemSize;
245         }
246     }
247 }
248 
RegularInit(const SizeF & frameSize)249 void WaterFlowSegmentedLayout::RegularInit(const SizeF& frameSize)
250 {
251     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
252     auto rowsTemplate = props->GetRowsTemplate().value_or("1fr");
253     auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr");
254     auto scale = props->GetLayoutConstraint()->scaleProperty;
255     auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
256     auto columnsGap = ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
257     mainGaps_ = { axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap };
258     crossGaps_ = { axis_ == Axis::VERTICAL ? columnsGap : rowsGap };
259 
260     auto crossSize = frameSize.CrossSize(axis_);
261     std::vector<double> crossLens;
262     std::pair<std::vector<double>, double> cross;
263     if (axis_ == Axis::VERTICAL) {
264         cross = ParseTemplateArgs(
265             WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGaps_[0], info_->childrenCount_);
266     } else {
267         cross = ParseTemplateArgs(
268             WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGaps_[0], info_->childrenCount_);
269     }
270     crossLens = cross.first;
271     if (crossLens.empty()) {
272         crossLens.push_back(crossSize);
273     }
274     crossGaps_ = { cross.second };
275 
276     itemsCrossSize_ = { {} };
277 
278     if (crossLens.size() < info_->items_[0].size()) {
279         auto it = info_->items_[0].find(crossLens.size());
280         info_->items_[0].erase(it, info_->items_[0].end());
281     }
282     int32_t index = 0;
283     for (const auto& len : crossLens) {
284         itemsCrossSize_[0].push_back(len);
285         info_->items_[0].try_emplace(index, std::map<int32_t, std::pair<float, float>>());
286         ++index;
287     }
288     info_->margins_.resize(1);
289     info_->segmentTails_ = { (info_->footerIndex_ >= 0) ? info_->childrenCount_ - 2 : info_->childrenCount_ - 1 };
290 }
291 
InitFooter(float crossSize)292 void WaterFlowSegmentedLayout::InitFooter(float crossSize)
293 {
294     mainGaps_.emplace_back(0.0f);
295     itemsCrossSize_.emplace_back(std::vector<float> { crossSize });
296 
297     if (info_->items_.size() == 1) {
298         info_->items_.emplace_back();
299         info_->items_.back().try_emplace(0);
300     }
301     info_->margins_.emplace_back();
302     info_->segmentTails_.emplace_back(info_->childrenCount_ - 1);
303 
304     if (info_->footerIndex_ != info_->childrenCount_ - 1) {
305         info_->footerIndex_ = std::min(info_->footerIndex_, info_->childrenCount_ - 1);
306         info_->ClearCacheAfterIndex(info_->footerIndex_ - 1);
307         // re-insert at the end
308         auto footer = wrapper_->GetOrCreateChildByIndex(info_->footerIndex_);
309         auto waterFlow = wrapper_->GetHostNode();
310         waterFlow->RemoveChildAtIndex(info_->footerIndex_);
311         footer->GetHostNode()->MountToParent(waterFlow);
312         footer->SetActive(false);
313         info_->segmentCache_.erase(info_->footerIndex_);
314         info_->footerIndex_ = info_->childrenCount_ - 1;
315     }
316 }
317 
MeasureOnOffset()318 void WaterFlowSegmentedLayout::MeasureOnOffset()
319 {
320     const float prevOffset = wrapper_->GetHostNode()->GetPattern<WaterFlowPattern>()->GetPrevOffset();
321     const bool forward = LessOrEqual(info_->currentOffset_, prevOffset) || info_->endIndex_ == -1;
322     if (forward) {
323         Fill(info_->endIndex_ + 1);
324     }
325 
326     const int32_t oldStart = info_->startIndex_;
327     info_->Sync(mainSize_, overScroll_);
328 
329     if (!forward) {
330         // measure appearing items when scrolling upwards
331         auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
332         const int32_t bound = std::min(oldStart, info_->endIndex_);
333         for (int32_t i = info_->startIndex_; i <= bound; ++i) {
334             auto item = MeasureItem(props, i, info_->itemInfos_[i].crossIdx,
335                 WaterFlowLayoutUtils::GetUserDefHeight(sections_, info_->GetSegment(i), i), false);
336             CHECK_NULL_BREAK(item);
337             if (!NearEqual(GetMeasuredHeight(item, axis_), info_->itemInfos_[i].mainSize)) {
338                 // refill from [i] if height doesn't match record
339                 info_->ClearCacheAfterIndex(i - 1);
340                 Fill(i);
341                 info_->Sync(mainSize_, overScroll_);
342                 break;
343             }
344         }
345     }
346 }
347 
MeasureOnJump(int32_t jumpIdx)348 void WaterFlowSegmentedLayout::MeasureOnJump(int32_t jumpIdx)
349 {
350     if (postJumpOffset_) { // preemptively reset layout range
351         info_->startIndex_ = 0;
352         info_->endIndex_ = -1;
353     }
354     if (info_->extraOffset_) {
355         postJumpOffset_ = postJumpOffset_.value_or(0.0f) + *info_->extraOffset_;
356     }
357     if (jumpIdx >= info_->childrenCount_) {
358         return;
359     }
360     if (jumpIdx == LAST_ITEM) {
361         jumpIdx = info_->childrenCount_ - 1;
362     }
363     if (jumpIdx >= static_cast<int32_t>(info_->itemInfos_.size())) {
364         // prepare items
365         MeasureToTarget(jumpIdx, std::nullopt);
366     }
367 
368     if (jumpIdx < 0 || jumpIdx >= static_cast<int32_t>(info_->itemInfos_.size())) {
369         TAG_LOGW(AceLogTag::ACE_WATERFLOW, "jumpIdx %{public}d is out of range, itemInfos_.size() = %{public}zu",
370             jumpIdx, info_->itemInfos_.size());
371         return;
372     }
373     // solve offset
374     const auto& item = info_->itemInfos_[jumpIdx];
375     if (info_->align_ == ScrollAlign::AUTO) {
376         info_->align_ = TransformAutoScroll(item);
377     }
378     info_->currentOffset_ = SolveJumpOffset(item) + postJumpOffset_.value_or(0.0f);
379 
380     Fill(jumpIdx);
381     info_->Sync(mainSize_, false);
382 
383     // only if range [startIndex, jumpIdx) isn't measured (used user-defined size)
384     if (!sections_) {
385         return;
386     }
387     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
388     for (int32_t i = info_->startIndex_; i < jumpIdx; ++i) {
389         auto seg = info_->GetSegment(i);
390         MeasureItem(
391             props, i, info_->itemInfos_[i].crossIdx, WaterFlowLayoutUtils::GetUserDefHeight(sections_, seg, i), false);
392     }
393 }
394 
TransformAutoScroll(const WaterFlowLayoutInfo::ItemInfo & item) const395 ScrollAlign WaterFlowSegmentedLayout::TransformAutoScroll(const WaterFlowLayoutInfo::ItemInfo& item) const
396 {
397     const bool isAbove = Negative(info_->currentOffset_ + item.mainOffset);
398     const bool isBelow = GreatNotEqual(info_->currentOffset_ + item.mainOffset + item.mainSize, mainSize_);
399     if (isAbove && isBelow) {
400         // possible when the item is larger than viewport
401         return ScrollAlign::NONE;
402     }
403     if (isAbove) {
404         return ScrollAlign::START;
405     }
406     if (isBelow) {
407         return ScrollAlign::END;
408     }
409     return ScrollAlign::NONE;
410 }
411 
SolveJumpOffset(const WaterFlowLayoutInfo::ItemInfo & item) const412 float WaterFlowSegmentedLayout::SolveJumpOffset(const WaterFlowLayoutInfo::ItemInfo& item) const
413 {
414     float offset = info_->currentOffset_;
415     switch (info_->align_) {
416         case ScrollAlign::START:
417             offset = -item.mainOffset;
418             break;
419 
420         case ScrollAlign::CENTER:
421             offset = -(item.mainOffset + item.mainSize / 2.0f) + mainSize_ / 2.0f;
422             break;
423 
424         case ScrollAlign::END:
425             offset = -(item.mainOffset + item.mainSize) + mainSize_;
426             break;
427         default:
428             break;
429     }
430     offset = std::min(0.0f, offset);
431     return offset;
432 }
433 
MeasureToTarget(int32_t targetIdx,std::optional<int64_t> cacheDeadline)434 void WaterFlowSegmentedLayout::MeasureToTarget(int32_t targetIdx, std::optional<int64_t> cacheDeadline)
435 {
436     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
437     targetIdx = std::min(targetIdx, info_->childrenCount_ - 1);
438     for (int32_t i = static_cast<int32_t>(info_->itemInfos_.size()); i <= targetIdx; ++i) {
439         int32_t seg = info_->GetSegment(i);
440         auto position = WaterFlowLayoutUtils::GetItemPosition(info_, i, mainGaps_[seg]);
441         float itemHeight = WaterFlowLayoutUtils::GetUserDefHeight(sections_, seg, i);
442         if (cacheDeadline || Negative(itemHeight)) {
443             auto item = MeasureItem(props, i, position.crossIndex, itemHeight, cacheDeadline.has_value());
444             if (item) {
445                 itemHeight = GetMeasuredHeight(item, axis_);
446             }
447         }
448         info_->RecordItem(i, position, itemHeight);
449         if (cacheDeadline && GetSysTimestamp() > *cacheDeadline) {
450             break;
451         }
452     }
453 }
454 
Fill(int32_t startIdx)455 void WaterFlowSegmentedLayout::Fill(int32_t startIdx)
456 {
457     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
458     for (int32_t i = startIdx; i < info_->childrenCount_; ++i) {
459         auto position = WaterFlowLayoutUtils::GetItemPosition(info_, i, mainGaps_[info_->GetSegment(i)]);
460         if (GreatOrEqual(position.startMainPos + info_->currentOffset_, mainSize_)) {
461             break;
462         }
463         float itemHeight = WaterFlowLayoutUtils::GetUserDefHeight(sections_, info_->GetSegment(i), i);
464         auto item = MeasureItem(props, i, position.crossIndex, itemHeight, false);
465         if (!item) {
466             continue;
467         }
468         if (info_->itemInfos_.size() <= static_cast<size_t>(i)) {
469             info_->RecordItem(i, position, GetMeasuredHeight(item, axis_));
470         }
471     }
472 }
473 
MeasureItem(const RefPtr<WaterFlowLayoutProperty> & props,int32_t idx,int32_t crossIdx,float userDefMainSize,bool isCache) const474 RefPtr<LayoutWrapper> WaterFlowSegmentedLayout::MeasureItem(const RefPtr<WaterFlowLayoutProperty>& props, int32_t idx,
475     int32_t crossIdx, float userDefMainSize, bool isCache) const
476 {
477     auto item = wrapper_->GetOrCreateChildByIndex(idx, !isCache, isCache);
478     CHECK_NULL_RETURN(item, nullptr);
479     // override user-defined main size
480     if (NonNegative(userDefMainSize)) {
481         WaterFlowLayoutUtils::UpdateItemIdealSize(item, axis_, userDefMainSize);
482     }
483     item->Measure(WaterFlowLayoutUtils::CreateChildConstraint(
484         { itemsCrossSize_[info_->GetSegment(idx)][crossIdx], mainSize_, axis_, NonNegative(userDefMainSize) }, props,
485         item));
486     if (isCache) {
487         item->Layout();
488         item->SetActive(false);
489     }
490     return item;
491 }
492 
PostMeasureSelf(SizeF size)493 void WaterFlowSegmentedLayout::PostMeasureSelf(SizeF size)
494 {
495     mainSize_ = info_->maxHeight_;
496     size.SetMainSize(mainSize_, axis_);
497     auto props = wrapper_->GetLayoutProperty();
498     AddPaddingToSize(props->CreatePaddingAndBorder(), size);
499     wrapper_->GetGeometryNode()->SetFrameSize(size);
500 }
501 
LayoutItem(int32_t idx,float crossPos,const OffsetF & padding,bool isReverse)502 void WaterFlowSegmentedLayout::LayoutItem(int32_t idx, float crossPos, const OffsetF& padding, bool isReverse)
503 {
504     const auto& item = info_->itemInfos_[idx];
505     auto mainOffset = item.mainOffset + info_->currentOffset_;
506     if (isReverse) {
507         mainOffset = mainSize_ - item.mainSize - mainOffset;
508     }
509 
510     OffsetF offset = (axis_ == Axis::VERTICAL) ? OffsetF(crossPos, mainOffset) : OffsetF(mainOffset, crossPos);
511     auto wrapper = wrapper_->GetChildByIndex(idx, idx < info_->startIndex_ || idx > info_->endIndex_);
512     CHECK_NULL_VOID(wrapper);
513     wrapper->GetGeometryNode()->SetMarginFrameOffset(offset + padding);
514     if (wrapper->CheckNeedForceMeasureAndLayout()) {
515         wrapper->Layout();
516     } else {
517         wrapper->GetHostNode()->ForceSyncGeometryNode();
518     }
519 
520     // recode restore info
521     if (idx == info_->startIndex_) {
522         info_->storedOffset_ = mainOffset;
523     }
524 }
525 
AppendCacheItem(LayoutWrapper * host,int32_t itemIdx,int64_t deadline)526 bool WaterFlowSegmentedLayout::AppendCacheItem(LayoutWrapper* host, int32_t itemIdx, int64_t deadline)
527 {
528     wrapper_ = host;
529     if (itemIdx < static_cast<int32_t>(info_->itemInfos_.size())) {
530         return host->GetOrCreateChildByIndex(itemIdx, false, true);
531     }
532     MeasureToTarget(itemIdx, deadline);
533     return true;
534 }
535 } // namespace OHOS::Ace::NG
536