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