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