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 #include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h"
16 
17 #include <algorithm>
18 #include <cfloat>
19 #include <queue>
20 
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/water_flow_layout_info_base.h"
25 #include "core/components_ng/pattern/waterflow/layout/water_flow_layout_utils.h"
26 #include "core/components_ng/pattern/waterflow/water_flow_pattern.h"
27 #include "core/components_ng/property/templates_parser.h"
28 
29 namespace OHOS::Ace::NG {
Measure(LayoutWrapper * wrapper)30 void WaterFlowLayoutSW::Measure(LayoutWrapper* wrapper)
31 {
32     info_->BeginUpdate();
33     wrapper_ = wrapper;
34     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper->GetLayoutProperty());
35     info_->axis_ = axis_ = props->GetAxis();
36 
37     auto [size, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_);
38     Init(size);
39     if (!IsSectionValid(info_, itemCnt_) || !CheckData()) {
40         info_->isDataValid_ = false;
41         return;
42     }
43     CheckReset();
44 
45     if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) {
46         MeasureOnJump(info_->jumpIndex_, info_->align_);
47     } else if (info_->targetIndex_) {
48         MeasureBeforeAnimation(*info_->targetIndex_);
49     } else {
50         MeasureOnOffset(info_->delta_);
51     }
52     if (matchChildren) {
53         PostMeasureSelf(size.CrossSize(axis_));
54     }
55 
56     info_->Sync(itemCnt_, mainLen_, mainGaps_);
57     if (props->GetShowCachedItemsValue(false)) {
58         SyncPreloadItems(wrapper_, info_, props->GetCachedCountValue(info_->defCachedCount_));
59     } else {
60         PreloadItems(wrapper_, info_, props->GetCachedCountValue(info_->defCachedCount_));
61     }
62 }
63 
Layout(LayoutWrapper * wrapper)64 void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper)
65 {
66     if (info_->lanes_.empty()) {
67         TAG_LOGW(AceLogTag::ACE_WATERFLOW, "Lanes not initialized, can't perform layout");
68         return;
69     }
70     if (!IsSectionValid(info_, itemCnt_) || !CheckData()) {
71         return;
72     }
73     if (info_->targetIndex_) {
74         // no item moves during MeasureToTarget tasks
75         return;
76     }
77 
78     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
79     CHECK_NULL_VOID(props);
80     const int32_t cacheCount = props->GetCachedCountValue(info_->defCachedCount_);
81     if (!props->HasCachedCount()) {
82         info_->UpdateDefaultCachedCount();
83     }
84     info_->BeginCacheUpdate();
85     RecoverCacheItems(cacheCount);
86 
87     auto padding = props->CreatePaddingAndBorder();
88     OffsetF paddingOffset { padding.left.value_or(0.0f), padding.top.value_or(0.0f) };
89 
90     const bool reverse = props->IsReverse();
91     for (size_t idx = 0; idx < info_->lanes_.size(); ++idx) {
92         LayoutSection(idx, paddingOffset, wrapper->GetGeometryNode()->GetContentSize().CrossSize(axis_), reverse,
93             props->GetNonAutoLayoutDirection() == TextDirection::RTL && axis_ == Axis::VERTICAL);
94     }
95     info_->EndCacheUpdate();
96 
97     wrapper->SetCacheCount(cacheCount);
98     wrapper->SetActiveChildRange(nodeIdx(info_->startIndex_), nodeIdx(info_->endIndex_), cacheCount, cacheCount,
99         props->GetShowCachedItemsValue(false));
100 
101     if (info_->itemEnd_) {
102         LayoutFooter(paddingOffset, reverse);
103     } else if (info_->footerIndex_ == 0) {
104         auto child = wrapper_->GetChildByIndex(0);
105         if (child) {
106             child->SetActive(false);
107         }
108     }
109 }
110 
Init(const SizeF & frameSize)111 void WaterFlowLayoutSW::Init(const SizeF& frameSize)
112 {
113     mainLen_ = frameSize.MainSize(axis_);
114     // omit footer from children count
115     itemCnt_ = info_->ItemCnt(wrapper_->GetTotalChildCount());
116     sections_ = wrapper_->GetHostNode()->GetPattern<WaterFlowPattern>()->GetSections();
117     if (sections_) {
118         const auto& sections = sections_->GetSectionInfo();
119         if (info_->segmentTails_.empty()) {
120             info_->InitSegments(sections, 0);
121         }
122         // implies section update
123         const auto& constraint = wrapper_->GetLayoutProperty()->GetLayoutConstraint();
124         if (info_->margins_.empty() && constraint) {
125             info_->InitMargins(sections, constraint->scaleProperty, constraint->percentReference.Width());
126         }
127         SegmentedInit(sections, info_->margins_, frameSize);
128     } else {
129         SingleInit(frameSize);
130     }
131 }
132 
SingleInit(const SizeF & frameSize)133 void WaterFlowLayoutSW::SingleInit(const SizeF& frameSize)
134 {
135     info_->lanes_.resize(1);
136     info_->segmentTails_ = { itemCnt_ - 1 };
137     info_->segmentCache_.clear();
138     info_->margins_.resize(1);
139 
140     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
141     const auto& constraint = props->GetLayoutConstraint();
142     CHECK_NULL_VOID(constraint);
143     auto scale = constraint->scaleProperty;
144     auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
145     auto columnsGap = ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
146     mainGaps_ = { axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap };
147     crossGaps_ = { axis_ == Axis::VERTICAL ? columnsGap : rowsGap };
148 
149     float crossSize = frameSize.CrossSize(axis_);
150     std::pair<std::vector<double>, double> cross;
151     auto rowsTemplate = props->GetRowsTemplate().value_or("1fr");
152     auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr");
153     if (axis_ == Axis::VERTICAL) {
154         cross =
155             ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGaps_[0], itemCnt_);
156     } else {
157         cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGaps_[0], itemCnt_);
158     }
159     if (cross.first.empty()) {
160         cross.first = { crossSize };
161     }
162     crossGaps_[0] = cross.second;
163 
164     itemsCrossSize_ = std::vector<std::vector<float>>(1);
165     for (const auto& len : cross.first) {
166         itemsCrossSize_[0].push_back(static_cast<float>(len));
167     }
168     info_->lanes_[0].resize(itemsCrossSize_[0].size());
169 }
170 
ItemHeightChanged() const171 bool WaterFlowLayoutSW::ItemHeightChanged() const
172 {
173     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
174     for (const auto& section : info_->lanes_) {
175         for (size_t i = 0; i < section.size(); ++i) {
176             for (const auto& item : section[i].items_) {
177                 if (!NearEqual(MeasureChild(props, item.idx, i), item.mainSize)) {
178                     return true;
179                 }
180             }
181         }
182     }
183     return false;
184 }
185 
CheckReset()186 void WaterFlowLayoutSW::CheckReset()
187 {
188     int32_t updateIdx = GetUpdateIdx(wrapper_, info_->footerIndex_);
189     if (info_->newStartIndex_ >= 0) {
190         info_->UpdateLanesIndex(updateIdx);
191         wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1);
192         return;
193     }
194     if (updateIdx > -1) {
195         wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1);
196         if (updateIdx <= info_->startIndex_) {
197             info_->ResetWithLaneOffset(std::nullopt);
198             FillBack(mainLen_, std::min(info_->startIndex_, itemCnt_ - 1), itemCnt_ - 1);
199             return;
200         }
201         info_->maxHeight_ = 0.0f;
202         info_->ClearDataFrom(updateIdx, mainGaps_);
203     }
204 
205     const bool childDirty = wrapper_->GetLayoutProperty()->GetPropertyChangeFlag() & PROPERTY_UPDATE_BY_CHILD_REQUEST;
206     if (childDirty && ItemHeightChanged()) {
207         info_->ResetWithLaneOffset(std::nullopt);
208         FillBack(mainLen_, info_->startIndex_, itemCnt_ - 1);
209         return;
210     }
211 
212     if (!wrapper_->IsConstraintNoChanged()) {
213         info_->ResetWithLaneOffset(std::nullopt);
214         FillBack(mainLen_, info_->startIndex_, itemCnt_ - 1);
215     }
216 }
217 
CheckData() const218 bool WaterFlowLayoutSW::CheckData() const
219 {
220     const size_t n = info_->segmentTails_.size();
221     if (mainGaps_.size() != n || crossGaps_.size() != n || itemsCrossSize_.size() != n || info_->margins_.size() != n) {
222         TAG_LOGW(ACE_WATERFLOW, "Internal data initialized incorrectly. Expected section size = %{public}zu", n);
223         return false;
224     }
225     if (info_->lanes_.size() != n) {
226         TAG_LOGW(ACE_WATERFLOW, "lanes initialized incorrectly. Actual = %{public}zu. Expected = %{public}zu", n,
227             info_->lanes_.size());
228         return false;
229     }
230     return true;
231 }
232 
MeasureOnOffset(float delta)233 void WaterFlowLayoutSW::MeasureOnOffset(float delta)
234 {
235     // handle initial layout
236     if (NearZero(delta) && info_->startIndex_ > info_->endIndex_) {
237         info_->ResetWithLaneOffset(info_->TopMargin());
238     }
239 
240     const bool forward = NonPositive(delta);
241     forward ? ClearBack(mainLen_) : ClearFront(); // clear items recorded during target pos calculation
242 
243     ApplyDelta(delta);
244     AdjustOverScroll();
245     // clear items that moved out of viewport
246     forward ? ClearFront() : ClearBack(mainLen_);
247 }
248 
ApplyDelta(float delta)249 void WaterFlowLayoutSW::ApplyDelta(float delta)
250 {
251     info_->totalOffset_ += delta;
252     for (auto& section : info_->lanes_) {
253         for (auto& lane : section) {
254             lane.startPos += delta;
255             lane.endPos += delta;
256         }
257     }
258 
259     if (Positive(delta)) {
260         // positive offset is scrolling upwards
261         FillFront(0.0f, info_->StartIndex() - 1, 0);
262     } else {
263         FillBack(mainLen_, info_->EndIndex() + 1, itemCnt_ - 1);
264     }
265 }
266 
MeasureBeforeAnimation(int32_t targetIdx)267 void WaterFlowLayoutSW::MeasureBeforeAnimation(int32_t targetIdx)
268 {
269     const std::pair prevRange { info_->startIndex_, info_->endIndex_ };
270     MeasureToTarget(targetIdx);
271     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
272     CHECK_NULL_VOID(props);
273     // skip Layout, only measure to calculate target position
274     const int32_t cacheCount = props->GetCachedCountValue(info_->defCachedCount_);
275     wrapper_->SetActiveChildRange(nodeIdx(prevRange.first), nodeIdx(prevRange.second), cacheCount, cacheCount,
276         props->GetShowCachedItemsValue(false));
277 }
278 
MeasureToTarget(int32_t targetIdx)279 void WaterFlowLayoutSW::MeasureToTarget(int32_t targetIdx)
280 {
281     if (itemCnt_ == 0) {
282         return;
283     }
284     if (targetIdx < info_->startIndex_) {
285         FillFront(-FLT_MAX, info_->startIndex_ - 1, targetIdx);
286     } else if (targetIdx > info_->endIndex_) {
287         FillBack(FLT_MAX, info_->endIndex_ + 1, targetIdx);
288     }
289 }
290 
291 namespace {
292 // [lane start/end position, lane index]
293 using lanePos = std::pair<float, size_t>;
294 
295 using StartPosQ = std::priority_queue<lanePos>;
296 using EndPosQ = std::priority_queue<lanePos, std::vector<lanePos>, std::greater<>>;
297 
298 using Lanes = std::vector<WaterFlowLayoutInfoSW::Lane>;
299 
PrepareStartPosQueue(StartPosQ & q,const Lanes & lanes,float mainGap,float viewportBound)300 void PrepareStartPosQueue(StartPosQ& q, const Lanes& lanes, float mainGap, float viewportBound)
301 {
302     for (size_t i = 0; i < lanes.size(); ++i) {
303         const float nextPos = lanes[i].startPos - (lanes[i].items_.empty() ? 0.0f : mainGap);
304         if (GreatNotEqual(nextPos, viewportBound)) {
305             q.push({ nextPos, i });
306         }
307     }
308 }
309 
PrepareEndPosQueue(EndPosQ & q,const Lanes & lanes,float mainGap,float viewportBound)310 void PrepareEndPosQueue(EndPosQ& q, const Lanes& lanes, float mainGap, float viewportBound)
311 {
312     for (size_t i = 0; i < lanes.size(); ++i) {
313         const float nextPos = lanes[i].endPos + (lanes[i].items_.empty() ? 0.0f : mainGap);
314         if (LessNotEqual(nextPos, viewportBound)) {
315             q.push({ nextPos, i });
316         }
317     }
318 }
319 
OverDue(const std::optional<int64_t> & deadline)320 bool OverDue(const std::optional<int64_t>& deadline)
321 {
322     return deadline && GetSysTimestamp() > *deadline;
323 }
324 } // namespace
325 
FillBack(float viewportBound,int32_t idx,int32_t maxChildIdx)326 void WaterFlowLayoutSW::FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx)
327 {
328     idx = std::max(idx, 0);
329     maxChildIdx = std::min(maxChildIdx, itemCnt_ - 1);
330 
331     info_->PrepareSectionPos(idx, true);
332     while (!FillBackSection(viewportBound, idx, maxChildIdx)) {
333         if (idx > maxChildIdx) {
334             break;
335         }
336         info_->PrepareSectionPos(idx, true);
337     }
338 }
339 
FillBackSection(float viewportBound,int32_t & idx,int32_t maxChildIdx)340 bool WaterFlowLayoutSW::FillBackSection(float viewportBound, int32_t& idx, int32_t maxChildIdx)
341 {
342     int32_t section = info_->GetSegment(idx);
343     maxChildIdx = std::min(maxChildIdx, info_->segmentTails_[section]);
344     if (info_->idxToLane_.count(idx)) {
345         RecoverBack(viewportBound, idx, maxChildIdx);
346     }
347     EndPosQ q;
348     PrepareEndPosQueue(q, info_->lanes_[section], mainGaps_[section], viewportBound);
349 
350     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
351     while (!q.empty() && idx <= maxChildIdx) {
352         if (OverDue(cacheDeadline_)) {
353             return true;
354         }
355         auto [_, laneIdx] = q.top();
356         q.pop();
357         info_->idxToLane_[idx] = laneIdx;
358         const float mainLen = MeasureChild(props, idx, laneIdx);
359         float endPos = FillBackHelper(mainLen, idx++, laneIdx);
360         if (LessNotEqual(endPos, viewportBound)) {
361             q.push({ endPos, laneIdx });
362         }
363     }
364     return q.empty();
365 }
366 
FillFront(float viewportBound,int32_t idx,int32_t minChildIdx)367 void WaterFlowLayoutSW::FillFront(float viewportBound, int32_t idx, int32_t minChildIdx)
368 {
369     idx = std::min(itemCnt_ - 1, idx);
370     minChildIdx = std::max(minChildIdx, 0);
371 
372     info_->PrepareSectionPos(idx, false);
373     while (!FillFrontSection(viewportBound, idx, minChildIdx)) {
374         if (idx < minChildIdx) {
375             break;
376         }
377         info_->PrepareSectionPos(idx, false);
378     }
379 }
380 
FillFrontSection(float viewportBound,int32_t & idx,int32_t minChildIdx)381 bool WaterFlowLayoutSW::FillFrontSection(float viewportBound, int32_t& idx, int32_t minChildIdx)
382 {
383     int32_t secIdx = info_->GetSegment(idx);
384     minChildIdx = std::max(minChildIdx, secIdx > 0 ? info_->segmentTails_[secIdx - 1] + 1 : 0);
385     if (info_->idxToLane_.count(idx)) {
386         RecoverFront(viewportBound, idx, minChildIdx);
387     }
388     StartPosQ q;
389     PrepareStartPosQueue(q, info_->lanes_[secIdx], mainGaps_[secIdx], viewportBound);
390 
391     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
392     while (!q.empty() && idx >= minChildIdx) {
393         if (OverDue(cacheDeadline_)) {
394             return true;
395         }
396         auto [_, laneIdx] = q.top();
397         q.pop();
398         info_->idxToLane_[idx] = laneIdx;
399         const float mainLen = MeasureChild(props, idx, laneIdx);
400         float startPos = FillFrontHelper(mainLen, idx--, laneIdx);
401         if (GreatNotEqual(startPos, viewportBound)) {
402             q.push({ startPos, laneIdx });
403         }
404     }
405     return q.empty();
406 }
407 
FillBackHelper(float itemLen,int32_t idx,size_t laneIdx)408 float WaterFlowLayoutSW::FillBackHelper(float itemLen, int32_t idx, size_t laneIdx)
409 {
410     int32_t secIdx = info_->GetSegment(idx);
411     if (info_->LaneOutOfBounds(laneIdx, secIdx)) {
412         return 0.0f;
413     }
414 
415     auto& lane = info_->lanes_[secIdx][laneIdx];
416     lane.endPos += mainGaps_[secIdx] + itemLen;
417     if (lane.items_.empty()) {
418         lane.endPos -= mainGaps_[secIdx];
419     }
420     lane.items_.push_back({ idx, itemLen });
421     return lane.endPos + mainGaps_[secIdx];
422 }
423 
FillFrontHelper(float itemLen,int32_t idx,size_t laneIdx)424 float WaterFlowLayoutSW::FillFrontHelper(float itemLen, int32_t idx, size_t laneIdx)
425 {
426     int32_t secIdx = info_->GetSegment(idx);
427     if (info_->LaneOutOfBounds(laneIdx, secIdx)) {
428         return 0.0f;
429     }
430 
431     auto& lane = info_->lanes_[secIdx][laneIdx];
432     lane.startPos -= mainGaps_[secIdx] + itemLen;
433     if (lane.items_.empty()) {
434         lane.startPos += mainGaps_[secIdx];
435     }
436     lane.items_.push_front({ idx, itemLen });
437     return lane.startPos - mainGaps_[secIdx];
438 }
439 
RecoverBack(float viewportBound,int32_t & idx,int32_t maxChildIdx)440 void WaterFlowLayoutSW::RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx)
441 {
442     std::unordered_set<size_t> lanes;
443     int32_t secIdx = info_->GetSegment(idx);
444     for (size_t i = 0; i < info_->lanes_[secIdx].size(); ++i) {
445         float endPos =
446             info_->lanes_[secIdx][i].endPos + (info_->lanes_[secIdx][i].items_.empty() ? 0.0f : mainGaps_[secIdx]);
447         if (LessNotEqual(endPos, viewportBound)) {
448             lanes.insert(i);
449         }
450     }
451 
452     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
453     while (!lanes.empty() && idx <= maxChildIdx && info_->idxToLane_.count(idx)) {
454         size_t laneIdx = info_->idxToLane_.at(idx);
455         const float mainLen = MeasureChild(props, idx, laneIdx);
456         float endPos = FillBackHelper(mainLen, idx++, laneIdx);
457         if (GreatOrEqual(endPos, viewportBound)) {
458             lanes.erase(laneIdx);
459         }
460         if (OverDue(cacheDeadline_)) {
461             return;
462         }
463     }
464 }
465 
RecoverFront(float viewportBound,int32_t & idx,int32_t minChildIdx)466 void WaterFlowLayoutSW::RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx)
467 {
468     std::unordered_set<size_t> lanes;
469     int32_t secIdx = info_->GetSegment(idx);
470     for (size_t i = 0; i < info_->lanes_[secIdx].size(); ++i) {
471         float startPos =
472             info_->lanes_[secIdx][i].startPos - (info_->lanes_[secIdx][i].items_.empty() ? 0.0f : mainGaps_[secIdx]);
473         if (GreatNotEqual(startPos, viewportBound)) {
474             lanes.insert(i);
475         }
476     }
477     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
478     while (!lanes.empty() && idx >= minChildIdx && info_->idxToLane_.count(idx)) {
479         size_t laneIdx = info_->idxToLane_.at(idx);
480         const float mainLen = MeasureChild(props, idx, laneIdx);
481         float startPos = FillFrontHelper(mainLen, idx--, laneIdx);
482         if (LessOrEqual(startPos, viewportBound)) {
483             lanes.erase(laneIdx);
484         }
485         if (OverDue(cacheDeadline_)) {
486             return;
487         }
488     }
489 }
490 
ClearBack(float bound)491 void WaterFlowLayoutSW::ClearBack(float bound)
492 {
493     int32_t startIdx = info_->StartIndex();
494     for (int32_t i = info_->EndIndex(); i > startIdx; --i) {
495         auto* lane = info_->GetMutableLane(i);
496         CHECK_NULL_BREAK(lane);
497         float itemStartPos = lane->endPos - lane->items_.back().mainSize;
498         if (LessNotEqual(itemStartPos, bound)) {
499             break;
500         }
501         lane->items_.pop_back();
502         lane->endPos = itemStartPos - mainGaps_[info_->GetSegment(i)];
503         if (lane->items_.empty()) {
504             lane->endPos += mainGaps_[info_->GetSegment(i)];
505         }
506     }
507 }
508 
ClearFront()509 void WaterFlowLayoutSW::ClearFront()
510 {
511     int32_t endIdx = info_->EndIndex();
512     for (int32_t i = info_->StartIndex(); i < endIdx; ++i) {
513         auto* lane = info_->GetMutableLane(i);
514         CHECK_NULL_BREAK(lane);
515         const float& itemLen = lane->items_.front().mainSize;
516         if (NearZero(itemLen) && NearZero(lane->startPos)) {
517             break;
518         }
519         const float itemEndPos = lane->startPos + itemLen;
520         if (Positive(itemEndPos)) {
521             break;
522         }
523         lane->items_.pop_front();
524         lane->startPos = itemEndPos + mainGaps_[info_->GetSegment(i)];
525         if (lane->items_.empty()) {
526             lane->startPos -= mainGaps_[info_->GetSegment(i)];
527         }
528     }
529 }
530 
ParseAutoAlign(int32_t jumpIdx,bool inView)531 ScrollAlign WaterFlowLayoutSW::ParseAutoAlign(int32_t jumpIdx, bool inView)
532 {
533     if (inView) {
534         if (Negative(info_->DistanceToTop(jumpIdx, mainGaps_[info_->GetSegment(jumpIdx)]))) {
535             return ScrollAlign::START;
536         }
537         if (Negative(info_->DistanceToBottom(jumpIdx, mainLen_, mainGaps_[info_->GetSegment(jumpIdx)]))) {
538             return ScrollAlign::END;
539         }
540         // item is already fully in viewport
541         return ScrollAlign::NONE;
542     }
543     if (jumpIdx < info_->startIndex_) {
544         return ScrollAlign::START;
545     }
546     return ScrollAlign::END;
547 }
548 
MeasureOnJump(int32_t jumpIdx,ScrollAlign align)549 void WaterFlowLayoutSW::MeasureOnJump(int32_t jumpIdx, ScrollAlign align)
550 {
551     if (jumpIdx == LAST_ITEM) {
552         jumpIdx = itemCnt_ - 1;
553     } else if (jumpIdx == itemCnt_ && info_->footerIndex_ == 0) {
554         // jump to footer
555         info_->delta_ = -Infinity<float>();
556     }
557     jumpIdx = std::min(itemCnt_ - 1, jumpIdx);
558     overScroll_ = false;
559 
560     bool inView = info_->ItemInView(jumpIdx);
561     if (align == ScrollAlign::AUTO) {
562         align = ParseAutoAlign(jumpIdx, inView);
563     }
564 
565     // If item is close, we simply scroll to it instead of triggering a reset/jump, which would change the layout.
566     bool closeToView = info_->ItemCloseToView(jumpIdx);
567     if (closeToView) {
568         MeasureToTarget(jumpIdx);
569     }
570     Jump(jumpIdx, align, inView || closeToView);
571     if (info_->extraOffset_) {
572         info_->delta_ += *info_->extraOffset_;
573     }
574     if (!NearZero(info_->delta_)) {
575         MeasureOnOffset(info_->delta_);
576     } else {
577         AdjustOverScroll();
578         ClearFront();
579         ClearBack(mainLen_);
580     }
581 }
582 
Jump(int32_t jumpIdx,ScrollAlign align,bool noSkip)583 void WaterFlowLayoutSW::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip)
584 {
585     switch (align) {
586         case ScrollAlign::START: {
587             if (noSkip) {
588                 ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGaps_[info_->GetSegment(jumpIdx)]));
589             } else {
590                 info_->ResetWithLaneOffset(0.0f);
591                 FillBack(mainLen_, jumpIdx, itemCnt_ - 1);
592             }
593             break;
594         }
595         case ScrollAlign::CENTER: {
596             auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
597             if (noSkip) {
598                 if (!info_->idxToLane_.count(jumpIdx)) {
599                     break;
600                 }
601                 float itemH = MeasureChild(props, jumpIdx, info_->idxToLane_.at(jumpIdx));
602                 ApplyDelta(
603                     -info_->DistanceToTop(jumpIdx, mainGaps_[info_->GetSegment(jumpIdx)]) + (mainLen_ - itemH) / 2.0f);
604             } else {
605                 info_->ResetWithLaneOffset(mainLen_ / 2.0f);
606                 info_->idxToLane_ = { { jumpIdx, 0 } };
607                 int32_t secIdx = info_->GetSegment(jumpIdx);
608                 if (info_->lanes_[secIdx].empty()) {
609                     break;
610                 }
611                 auto& lane = info_->lanes_[secIdx][0];
612                 float itemH = MeasureChild(props, jumpIdx, 0);
613                 lane.startPos = (mainLen_ - itemH) / 2.0f;
614                 lane.endPos = (mainLen_ + itemH) / 2.0f;
615                 lane.items_.push_back({ jumpIdx, itemH });
616 
617                 FillFront(0.0f, jumpIdx - 1, 0);
618                 FillBack(mainLen_, jumpIdx + 1, itemCnt_ - 1);
619             }
620             break;
621         }
622         case ScrollAlign::END: {
623             if (noSkip) {
624                 ApplyDelta(info_->DistanceToBottom(jumpIdx, mainLen_, mainGaps_[info_->GetSegment(jumpIdx)]));
625             } else {
626                 info_->ResetWithLaneOffset(mainLen_);
627                 FillFront(0.0f, jumpIdx, 0);
628             }
629             break;
630         }
631         default:
632             break;
633     }
634 }
635 
AdjustOverScroll()636 void WaterFlowLayoutSW::AdjustOverScroll()
637 {
638     if (info_->lanes_.empty()) {
639         return;
640     }
641     float maxEnd = info_->EndPos();
642     float minStart = info_->StartPos();
643 
644     if (LessOrEqual(maxEnd, mainLen_) && info_->footerIndex_ == 0) {
645         info_->footerHeight_ = WaterFlowLayoutUtils::MeasureFooter(wrapper_, axis_);
646         maxEnd += info_->footerHeight_;
647     }
648 
649     if (overScroll_) {
650         return;
651     }
652     maxEnd += info_->BotMargin();
653     minStart -= info_->TopMargin();
654 
655     int32_t startIdx = info_->StartIndex();
656     if (startIdx == 0 && Positive(minStart)) {
657         ApplyDelta(-minStart);
658     } else if (info_->EndIndex() == itemCnt_ - 1 && LessNotEqual(maxEnd, mainLen_)) {
659         float delta = mainLen_ - maxEnd;
660         if (startIdx == 0) {
661             delta = std::min(-minStart, delta);
662         }
663         ApplyDelta(delta);
664 
665         // handle special case when content < viewport && jump to end items
666         minStart = info_->StartPosWithMargin();
667         if (info_->StartIndex() == 0 && Positive(minStart)) {
668             ApplyDelta(-minStart);
669         }
670     }
671 }
672 
MeasureChild(const RefPtr<WaterFlowLayoutProperty> & props,int32_t idx,size_t lane) const673 float WaterFlowLayoutSW::MeasureChild(const RefPtr<WaterFlowLayoutProperty>& props, int32_t idx, size_t lane) const
674 {
675     auto child = wrapper_->GetOrCreateChildByIndex(nodeIdx(idx), !cacheDeadline_, cacheDeadline_.has_value());
676     CHECK_NULL_RETURN(child, 0.0f);
677     float userHeight = WaterFlowLayoutUtils::GetUserDefHeight(sections_, info_->GetSegment(idx), idx);
678     if (NonNegative(userHeight)) {
679         WaterFlowLayoutUtils::UpdateItemIdealSize(child, axis_, userHeight);
680     }
681     child->Measure(WaterFlowLayoutUtils::CreateChildConstraint(
682         { itemsCrossSize_[info_->GetSegment(idx)][lane], mainLen_, axis_ }, props, child));
683     if (cacheDeadline_) {
684         child->Layout();
685         child->SetActive(false);
686     }
687     return child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_);
688 }
689 
690 namespace {
MarginStart(Axis axis,const PaddingPropertyF & margin)691 float MarginStart(Axis axis, const PaddingPropertyF& margin)
692 {
693     return (axis == Axis::VERTICAL ? margin.left : margin.top).value_or(0.0f);
694 }
MarginEnd(Axis axis,const PaddingPropertyF & margin)695 float MarginEnd(Axis axis, const PaddingPropertyF& margin)
696 {
697     return (axis == Axis::VERTICAL ? margin.right : margin.bottom).value_or(0.0f);
698 }
699 } // namespace
700 
LayoutSection(size_t idx,const OffsetF & paddingOffset,float selfCrossLen,bool reverse,bool rtl)701 void WaterFlowLayoutSW::LayoutSection(
702     size_t idx, const OffsetF& paddingOffset, float selfCrossLen, bool reverse, bool rtl)
703 {
704     auto props = DynamicCast<WaterFlowLayoutProperty>(wrapper_->GetLayoutProperty());
705     CHECK_NULL_VOID(props);
706     const auto& margin = info_->margins_[idx];
707     float crossPos = rtl ? selfCrossLen + crossGaps_[idx] - MarginEnd(axis_, margin) : MarginStart(axis_, margin);
708     for (size_t i = 0; i < info_->lanes_[idx].size(); ++i) {
709         if (rtl) {
710             crossPos -= itemsCrossSize_[idx][i] + crossGaps_[idx];
711         }
712         const auto& lane = info_->lanes_[idx][i];
713         float mainPos = lane.startPos;
714         for (const auto& item : lane.items_) {
715             const bool isCache = !props->GetShowCachedItemsValue(false) &&
716                                  (item.idx < info_->startIndex_ || item.idx > info_->endIndex_);
717             auto child = wrapper_->GetChildByIndex(nodeIdx(item.idx), isCache);
718             if (!child) {
719                 mainPos += item.mainSize + mainGaps_[idx];
720                 continue;
721             }
722             auto childNode = child->GetGeometryNode();
723             auto offset = reverse ? OffsetF(crossPos, mainLen_ - item.mainSize - mainPos) : OffsetF(crossPos, mainPos);
724             if (axis_ != Axis::VERTICAL) {
725                 offset = OffsetF(offset.GetY(), offset.GetX());
726             }
727             childNode->SetMarginFrameOffset(offset + paddingOffset);
728 
729             mainPos += item.mainSize + mainGaps_[idx];
730             if (isCache) {
731                 continue;
732             }
733             if (child->CheckNeedForceMeasureAndLayout()) {
734                 child->Layout();
735             } else {
736                 child->GetHostNode()->ForceSyncGeometryNode();
737             }
738         }
739         if (!rtl) {
740             crossPos += itemsCrossSize_[idx][i] + crossGaps_[idx];
741         }
742     }
743 }
744 
LayoutFooter(const OffsetF & paddingOffset,bool reverse)745 void WaterFlowLayoutSW::LayoutFooter(const OffsetF& paddingOffset, bool reverse)
746 {
747     if (info_->footerIndex_ != 0) {
748         return;
749     }
750     float mainPos = info_->EndPos();
751     if (reverse) {
752         mainPos = mainLen_ - info_->footerHeight_ - mainPos;
753     }
754     auto footer = wrapper_->GetOrCreateChildByIndex(0);
755     CHECK_NULL_VOID(footer);
756     footer->GetGeometryNode()->SetMarginFrameOffset(
757         (axis_ == Axis::VERTICAL) ? OffsetF(0.0f, mainPos) + paddingOffset : OffsetF(mainPos, 0.0f) + paddingOffset);
758     footer->Layout();
759 }
760 
PostMeasureSelf(float selfCrossLen)761 void WaterFlowLayoutSW::PostMeasureSelf(float selfCrossLen)
762 {
763     mainLen_ = info_->GetContentHeight();
764     SizeF selfSize = (axis_ == Axis::VERTICAL) ? SizeF(selfCrossLen, mainLen_) : SizeF(mainLen_, selfCrossLen);
765     auto props = wrapper_->GetLayoutProperty();
766     AddPaddingToSize(props->CreatePaddingAndBorder(), selfSize);
767     wrapper_->GetGeometryNode()->SetFrameSize(selfSize);
768 }
769 
nodeIdx(int32_t idx) const770 inline int32_t WaterFlowLayoutSW::nodeIdx(int32_t idx) const
771 {
772     return idx + info_->footerIndex_ + 1;
773 }
774 
PreloadItem(LayoutWrapper * host,int32_t itemIdx,int64_t deadline)775 bool WaterFlowLayoutSW::PreloadItem(LayoutWrapper* host, int32_t itemIdx, int64_t deadline)
776 {
777     if (!IsSectionValid(info_, itemCnt_) || !CheckData()) {
778         return false;
779     }
780     cacheDeadline_ = deadline;
781     wrapper_ = host;
782     return PreloadItemImpl(itemIdx);
783 }
784 
SyncPreloadItem(LayoutWrapper * host,int32_t itemIdx)785 void WaterFlowLayoutSW::SyncPreloadItem(LayoutWrapper* host, int32_t itemIdx)
786 {
787     PreloadItemImpl(itemIdx);
788 }
789 
StartCacheLayout()790 void WaterFlowLayoutSW::StartCacheLayout()
791 {
792     info_->BeginCacheUpdate();
793 }
EndCacheLayout()794 void WaterFlowLayoutSW::EndCacheLayout()
795 {
796     cacheDeadline_.reset();
797     info_->EndCacheUpdate();
798 }
799 
PreloadItemImpl(int32_t itemIdx)800 bool WaterFlowLayoutSW::PreloadItemImpl(int32_t itemIdx)
801 {
802     const int32_t start = info_->StartIndex();
803     const int32_t end = info_->EndIndex();
804     if (itemIdx < start) {
805         FillFront(-FLT_MAX, start - 1, itemIdx);
806     } else if (itemIdx > end) {
807         FillBack(FLT_MAX, end + 1, itemIdx);
808     } else {
809         return false;
810     }
811     return true;
812 }
813 
RecoverCacheItems(int32_t cacheCount)814 void WaterFlowLayoutSW::RecoverCacheItems(int32_t cacheCount)
815 {
816     const int32_t minIdx = std::max(0, info_->startIndex_ - cacheCount);
817     for (int i = info_->startIndex_ - 1; i >= minIdx; --i) {
818         if (!RecoverCachedHelper(i, true)) {
819             break;
820         }
821     }
822     const int32_t maxIdx = std::min(itemCnt_ - 1, info_->endIndex_ + cacheCount);
823     for (int i = info_->endIndex_ + 1; i <= maxIdx; ++i) {
824         if (!RecoverCachedHelper(i, false)) {
825             break;
826         }
827     }
828 }
829 
RecoverCachedHelper(int32_t idx,bool front)830 bool WaterFlowLayoutSW::RecoverCachedHelper(int32_t idx, bool front)
831 {
832     auto it = info_->idxToLane_.find(idx);
833     if (it == info_->idxToLane_.end()) {
834         return false;
835     }
836     if (it->second >= info_->lanes_[info_->GetSegment(idx)].size()) {
837         TAG_LOGW(ACE_WATERFLOW, "Invalid lane index in map: %{public}zu for section: %{public}d", it->second,
838             info_->GetSegment(idx));
839         return false;
840     }
841     auto child = wrapper_->GetChildByIndex(nodeIdx(idx), true);
842     CHECK_NULL_RETURN(child, false);
843     const float mainLen = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_);
844     info_->PrepareSectionPos(idx, !front);
845     front ? FillFrontHelper(mainLen, idx, it->second) : FillBackHelper(mainLen, idx, it->second);
846     return true;
847 }
848 } // namespace OHOS::Ace::NG
849