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