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