1 /*
2  * Copyright (c) 2023-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_layout_algorithm.h"
17 
18 #include "core/components_ng/pattern/waterflow/layout/water_flow_layout_utils.h"
19 #include "core/components_ng/property/templates_parser.h"
20 
21 namespace OHOS::Ace::NG {
22 namespace {
23 const std::string UNIT_AUTO = "auto";
24 } // namespace
25 
ComputeCrossPosition(int32_t crossIndex) const26 float WaterFlowLayoutAlgorithm::ComputeCrossPosition(int32_t crossIndex) const
27 {
28     float position = 0.0f;
29     for (int32_t index = 0; index < crossIndex; ++index) {
30         if (index >= 0 && index < static_cast<int32_t>(itemsCrossSize_.size())) {
31             position += itemsCrossSize_.at(index);
32         }
33     }
34     position += crossIndex * crossGap_;
35     return position;
36 }
37 
InitialItemsCrossSize(const RefPtr<WaterFlowLayoutProperty> & layoutProperty,const SizeF & frameSize,int32_t childrenCount)38 void WaterFlowLayoutAlgorithm::InitialItemsCrossSize(
39     const RefPtr<WaterFlowLayoutProperty>& layoutProperty, const SizeF& frameSize, int32_t childrenCount)
40 {
41     itemsCrossSize_.clear();
42     itemsCrossPosition_.clear();
43     auto rowsTemplate = layoutProperty->GetRowsTemplate().value_or("1fr");
44     auto columnsTemplate = layoutProperty->GetColumnsTemplate().value_or("1fr");
45     axis_ = layoutProperty->GetAxis();
46     auto scale = layoutProperty->GetLayoutConstraint()->scaleProperty;
47     auto rowsGap = ConvertToPx(layoutProperty->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
48     auto columnsGap =
49         ConvertToPx(layoutProperty->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
50     mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap;
51     crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap;
52 
53     auto crossSize = frameSize.CrossSize(axis_);
54     std::vector<double> crossLens;
55     std::pair<std::vector<double>, double> cross;
56     if (axis_ == Axis::VERTICAL) {
57         cross =
58             ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount);
59     } else {
60         cross =
61             ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount);
62     }
63     crossLens = cross.first;
64     if (crossLens.empty()) {
65         crossLens.push_back(crossSize);
66     }
67     crossGap_ = cross.second;
68 
69     // cross count changed by auto-fill and cross size change
70     if (!layoutInfo_->items_[0].empty() && crossLens.size() != layoutInfo_->items_[0].size()) {
71         layoutInfo_->Reset();
72     }
73 
74     int32_t index = 0;
75     for (const auto& len : crossLens) {
76         itemsCrossSize_.try_emplace(index, len);
77         itemsCrossPosition_.try_emplace(index, ComputeCrossPosition(index));
78         layoutInfo_->items_[0].try_emplace(index, std::map<int32_t, std::pair<float, float>>());
79         ++index;
80     }
81 }
82 
Measure(LayoutWrapper * layoutWrapper)83 void WaterFlowLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
84 {
85     auto layoutProperty = AceType::DynamicCast<WaterFlowLayoutProperty>(layoutWrapper->GetLayoutProperty());
86     CHECK_NULL_VOID(layoutProperty);
87 
88     Axis axis = layoutProperty->GetAxis();
89     auto idealSize =
90         CreateIdealSize(layoutProperty->GetLayoutConstraint().value(), axis, layoutProperty->GetMeasureType(), true);
91     if (NearZero(GetCrossAxisSize(idealSize, axis))) {
92         TAG_LOGI(AceLogTag::ACE_WATERFLOW, "cross size is 0, skip measure");
93         skipMeasure_ = true;
94         return;
95     }
96     auto matchChildren = GreaterOrEqualToInfinity(GetMainAxisSize(idealSize, axis));
97     if (!matchChildren) {
98         layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
99     }
100     MinusPaddingToSize(layoutProperty->CreatePaddingAndBorder(), idealSize);
101 
102     int32_t updateIdx = GetUpdateIdx(layoutWrapper, layoutInfo_->footerIndex_);
103     if (updateIdx != -1) {
104         layoutInfo_->Reset(updateIdx);
105         layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
106     }
107 
108     layoutInfo_->childrenCount_ = layoutWrapper->GetTotalChildCount();
109 
110     InitialItemsCrossSize(layoutProperty, idealSize, layoutInfo_->childrenCount_);
111     mainSize_ = GetMainAxisSize(idealSize, axis);
112     if (layoutInfo_->jumpIndex_ >= 0 && layoutInfo_->jumpIndex_ < layoutInfo_->childrenCount_) {
113         auto crossIndex = layoutInfo_->GetCrossIndex(layoutInfo_->jumpIndex_);
114         if (crossIndex == -1) {
115             // jump to out of cache
116         } else {
117             layoutInfo_->JumpTo(layoutInfo_->items_[0][crossIndex][layoutInfo_->jumpIndex_]);
118         }
119     } else {
120         layoutInfo_->jumpIndex_ = EMPTY_JUMP_INDEX;
121     }
122 
123     FillViewport(mainSize_, layoutWrapper);
124     if (layoutInfo_->targetIndex_.has_value()) {
125         MeasureToTarget(layoutWrapper, layoutInfo_->endIndex_, std::nullopt);
126     }
127     if (layoutInfo_->jumpIndex_ != EMPTY_JUMP_INDEX) {
128         if (layoutInfo_->extraOffset_.has_value() && Negative(layoutInfo_->extraOffset_.value())) {
129             layoutInfo_->extraOffset_.reset();
130         }
131         layoutInfo_->JumpTo({ footerMainStartPos_, footerMainSize_ });
132     }
133     if (matchChildren) {
134         mainSize_ = layoutInfo_->GetMaxMainHeight() + footerMainSize_;
135         idealSize.SetMainSize(mainSize_, axis_);
136         AddPaddingToSize(layoutProperty->CreatePaddingAndBorder(), idealSize);
137         layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
138     }
139     layoutInfo_->lastMainSize_ = mainSize_;
140 
141     const int32_t cacheCnt = layoutProperty->GetCachedCountValue(layoutInfo_->defCachedCount_);
142     layoutWrapper->SetCacheCount(cacheCnt);
143     if (layoutProperty->GetShowCachedItemsValue(false)) {
144         SyncPreloadItems(layoutWrapper, layoutInfo_, cacheCnt);
145     } else {
146         PreloadItems(layoutWrapper, layoutInfo_, cacheCnt);
147     }
148 }
149 
MeasureToTarget(LayoutWrapper * layoutWrapper,int32_t startFrom,std::optional<int64_t> cacheDeadline)150 bool WaterFlowLayoutAlgorithm::MeasureToTarget(
151     LayoutWrapper* layoutWrapper, int32_t startFrom, std::optional<int64_t> cacheDeadline)
152 {
153     if (layoutInfo_->targetIndex_.value() > layoutInfo_->childrenCount_) {
154         layoutInfo_->targetIndex_.reset();
155         return false;
156     }
157     auto layoutProperty = AceType::DynamicCast<WaterFlowLayoutProperty>(layoutWrapper->GetLayoutProperty());
158     int32_t currentIndex = startFrom;
159     auto position = GetItemPosition(currentIndex);
160     if (layoutInfo_->targetIndex_.value() == LAST_ITEM) {
161         layoutInfo_->targetIndex_ = layoutInfo_->childrenCount_ - 1;
162     }
163     while (layoutInfo_->targetIndex_.has_value() && (startFrom < layoutInfo_->targetIndex_.value())) {
164         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(
165             GetChildIndexWithFooter(currentIndex), !cacheDeadline, cacheDeadline.has_value());
166         if (!itemWrapper) {
167             layoutInfo_->targetIndex_.reset();
168             return false;
169         }
170         auto itemCrossSize = itemsCrossSize_.find(position.crossIndex);
171         if (itemCrossSize == itemsCrossSize_.end()) {
172             return false;
173         }
174         itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint(
175             { itemCrossSize->second, mainSize_, axis_ }, layoutProperty, itemWrapper));
176         if (cacheDeadline) {
177             itemWrapper->Layout();
178             itemWrapper->SetActive(false);
179         }
180         auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
181         auto itemHeight = GetMainAxisSize(itemSize, axis_);
182         auto item = layoutInfo_->items_[0][position.crossIndex].find(currentIndex);
183         if (item == layoutInfo_->items_[0][position.crossIndex].end()) {
184             layoutInfo_->items_[0][position.crossIndex][currentIndex] =
185                 std::make_pair(position.startMainPos, itemHeight);
186         } else {
187             if (item->second.second != itemHeight) {
188                 item->second.second = itemHeight;
189                 layoutInfo_->ClearCacheAfterIndex(currentIndex);
190                 TAG_LOGD(AceLogTag::ACE_WATERFLOW, "item size changed");
191             }
192         }
193         if (layoutInfo_->targetIndex_.value() == currentIndex) {
194             layoutInfo_->targetIndex_.reset();
195         }
196         currentIndex++;
197         position = GetItemPosition(currentIndex);
198         if (cacheDeadline && GetSysTimestamp() > *cacheDeadline) {
199             break;
200         }
201     }
202     return true;
203 }
204 
Layout(LayoutWrapper * layoutWrapper)205 void WaterFlowLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
206 {
207     if (skipMeasure_) {
208         skipMeasure_ = false;
209         return;
210     }
211     auto layoutProperty = AceType::DynamicCast<WaterFlowLayoutProperty>(layoutWrapper->GetLayoutProperty());
212     const int32_t cachedCount = layoutProperty->GetCachedCountValue(layoutInfo_->defCachedCount_);
213 
214     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
215     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
216     MinusPaddingToSize(padding, size);
217     auto childFrameOffset = OffsetF(padding.left.value_or(0.0f), padding.top.value_or(0.0f));
218     layoutInfo_->UpdateStartIndex();
219     const bool showCache = layoutProperty->GetShowCachedItemsValue(false);
220     if (!layoutProperty->HasCachedCount()) {
221         layoutInfo_->UpdateDefaultCachedCount();
222     }
223 
224     auto firstIndex = layoutInfo_->endIndex_;
225     auto crossSize = size.CrossSize(axis_);
226     auto layoutDirection = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection();
227     const bool isRtl = layoutDirection == TextDirection::RTL && axis_ == Axis::VERTICAL;
228     for (const auto& mainPositions : layoutInfo_->items_[0]) {
229         for (const auto& item : mainPositions.second) {
230             if (item.first < layoutInfo_->startIndex_ - cachedCount ||
231                 item.first > layoutInfo_->endIndex_ + cachedCount) {
232                 continue;
233             }
234             auto itemCrossPosition = itemsCrossPosition_.find(mainPositions.first);
235             if (itemCrossPosition == itemsCrossPosition_.end()) {
236                 return;
237             }
238             auto currentOffset = childFrameOffset;
239             auto crossOffset = itemCrossPosition->second;
240             auto mainOffset = item.second.first + layoutInfo_->currentOffset_;
241             if (isRtl) {
242                 crossOffset = crossSize - crossOffset - itemsCrossSize_.at(mainPositions.first);
243             }
244             if (layoutProperty->IsReverse()) {
245                 mainOffset = mainSize_ - item.second.second - mainOffset;
246             }
247             if (axis_ == Axis::VERTICAL) {
248                 currentOffset += OffsetF(crossOffset, mainOffset);
249             } else {
250                 currentOffset += OffsetF(mainOffset, crossOffset);
251             }
252             const bool inCacheRange = item.first < layoutInfo_->startIndex_ || item.first > layoutInfo_->endIndex_;
253             const bool isCache = !showCache && inCacheRange;
254             auto wrapper = layoutWrapper->GetChildByIndex(GetChildIndexWithFooter(item.first), isCache);
255             if (!wrapper) {
256                 continue;
257             }
258             wrapper->GetGeometryNode()->SetMarginFrameOffset(currentOffset);
259 
260             if (isCache) {
261                 continue;
262             }
263             if (wrapper->CheckNeedForceMeasureAndLayout()) {
264                 wrapper->Layout();
265             } else {
266                 wrapper->GetHostNode()->ForceSyncGeometryNode();
267             }
268             // recode restore info
269             if (item.first == layoutInfo_->startIndex_) {
270                 layoutInfo_->storedOffset_ = mainOffset;
271             }
272 
273             if (!inCacheRange && NonNegative(mainOffset + item.second.second)) {
274                 firstIndex = std::min(firstIndex, item.first);
275             }
276             auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
277             if (frameNode) {
278                 frameNode->MarkAndCheckNewOpIncNode();
279             }
280         }
281     }
282     layoutInfo_->firstIndex_ = firstIndex;
283     layoutWrapper->SetActiveChildRange(layoutInfo_->NodeIdx(layoutInfo_->FirstIdx()),
284         layoutInfo_->NodeIdx(layoutInfo_->endIndex_), cachedCount, cachedCount, showCache);
285 
286     LayoutFooter(layoutWrapper, childFrameOffset, layoutProperty->IsReverse());
287 }
288 
LayoutFooter(LayoutWrapper * layoutWrapper,const OffsetF & childFrameOffset,bool reverse)289 void WaterFlowLayoutAlgorithm::LayoutFooter(LayoutWrapper* layoutWrapper, const OffsetF& childFrameOffset, bool reverse)
290 {
291     if (layoutInfo_->footerIndex_ < 0) {
292         return;
293     }
294     if (layoutInfo_->itemEnd_) {
295         auto footer = layoutWrapper->GetOrCreateChildByIndex(layoutInfo_->footerIndex_);
296         CHECK_NULL_VOID(footer);
297         auto footerOffset = childFrameOffset;
298         auto mainOffset = layoutInfo_->GetMaxMainHeight() + layoutInfo_->currentOffset_;
299         if (reverse) {
300             mainOffset = mainSize_ - footerMainSize_ - mainOffset;
301         }
302         footerOffset += (axis_ == Axis::VERTICAL) ? OffsetF(0, mainOffset) : OffsetF(mainOffset, 0);
303         footer->GetGeometryNode()->SetMarginFrameOffset(footerOffset);
304         footer->Layout();
305     } else {
306         auto footer = layoutWrapper->GetChildByIndex(layoutInfo_->footerIndex_);
307         CHECK_NULL_VOID(footer);
308         footer->SetActive(false);
309     }
310 }
311 
GetItemPosition(int32_t index)312 FlowItemPosition WaterFlowLayoutAlgorithm::GetItemPosition(int32_t index)
313 {
314     auto crossIndex = layoutInfo_->GetCrossIndex(index);
315     // already in layoutInfo
316     if (crossIndex != -1) {
317         return { crossIndex, layoutInfo_->GetStartMainPos(crossIndex, index) };
318     }
319     auto itemIndex = layoutInfo_->GetCrossIndexForNextItem(layoutInfo_->GetSegment(index));
320     if (itemIndex.lastItemIndex < 0) {
321         return { itemIndex.crossIndex, 0.0f };
322     }
323     auto mainHeight = layoutInfo_->GetMainHeight(itemIndex.crossIndex, itemIndex.lastItemIndex);
324     return { itemIndex.crossIndex, mainHeight + mainGap_ };
325 }
326 
FillViewport(float mainSize,LayoutWrapper * layoutWrapper)327 void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layoutWrapper)
328 {
329     if (layoutInfo_->currentOffset_ >= 0) {
330         if (!canOverScroll_) {
331             layoutInfo_->currentOffset_ = 0;
332         }
333         layoutInfo_->itemStart_ = true;
334     } else {
335         layoutInfo_->itemStart_ = false;
336     }
337 
338     layoutInfo_->UpdateStartIndex();
339     auto layoutProperty = AceType::DynamicCast<WaterFlowLayoutProperty>(layoutWrapper->GetLayoutProperty());
340     auto currentIndex = layoutInfo_->startIndex_;
341     auto position = GetItemPosition(currentIndex);
342     bool fill = false;
343     while (
344         LessNotEqual(position.startMainPos + layoutInfo_->currentOffset_, mainSize) || layoutInfo_->jumpIndex_ >= 0) {
345         auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(GetChildIndexWithFooter(currentIndex));
346         if (!itemWrapper) {
347             break;
348         }
349         auto itemCrossSize = itemsCrossSize_.find(position.crossIndex);
350         if (itemCrossSize == itemsCrossSize_.end()) {
351             break;
352         }
353         itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint(
354             { itemCrossSize->second, mainSize_, axis_ }, layoutProperty, itemWrapper));
355         auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
356         auto itemHeight = GetMainAxisSize(itemSize, axis_);
357         auto item = layoutInfo_->items_[0][position.crossIndex].find(currentIndex);
358         if (item == layoutInfo_->items_[0][position.crossIndex].end()) {
359             layoutInfo_->items_[0][position.crossIndex][currentIndex] =
360                 std::make_pair(position.startMainPos, itemHeight);
361         } else {
362             if (item->second.second != itemHeight) {
363                 TAG_LOGI(AceLogTag::ACE_WATERFLOW,
364                     "item size change. currentIdx:%{public}d,cacheHeight:%{public}f,itemHeight:%{public}f",
365                     currentIndex, item->second.second, itemHeight);
366                 item->second.second = itemHeight;
367                 layoutInfo_->ClearCacheAfterIndex(currentIndex);
368             }
369         }
370         if (layoutInfo_->jumpIndex_ == currentIndex) {
371             layoutInfo_->currentOffset_ =
372                 layoutInfo_->JumpToTargetAlign(layoutInfo_->items_[0][position.crossIndex][currentIndex]);
373             layoutInfo_->currentOffset_ += layoutInfo_->restoreOffset_;
374             // restoreOffSet only be used once
375             layoutInfo_->restoreOffset_ = 0.0f;
376             layoutInfo_->align_ = ScrollAlign::START;
377             layoutInfo_->jumpIndex_ = EMPTY_JUMP_INDEX;
378             layoutInfo_->itemStart_ = false;
379             if (layoutInfo_->extraOffset_.has_value()) {
380                 layoutInfo_->currentOffset_ += layoutInfo_->extraOffset_.value();
381                 layoutInfo_->extraOffset_.reset();
382             }
383         }
384         position = GetItemPosition(++currentIndex);
385         fill = true;
386     }
387     layoutInfo_->endIndex_ = !fill ? currentIndex : currentIndex - 1;
388 
389     layoutInfo_->itemEnd_ = GetChildIndexWithFooter(currentIndex) == layoutInfo_->childrenCount_;
390     if (layoutInfo_->itemEnd_) {
391         ModifyCurrentOffsetWhenReachEnd(mainSize, layoutWrapper);
392     } else {
393         layoutInfo_->offsetEnd_ = false;
394     }
395 }
396 
ModifyCurrentOffsetWhenReachEnd(float mainSize,LayoutWrapper * layoutWrapper)397 void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, LayoutWrapper* layoutWrapper)
398 {
399     auto maxItemHeight = layoutInfo_->GetMaxMainHeight();
400     if (layoutInfo_->footerIndex_ >= 0) {
401         footerMainStartPos_ = maxItemHeight;
402         footerMainSize_ = WaterFlowLayoutUtils::MeasureFooter(layoutWrapper, axis_);
403         maxItemHeight += footerMainSize_;
404     }
405     layoutInfo_->maxHeight_ = maxItemHeight;
406 
407     if (mainSize >= maxItemHeight) {
408         if (!canOverScroll_) {
409             layoutInfo_->currentOffset_ = 0;
410         }
411         layoutInfo_->itemStart_ = GreatOrEqual(layoutInfo_->currentOffset_, 0.0f);
412         layoutInfo_->offsetEnd_ = LessOrEqual(layoutInfo_->currentOffset_, 0.0f);
413         return;
414     }
415 
416     if (LessOrEqual(layoutInfo_->currentOffset_ + maxItemHeight, mainSize)) {
417         layoutInfo_->offsetEnd_ = true;
418         if (!canOverScroll_) {
419             layoutInfo_->currentOffset_ = mainSize - maxItemHeight;
420         }
421 
422         auto oldStart = layoutInfo_->startIndex_;
423         layoutInfo_->UpdateStartIndex();
424         // lazyforeach
425         for (auto i = oldStart; i >= layoutInfo_->startIndex_; i--) {
426             auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(GetChildIndexWithFooter(i));
427             CHECK_NULL_VOID(itemWrapper);
428             auto layoutProperty = AceType::DynamicCast<WaterFlowLayoutProperty>(layoutWrapper->GetLayoutProperty());
429             float crossSize = itemsCrossSize_.at(layoutInfo_->GetCrossIndex(i));
430             itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint(
431                 { crossSize, mainSize_, axis_ }, layoutProperty, itemWrapper));
432         }
433     } else {
434         layoutInfo_->offsetEnd_ = false;
435     }
436 }
437 
PreloadItem(LayoutWrapper * host,int32_t itemIdx,int64_t deadline)438 bool WaterFlowLayoutAlgorithm::PreloadItem(LayoutWrapper* host, int32_t itemIdx, int64_t deadline)
439 {
440     const int32_t lastItem = layoutInfo_->GetLastItem();
441     if (itemIdx <= lastItem) {
442         return host->GetOrCreateChildByIndex(itemIdx, false, true);
443     }
444     const auto sub = layoutInfo_->targetIndex_;
445     layoutInfo_->targetIndex_ = itemIdx;
446     const bool res = MeasureToTarget(host, lastItem, deadline);
447     layoutInfo_->targetIndex_ = sub;
448     return res;
449 }
450 
SyncPreloadItem(LayoutWrapper * host,int32_t itemIdx)451 void WaterFlowLayoutAlgorithm::SyncPreloadItem(LayoutWrapper* host, int32_t itemIdx)
452 {
453     const int32_t lastItem = layoutInfo_->GetLastItem();
454     if (itemIdx <= lastItem) {
455         auto pos = GetItemPosition(itemIdx);
456         auto item = host->GetOrCreateChildByIndex(GetChildIndexWithFooter(itemIdx));
457         CHECK_NULL_VOID(item);
458         auto itemCrossSize = itemsCrossSize_.find(pos.crossIndex);
459         if (itemCrossSize == itemsCrossSize_.end()) {
460             return;
461         }
462         item->Measure(WaterFlowLayoutUtils::CreateChildConstraint({ itemCrossSize->second, mainSize_, axis_ },
463             DynamicCast<WaterFlowLayoutProperty>(host->GetLayoutProperty()), item));
464     } else {
465         layoutInfo_->targetIndex_ = itemIdx;
466         MeasureToTarget(host, lastItem, std::nullopt);
467         layoutInfo_->targetIndex_.reset();
468     }
469 }
470 } // namespace OHOS::Ace::NG
471