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