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_info_sw.h"
16 
17 #include <algorithm>
18 #include <cstdint>
19 
20 #include "base/utils/utils.h"
21 
22 namespace OHOS::Ace::NG {
Sync(int32_t itemCnt,float mainSize,const std::vector<float> & mainGap)23 void WaterFlowLayoutInfoSW::Sync(int32_t itemCnt, float mainSize, const std::vector<float>& mainGap)
24 {
25     if (lanes_.empty()) {
26         return;
27     }
28     startIndex_ = StartIndex();
29     endIndex_ = EndIndex();
30     if (startIndex_ > endIndex_) {
31         SyncOnEmptyLanes();
32         return;
33     }
34     const auto* startLane = GetLane(startIndex_);
35     CHECK_NULL_VOID(startLane);
36     storedOffset_ = startLane->startPos;
37 
38     delta_ = 0.0f;
39     lastMainSize_ = mainSize;
40     mainGap_ = mainGap;
41     startPos_ = StartPos();
42     endPos_ = EndPos();
43 
44     itemStart_ = startIndex_ == 0 && NonNegative(startPos_ - TopMargin());
45     itemEnd_ = endIndex_ == itemCnt - 1;
46     if (footerIndex_ == 0) {
47         itemEnd_ &= LessOrEqual(endPos_, mainSize);
48     }
49     offsetEnd_ = itemEnd_ && LessOrEqual(endPos_ + footerHeight_ + BotMargin(), mainSize);
50     maxHeight_ = std::max(endPos_ - startPos_ + footerHeight_, maxHeight_);
51 
52     if (!itemEnd_) {
53         footerHeight_ = 0.0f;
54     }
55 
56     newStartIndex_ = EMPTY_NEW_START_INDEX;
57 
58     synced_ = true;
59 }
60 
CalibrateOffset()61 float WaterFlowLayoutInfoSW::CalibrateOffset()
62 {
63     if (startIndex_ == 0) {
64         // can calibrate totalOffset when at top
65         const float prev = totalOffset_;
66         totalOffset_ = startPos_ - TopMargin();
67         return totalOffset_ - prev;
68     }
69     return 0.0f;
70 }
71 
DistanceToTop(int32_t itemIdx,float mainGap) const72 float WaterFlowLayoutInfoSW::DistanceToTop(int32_t itemIdx, float mainGap) const
73 {
74     if (!ItemInView(itemIdx)) {
75         return 0.0f;
76     }
77     const auto* lane = GetLane(itemIdx);
78     CHECK_NULL_RETURN(lane, 0.0f);
79     float dist = lane->startPos;
80     for (const auto& item : lane->items_) {
81         if (item.idx == itemIdx) {
82             break;
83         }
84         dist += item.mainSize + mainGap;
85     }
86     return dist;
87 }
88 
DistanceToBottom(int32_t itemIdx,float mainSize,float mainGap) const89 float WaterFlowLayoutInfoSW::DistanceToBottom(int32_t itemIdx, float mainSize, float mainGap) const
90 {
91     if (!ItemInView(itemIdx)) {
92         return 0.0f;
93     }
94     const auto* lane = GetLane(itemIdx);
95     CHECK_NULL_RETURN(lane, 0.0f);
96     float dist = mainSize - lane->endPos;
97     for (auto item = lane->items_.rbegin(); item != lane->items_.rend(); ++item) {
98         if (item->idx == itemIdx) {
99             break;
100         }
101         dist += item->mainSize + mainGap;
102     }
103     return dist;
104 }
105 
OutOfBounds() const106 bool WaterFlowLayoutInfoSW::OutOfBounds() const
107 {
108     if (lanes_.empty()) {
109         return false;
110     }
111     // checking first lane is enough because re-align automatically happens when reaching start
112     if (itemStart_ && !lanes_[0].empty() && Positive(lanes_[0][0].startPos - TopMargin())) {
113         return true;
114     }
115     if (!itemStart_ && offsetEnd_) {
116         return std::all_of(lanes_.back().begin(), lanes_.back().end(), [this](const Lane& lane) {
117             return LessNotEqual(lane.endPos + footerHeight_ + BotMargin(), lastMainSize_);
118         });
119     }
120     return false;
121 }
122 
GetOverScrolledDelta(float delta) const123 OverScrollOffset WaterFlowLayoutInfoSW::GetOverScrolledDelta(float delta) const
124 {
125     OverScrollOffset res {};
126     if (lanes_.empty()) {
127         return res;
128     }
129 
130     if (startIndex_ == 0) {
131         float disToTop = -StartPosWithMargin();
132         if (!itemStart_) {
133             res.start = std::max(0.0f, delta - disToTop);
134         } else if (Positive(delta)) {
135             res.start = delta;
136         } else {
137             res.start = std::max(delta, disToTop);
138         }
139     }
140 
141     if (!itemEnd_) {
142         return res;
143     }
144 
145     float disToBot = EndPosWithMargin() + footerHeight_ - std::min(lastMainSize_, maxHeight_);
146     if (Positive(disToBot) && LessNotEqual(maxHeight_, lastMainSize_)) {
147         res.end = std::min(0.0f, disToBot + delta);
148         return res;
149     }
150     if (!offsetEnd_) {
151         res.end = std::min(0.0f, disToBot + delta);
152     } else if (Negative(delta)) {
153         res.end = delta;
154     } else {
155         res.end = std::min(delta, -disToBot);
156     }
157     return res;
158 }
159 
CalcOverScroll(float mainSize,float delta) const160 float WaterFlowLayoutInfoSW::CalcOverScroll(float mainSize, float delta) const
161 {
162     if (lanes_.empty()) {
163         return 0.0f;
164     }
165     float res = 0.0f;
166     if (itemStart_) {
167         res = StartPosWithMargin() + delta;
168     }
169     if (offsetEnd_) {
170         res = mainSize - (EndPosWithMargin() + footerHeight_ + delta);
171     }
172     return res;
173 }
174 
175 namespace {
176 using Lane = WaterFlowLayoutInfoSW::Lane;
SectionEmpty(const std::vector<WaterFlowLayoutInfoSW::Lane> & section)177 inline bool SectionEmpty(const std::vector<WaterFlowLayoutInfoSW::Lane>& section)
178 {
179     return section.empty() ||
180            std::all_of(section.begin(), section.end(), [](const auto& lane) { return lane.items_.empty(); });
181 }
SectionEndPos(const std::vector<WaterFlowLayoutInfoSW::Lane> & section)182 inline float SectionEndPos(const std::vector<WaterFlowLayoutInfoSW::Lane>& section)
183 {
184     if (section.empty()) {
185         return 0.0f;
186     }
187     return std::max_element(section.begin(), section.end(), [](const Lane& left, const Lane& right) {
188         return LessNotEqual(left.endPos, right.endPos);
189     })->endPos;
190 }
191 
SectionStartPos(const std::vector<WaterFlowLayoutInfoSW::Lane> & section)192 inline float SectionStartPos(const std::vector<WaterFlowLayoutInfoSW::Lane>& section)
193 {
194     if (section.empty()) {
195         return 0.0f;
196     }
197     return std::min_element(section.begin(), section.end(), [](const Lane& left, const Lane& right) {
198         return LessNotEqual(left.startPos, right.startPos);
199     })->startPos;
200 }
201 } // namespace
202 
EndPos() const203 float WaterFlowLayoutInfoSW::EndPos() const
204 {
205     if (synced_) {
206         return endPos_;
207     }
208     for (auto it = lanes_.rbegin(); it != lanes_.rend(); ++it) {
209         if (SectionEmpty(*it)) {
210             continue;
211         }
212         return SectionEndPos(*it);
213     }
214     return 0.0f;
215 }
216 
StartPos() const217 float WaterFlowLayoutInfoSW::StartPos() const
218 {
219     if (synced_) {
220         return startPos_;
221     }
222     for (const auto& section : lanes_) {
223         if (SectionEmpty(section)) {
224             continue;
225         }
226         return SectionStartPos(section);
227     }
228     return 0.0f;
229 }
230 
ReachStart(float prevPos,bool firstLayout) const231 bool WaterFlowLayoutInfoSW::ReachStart(float prevPos, bool firstLayout) const
232 {
233     if (!itemStart_ || lanes_.empty()) {
234         return false;
235     }
236     return firstLayout || Negative(prevPos);
237 }
238 
ReachEnd(float prevPos,bool firstLayout) const239 bool WaterFlowLayoutInfoSW::ReachEnd(float prevPos, bool firstLayout) const
240 {
241     if (!offsetEnd_ || lanes_.empty()) {
242         return false;
243     }
244     float prevEndPos = EndPos() - (totalOffset_ - prevPos);
245     return firstLayout || GreatNotEqual(prevEndPos + footerHeight_, lastMainSize_);
246 }
247 
GetContentHeight() const248 float WaterFlowLayoutInfoSW::GetContentHeight() const
249 {
250     // only height in view are remembered
251     return maxHeight_;
252 }
253 
GetMainCount() const254 int32_t WaterFlowLayoutInfoSW::GetMainCount() const
255 {
256     if (lanes_.empty()) {
257         return 0;
258     }
259     for (const auto& section : lanes_) {
260         if (SectionEmpty(section)) {
261             continue;
262         }
263         return static_cast<int32_t>(
264             std::max_element(section.begin(), section.end(), [](const Lane& left, const Lane& right) {
265                 return left.items_.size() < right.items_.size();
266             })->items_.size());
267     }
268     return 0;
269 }
270 
CalcTargetPosition(int32_t idx,int32_t) const271 float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx */) const
272 {
273     if (!ItemInView(idx)) {
274         return Infinity<float>();
275     }
276     const auto* lane = GetLane(idx);
277     CHECK_NULL_RETURN(lane, Infinity<float>());
278     float pos = 0.0f; // main-axis position of the item's top edge relative to viewport top. Positive if below viewport
279     float itemSize = 0.0f;
280     if (idx < endIndex_) {
281         pos = DistanceToTop(idx, mainGap_[GetSegment(idx)]);
282         auto it = std::find_if(
283             lane->items_.begin(), lane->items_.end(), [idx](const ItemInfo& item) { return item.idx == idx; });
284         if (it == lane->items_.end()) {
285             return Infinity<float>();
286         }
287         itemSize = it->mainSize;
288     } else {
289         if (lane->items_.empty()) {
290             return Infinity<float>();
291         }
292         itemSize = lane->items_.back().mainSize;
293         pos = lane->endPos - itemSize;
294     }
295     switch (align_) {
296         case ScrollAlign::START:
297             break;
298         case ScrollAlign::END:
299             pos = pos - lastMainSize_ + itemSize;
300             break;
301         case ScrollAlign::AUTO:
302             if (Negative(pos)) {
303                 /* */
304             } else if (GreatNotEqual(pos + itemSize, lastMainSize_)) {
305                 pos = pos - lastMainSize_ + itemSize;
306             } else {
307                 pos = 0.0f; // already in viewport, no movement needed
308             }
309             break;
310         case ScrollAlign::CENTER:
311             pos = pos - (lastMainSize_ - itemSize) / 2.0f;
312             break;
313         default:
314             pos = 0.0f;
315             break;
316     }
317     // convert to absolute position
318     return pos - totalOffset_;
319 }
320 
PrepareJump()321 void WaterFlowLayoutInfoSW::PrepareJump()
322 {
323     if (startIndex_ > endIndex_) {
324         return;
325     }
326     align_ = ScrollAlign::START;
327     jumpIndex_ = startIndex_;
328     delta_ = storedOffset_;
329 }
330 
Reset()331 void WaterFlowLayoutInfoSW::Reset()
332 {
333     PrepareJump();
334     for (auto& section : lanes_) {
335         for (auto& lane : section) {
336             lane.items_.clear();
337         }
338     }
339     idxToLane_.clear();
340     maxHeight_ = 0.0f;
341     synced_ = false;
342 }
343 
EndIndex() const344 int32_t WaterFlowLayoutInfoSW::EndIndex() const
345 {
346     if (synced_) {
347         return endIndex_;
348     }
349     int32_t maxIdx = -1;
350     for (auto it = lanes_.rbegin(); it != lanes_.rend(); ++it) {
351         bool flag = false;
352         for (const auto& lane : *it) {
353             if (lane.items_.empty()) {
354                 continue;
355             }
356             flag = true;
357             maxIdx = std::max(maxIdx, lane.items_.back().idx);
358         }
359         if (flag) {
360             break;
361         }
362     }
363     return maxIdx;
364 }
365 
StartIndex() const366 int32_t WaterFlowLayoutInfoSW::StartIndex() const
367 {
368     if (synced_) {
369         return startIndex_;
370     }
371     auto minIdx = Infinity<int32_t>();
372     for (const auto& section : lanes_) {
373         bool flag = false;
374         for (const auto& lane : section) {
375             if (lane.items_.empty()) {
376                 continue;
377             }
378             flag = true;
379             minIdx = std::min(minIdx, lane.items_.front().idx);
380         }
381         if (flag) {
382             break;
383         }
384     }
385     return minIdx;
386 }
387 
GetCrossIndex(int32_t itemIndex) const388 int32_t WaterFlowLayoutInfoSW::GetCrossIndex(int32_t itemIndex) const
389 {
390     if (!ItemInView(itemIndex)) {
391         return -1;
392     }
393     auto it = idxToLane_.find(itemIndex);
394     if (it == idxToLane_.end()) {
395         return -1;
396     }
397     return static_cast<int32_t>(it->second);
398 }
399 
ResetWithLaneOffset(std::optional<float> laneBasePos)400 void WaterFlowLayoutInfoSW::ResetWithLaneOffset(std::optional<float> laneBasePos)
401 {
402     for (auto& section : lanes_) {
403         std::for_each(section.begin(), section.end(), [&laneBasePos](auto& lane) {
404             lane.items_.clear();
405             if (laneBasePos) {
406                 lane.startPos = *laneBasePos;
407                 lane.endPos = *laneBasePos;
408             } else {
409                 lane.endPos = lane.startPos;
410             }
411         });
412     }
413     maxHeight_ = 0.0f;
414     idxToLane_.clear();
415     synced_ = false;
416 }
417 
ToString() const418 std::string WaterFlowLayoutInfoSW::Lane::ToString() const
419 {
420     std::string res = "{StartPos: " + std::to_string(startPos) + " EndPos: " + std::to_string(endPos) + " ";
421     if (items_.empty()) {
422         res += "empty";
423     } else {
424         res += "Items [";
425         for (const auto& item : items_) {
426             res += std::to_string(item.idx) + " ";
427         }
428         res += "] ";
429     }
430     res += "}";
431     return res;
432 }
433 
ItemCloseToView(int32_t idx) const434 bool WaterFlowLayoutInfoSW::ItemCloseToView(int32_t idx) const
435 {
436     if (lanes_.empty() || std::all_of(lanes_.begin(), lanes_.end(), [](const auto& lanes) { return lanes.empty(); })) {
437         return false;
438     }
439     int32_t startIdx = StartIndex();
440     int32_t endIdx = EndIndex();
441     using std::abs, std::min;
442     return min(abs(idx - endIdx), abs(idx - startIdx)) < endIdx - startIdx + 1;
443 }
444 
ClearDataFrom(int32_t idx,const std::vector<float> & mainGap)445 void WaterFlowLayoutInfoSW::ClearDataFrom(int32_t idx, const std::vector<float>& mainGap)
446 {
447     for (auto it = idxToLane_.begin(); it != idxToLane_.end();) {
448         if (it->first >= idx) {
449             it = idxToLane_.erase(it); // Erase and get the iterator to the next element
450         } else {
451             ++it;
452         }
453     }
454     if (mainGap.size() < lanes_.size()) {
455         TAG_LOGW(ACE_WATERFLOW,
456             "internal data structure is corrupted. mainGap size = %{public}zu, lanes size = %{public}zu",
457             mainGap.size(), lanes_.size());
458         return;
459     }
460     for (int32_t i = GetSegment(idx); i < static_cast<int32_t>(lanes_.size()); ++i) {
461         for (auto& lane : lanes_[i]) {
462             while (!lane.items_.empty() && lane.items_.back().idx >= idx) {
463                 lane.endPos -= lane.items_.back().mainSize + mainGap[i];
464                 lane.items_.pop_back();
465             }
466             lane.endPos = std::max(lane.endPos, lane.startPos);
467         }
468     }
469 }
470 
TopFinalPos() const471 float WaterFlowLayoutInfoSW::TopFinalPos() const
472 {
473     return -(StartPosWithMargin() + delta_);
474 };
475 
BottomFinalPos(float viewHeight) const476 float WaterFlowLayoutInfoSW::BottomFinalPos(float viewHeight) const
477 {
478     return -(EndPosWithMargin() + delta_ + footerHeight_) + std::min(maxHeight_, viewHeight);
479 };
480 
IsMisaligned() const481 bool WaterFlowLayoutInfoSW::IsMisaligned() const
482 {
483     if (lanes_.empty()) {
484         return false;
485     }
486 
487     const int32_t startIdx = StartIndex();
488     int32_t startSeg = GetSegment(startIdx);
489     if (startSeg < 0) {
490         return false;
491     }
492     if (startSeg == 0) {
493         if (startIdx > 0) {
494             ++startSeg;
495         }
496     } else if (startIdx != segmentTails_[startSeg - 1] + 1) {
497         ++startSeg;
498     }
499 
500     const int32_t endSeg = GetSegment(EndIndex());
501     for (int32_t i = startSeg; i <= endSeg; ++i) {
502         if (i >= static_cast<int32_t>(lanes_.size())) {
503             break;
504         }
505         if (SectionEmpty(lanes_[i])) {
506             continue;
507         }
508         const float startPos = SectionStartPos(lanes_[i]);
509         if (std::any_of(lanes_[i].begin(), lanes_[i].end(),
510                 [&startPos](const auto& lane) { return !NearEqual(lane.startPos, startPos); })) {
511             return true;
512         }
513         if (lanes_[i].empty() || lanes_[i][0].items_.empty()) {
514             return true;
515         }
516         const int32_t sectionStart = (i == 0) ? 0 : segmentTails_[i - 1] + 1;
517         if (sectionStart != lanes_[i][0].items_.front().idx) {
518             return true;
519         }
520     }
521     return false;
522 }
523 
InitSegments(const std::vector<WaterFlowSections::Section> & sections,int32_t start)524 void WaterFlowLayoutInfoSW::InitSegments(const std::vector<WaterFlowSections::Section>& sections, int32_t start)
525 {
526     synced_ = false;
527     const size_t n = sections.size();
528     if (n == 0) {
529         return;
530     }
531 
532     InitSegmentTails(sections);
533 
534     InitLanes(sections, start);
535 
536     margins_.clear(); // to be initialized during layout
537 }
538 
PrepareSectionPos(int32_t idx,bool fillBack)539 void WaterFlowLayoutInfoSW::PrepareSectionPos(int32_t idx, bool fillBack)
540 {
541     int32_t prevSeg = GetSegment(fillBack ? idx - 1 : idx + 1);
542     int32_t curSeg = GetSegment(idx);
543     if (prevSeg == curSeg) {
544         return;
545     }
546     if (SectionEmpty(lanes_[prevSeg])) {
547         // previous section is invalid
548         return;
549     }
550     // prepare sections below
551     if (prevSeg < curSeg) {
552         for (int32_t i = prevSeg + 1; i <= curSeg; ++i) {
553             float pos = SectionEndPos(lanes_[i - 1]);
554             pos += axis_ == Axis::VERTICAL ? margins_[i - 1].bottom.value_or(0.0f) + margins_[i].top.value_or(0.0f)
555                                            : margins_[i - 1].right.value_or(0.0f) + margins_[i].left.value_or(0.0f);
556             std::for_each(lanes_[i].begin(), lanes_[i].end(), [pos](Lane& lane) {
557                 lane.startPos = lane.endPos = pos;
558                 lane.items_.clear();
559             });
560         }
561         return;
562     }
563     // prepare sections above
564     for (int32_t i = prevSeg - 1; i >= curSeg; --i) {
565         float pos = SectionStartPos(lanes_[i + 1]);
566         pos -= axis_ == Axis::VERTICAL ? margins_[i + 1].top.value_or(0.0f) + margins_[i].bottom.value_or(0.0f)
567                                        : margins_[i + 1].left.value_or(0.0f) + margins_[i].right.value_or(0.0f);
568         float diff = SectionEndPos(lanes_[i]) - pos;
569         if (NearZero(diff)) {
570             continue;
571         }
572         // use subtraction to keep the end positions staggered
573         std::for_each(lanes_[i].begin(), lanes_[i].end(), [diff](Lane& lane) {
574             lane.endPos -= diff;
575             lane.startPos = lane.endPos;
576             lane.items_.clear();
577         });
578     }
579 }
580 
InitSegmentTails(const std::vector<WaterFlowSections::Section> & sections)581 void WaterFlowLayoutInfoSW::InitSegmentTails(const std::vector<WaterFlowSections::Section>& sections)
582 {
583     const size_t n = sections.size();
584     segmentCache_.clear();
585     if (n == 0) {
586         segmentTails_.clear();
587         return;
588     }
589     segmentTails_ = { sections[0].itemsCount - 1 };
590     for (size_t i = 1; i < n; ++i) {
591         segmentTails_.push_back(segmentTails_[i - 1] + sections[i].itemsCount);
592     }
593 }
594 
InitLanes(const std::vector<WaterFlowSections::Section> & sections,int32_t start)595 void WaterFlowLayoutInfoSW::InitLanes(const std::vector<WaterFlowSections::Section>& sections, int32_t start)
596 {
597     const size_t n = sections.size();
598 
599     start = std::min(start, static_cast<int32_t>(n));
600     const int32_t lastValidIdx = start > 0 ? segmentTails_[start - 1] : -1;
601     if (lastValidIdx < endIndex_) {
602         PrepareJump();
603     }
604 
605     lanes_.resize(n);
606     for (size_t i = static_cast<size_t>(start); i < n; ++i) {
607         lanes_[i] = std::vector<Lane>(sections[i].crossCount.value_or(1));
608     }
609 
610     for (auto it = idxToLane_.begin(); it != idxToLane_.end();) {
611         if (it->first > lastValidIdx) {
612             it = idxToLane_.erase(it);
613         } else {
614             ++it;
615         }
616     }
617 }
618 
InitSegmentsForKeepPositionMode(const std::vector<WaterFlowSections::Section> & sections,const std::vector<WaterFlowSections::Section> & prevSections,int32_t start)619 void WaterFlowLayoutInfoSW::InitSegmentsForKeepPositionMode(const std::vector<WaterFlowSections::Section>& sections,
620     const std::vector<WaterFlowSections::Section>& prevSections, int32_t start)
621 {
622     synced_ = false;
623     const size_t n = sections.size();
624     if (n == 0) {
625         ClearData();
626         return;
627     }
628 
629     WaterFlowSections::Section prevSection;
630     auto prevSegIdx = GetSegment(startIndex_);
631     if (prevSections.size() > static_cast<size_t>(prevSegIdx)) {
632         prevSection = prevSections[prevSegIdx];
633     }
634 
635     InitSegmentTails(sections);
636 
637     if (AdjustLanes(sections, prevSection, start, prevSegIdx)) {
638         margins_.clear();
639         return;
640     }
641 
642     InitLanes(sections, start);
643 
644     margins_.clear(); // to be initialized during layout
645 }
646 
AdjustLanes(const std::vector<WaterFlowSections::Section> & sections,const WaterFlowSections::Section & prevSection,int32_t start,int32_t prevSegIdx)647 bool WaterFlowLayoutInfoSW::AdjustLanes(const std::vector<WaterFlowSections::Section>& sections,
648     const WaterFlowSections::Section& prevSection, int32_t start, int32_t prevSegIdx)
649 {
650     if (newStartIndex_ < 0) {
651         return false;
652     }
653     const size_t n = sections.size();
654     const size_t curSegIdx = static_cast<size_t>(GetSegment(newStartIndex_));
655     if (curSegIdx >= n) {
656         return false;
657     }
658     const auto& curSection = sections[curSegIdx];
659     if (curSection.OnlyCountDiff(prevSection)) {
660         // move old lanes_[prevSegIdx,...] to Lanes_[curSegIdx,...]
661         if (n <= lanes_.size()) {
662             // means curSegIdx <= prevSegIdx
663             for (size_t i = 0; i < curSegIdx; ++i) {
664                 lanes_[i] = std::vector<Lane>(sections[i].crossCount.value_or(1));
665             }
666             for (size_t i = curSegIdx; i < n; ++i) {
667                 lanes_[i] = lanes_[prevSegIdx++];
668             }
669             lanes_.resize(n);
670         } else {
671             // 分组增加了,means curSegIdx > prevSegIdx
672             size_t oriSize = lanes_.size() - 1;
673             lanes_.resize(n);
674             for (size_t i = n - 1; i >= curSegIdx; i--) {
675                 lanes_[i] = lanes_[oriSize--];
676             }
677             for (size_t i = 0; i < curSegIdx; ++i) {
678                 lanes_[i] = std::vector<Lane>(sections[i].crossCount.value_or(1));
679             }
680         }
681         margins_.clear();
682         return true;
683     } else {
684         newStartIndex_ = INVALID_NEW_START_INDEX;
685     }
686     return false;
687 }
688 
PrepareNewStartIndex()689 bool WaterFlowLayoutInfoSW::PrepareNewStartIndex()
690 {
691     if (newStartIndex_ == EMPTY_NEW_START_INDEX) {
692         newStartIndex_ = StartIndex();
693     }
694     if (newStartIndex_ == Infinity<int32_t>()) {
695         newStartIndex_ = INVALID_NEW_START_INDEX;
696     }
697     if (newStartIndex_ == INVALID_NEW_START_INDEX) {
698         return false;
699     }
700     return true;
701 }
702 
NotifyDataChange(int32_t index,int32_t count)703 void WaterFlowLayoutInfoSW::NotifyDataChange(int32_t index, int32_t count)
704 {
705     if (!PrepareNewStartIndex()) {
706         return;
707     }
708     // 更新的index是否在newStartIndex_上方、是否会影响newStartIndex_
709     if ((count == 0 && newStartIndex_ <= index) || (count > 0 && newStartIndex_ < index) ||
710         (count < 0 && newStartIndex_ <= index - count - 1)) {
711         newStartIndex_ = INVALID_NEW_START_INDEX;
712         return;
713     }
714     newStartIndex_ += count;
715 }
716 
UpdateLanesIndex(int32_t updateIdx)717 void WaterFlowLayoutInfoSW::UpdateLanesIndex(int32_t updateIdx)
718 {
719     idxToLane_.clear();
720     const int32_t diff = newStartIndex_ - startIndex_;
721     for (auto& section : lanes_) {
722         for (size_t i = 0; i < section.size(); i++) {
723             for (auto& item : section[i].items_) {
724                 item.idx += diff;
725                 idxToLane_[item.idx] = i;
726             }
727         }
728     }
729     startIndex_ = StartIndex();
730     endIndex_ = EndIndex();
731 }
732 
BeginCacheUpdate()733 void WaterFlowLayoutInfoSW::BeginCacheUpdate()
734 {
735     savedLanes_ = std::make_unique<decltype(lanes_)>(lanes_);
736     synced_ = false;
737 }
738 
EndCacheUpdate()739 void WaterFlowLayoutInfoSW::EndCacheUpdate()
740 {
741     synced_ = true;
742     if (savedLanes_) {
743         lanes_ = std::move(*savedLanes_);
744         savedLanes_.reset();
745     }
746 }
747 
ClearData()748 void WaterFlowLayoutInfoSW::ClearData()
749 {
750     segmentCache_.clear();
751     segmentTails_.clear();
752     lanes_.clear();
753     idxToLane_.clear();
754     margins_.clear();
755     maxHeight_ = 0.0f;
756     synced_ = false;
757     startIndex_ = 0;
758     endIndex_ = -1;
759 }
760 
SyncOnEmptyLanes()761 void WaterFlowLayoutInfoSW::SyncOnEmptyLanes()
762 {
763     startPos_ = 0.0f;
764     endPos_ = 0.0f;
765     itemStart_ = true;
766     itemEnd_ = true;
767     offsetEnd_ = true;
768     maxHeight_ = footerHeight_;
769     newStartIndex_ = EMPTY_NEW_START_INDEX;
770     synced_ = true;
771 }
772 
LaneOutOfBounds(size_t laneIdx,int32_t section) const773 bool WaterFlowLayoutInfoSW::LaneOutOfBounds(size_t laneIdx, int32_t section) const
774 {
775     if (laneIdx >= lanes_[section].size()) {
776         TAG_LOGW(ACE_WATERFLOW, "lane %{public}zu is out of bounds in section %{public}d", laneIdx, section);
777         return true;
778     }
779     return false;
780 }
781 
GetMutableLane(int32_t itemIdx)782 Lane* WaterFlowLayoutInfoSW::GetMutableLane(int32_t itemIdx)
783 {
784     return const_cast<Lane*>(GetLane(itemIdx));
785 }
786 
GetLane(int32_t itemIdx) const787 const Lane* WaterFlowLayoutInfoSW::GetLane(int32_t itemIdx) const
788 {
789     if (!idxToLane_.count(itemIdx)) {
790         TAG_LOGW(ACE_WATERFLOW, "Inconsistent data found on item %{public}d", itemIdx);
791         return nullptr;
792     }
793     size_t laneIdx = idxToLane_.at(itemIdx);
794     int32_t secIdx = GetSegment(itemIdx);
795     if (LaneOutOfBounds(laneIdx, secIdx)) {
796         return nullptr;
797     }
798     auto&& lane = lanes_[secIdx][laneIdx];
799     if (lane.items_.empty()) {
800         TAG_LOGW(ACE_WATERFLOW, "Inconsistent data found on item %{public}d when accessing lane %{public}zu", itemIdx,
801             laneIdx);
802         return nullptr;
803     }
804     return &lane;
805 }
806 } // namespace OHOS::Ace::NG
807