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