1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h"
17
18 #include <algorithm>
19
20 #include "core/components_ng/property/measure_property.h"
21
22 constexpr float HALF = 0.5f;
23
24 namespace OHOS::Ace::NG {
GetCrossIndex(int32_t itemIndex) const25 int32_t WaterFlowLayoutInfo::GetCrossIndex(int32_t itemIndex) const
26 {
27 if (static_cast<size_t>(itemIndex) < itemInfos_.size()) {
28 return itemInfos_[itemIndex].crossIdx;
29 }
30 for (const auto& crossItems : items_[GetSegment(itemIndex)]) {
31 auto iter = crossItems.second.find(itemIndex);
32 if (iter != crossItems.second.end()) {
33 return crossItems.first;
34 }
35 }
36 return -1;
37 }
38
UpdateStartIndex()39 void WaterFlowLayoutInfo::UpdateStartIndex()
40 {
41 if (childrenCount_ == 0) {
42 return;
43 }
44 if (!itemInfos_.empty()) {
45 // don't use in new segmented layout
46 return;
47 }
48 auto mainHeight = GetMaxMainHeight();
49 // need more items for currentOffset_
50 if (LessOrEqual(currentOffset_ + mainHeight, 0.0f)) {
51 return;
52 }
53
54 int32_t tempStartIndex = -1;
55 for (const auto& crossItems : items_[GetSegment(tempStartIndex)]) {
56 for (const auto& iter : crossItems.second) {
57 if (GreatNotEqual(iter.second.first + iter.second.second + currentOffset_, 0.0f)) {
58 tempStartIndex = tempStartIndex != -1 ? std::min(tempStartIndex, iter.first) : iter.first;
59 break;
60 }
61 // FlowItem that have not been loaded at the beginning of each cross need to be selected as startIndex_ for
62 // the ClearCache later.
63 if (NearZero(iter.second.first + iter.second.second) && NearZero(currentOffset_)) {
64 tempStartIndex = tempStartIndex != -1 ? std::min(tempStartIndex, iter.first) : iter.first;
65 break;
66 }
67 }
68 }
69 startIndex_ = tempStartIndex == -1 ? 0 : tempStartIndex;
70 }
71
GetEndIndexByOffset(float offset) const72 int32_t WaterFlowLayoutInfo::GetEndIndexByOffset(float offset) const
73 {
74 int32_t endIndex = 0;
75 bool found = false;
76 for (const auto& crossItems : items_[GetSegment(endIndex)]) {
77 for (const auto& iter : crossItems.second) {
78 if (GreatNotEqual(iter.second.first + iter.second.second + offset, 0)) {
79 endIndex = std::max(endIndex, iter.first);
80 found = true;
81 break;
82 }
83 }
84 }
85 return found ? endIndex : -1;
86 }
87
GetMaxMainHeight() const88 float WaterFlowLayoutInfo::GetMaxMainHeight() const
89 {
90 if (!endPosArray_.empty()) {
91 const auto& margin = margins_.back();
92 return endPosArray_.back().first + (axis_ == Axis::VERTICAL ? margin.bottom : margin.right).value_or(0.0f);
93 }
94 if (items_.empty()) {
95 return 0.0f;
96 }
97 float result = 0.0f;
98 for (const auto& crossItems : *items_.rbegin()) {
99 if (crossItems.second.empty()) {
100 continue;
101 }
102 auto lastItem = crossItems.second.rbegin();
103 auto crossMainHeight = lastItem->second.first + lastItem->second.second;
104 if (NearEqual(result, 0.0f)) {
105 result = crossMainHeight;
106 }
107 if (LessNotEqual(result, crossMainHeight)) {
108 result = crossMainHeight;
109 }
110 }
111 return result;
112 }
113
GetContentHeight() const114 float WaterFlowLayoutInfo::GetContentHeight() const
115 {
116 return NearZero(maxHeight_) ? GetMaxMainHeight() : maxHeight_;
117 }
118
GetMainHeight(int32_t crossIndex,int32_t itemIndex) const119 float WaterFlowLayoutInfo::GetMainHeight(int32_t crossIndex, int32_t itemIndex) const
120 {
121 if (static_cast<size_t>(itemIndex) < itemInfos_.size() && itemInfos_[itemIndex].crossIdx == crossIndex) {
122 return itemInfos_[itemIndex].mainOffset + itemInfos_[itemIndex].mainSize;
123 }
124 auto seg = GetSegment(itemIndex);
125 float result = segmentStartPos_[seg];
126
127 auto cross = items_[seg].find(crossIndex);
128 if (cross == items_[seg].end()) {
129 return result;
130 }
131 auto item = cross->second.find(itemIndex);
132 if (item == cross->second.end()) {
133 return result;
134 }
135 result = item->second.first + item->second.second;
136 return result;
137 }
138
GetStartMainPos(int32_t crossIndex,int32_t itemIndex) const139 float WaterFlowLayoutInfo::GetStartMainPos(int32_t crossIndex, int32_t itemIndex) const
140 {
141 if (static_cast<size_t>(itemIndex) < itemInfos_.size() && itemInfos_[itemIndex].crossIdx == crossIndex) {
142 return itemInfos_[itemIndex].mainOffset;
143 }
144 float result = 0.0f;
145 auto cross = items_[GetSegment(itemIndex)].find(crossIndex);
146 if (cross == items_[GetSegment(itemIndex)].end()) {
147 return result;
148 }
149 auto item = cross->second.find(itemIndex);
150 if (item == cross->second.end()) {
151 return result;
152 }
153 result = item->second.first;
154 return result;
155 }
156
GetOverScrolledDelta(float delta) const157 OverScrollOffset WaterFlowLayoutInfo::GetOverScrolledDelta(float delta) const
158 {
159 OverScrollOffset offset = { 0, 0 };
160 if (startIndex_ == 0) {
161 auto startPos = currentOffset_;
162 auto newStartPos = startPos + delta;
163 if (startPos > 0 && newStartPos > 0) {
164 offset.start = delta;
165 }
166 if (startPos > 0 && newStartPos <= 0) {
167 offset.start = -startPos;
168 }
169 if (startPos <= 0 && newStartPos > 0) {
170 offset.start = newStartPos;
171 }
172 }
173 if (itemEnd_) {
174 auto endPos = currentOffset_ + maxHeight_;
175 if (GreatNotEqual(lastMainSize_, currentOffset_ + maxHeight_)) {
176 endPos = currentOffset_ + lastMainSize_;
177 }
178 auto newEndPos = endPos + delta;
179 if (endPos < lastMainSize_ && newEndPos < lastMainSize_) {
180 offset.end = delta;
181 }
182 if (endPos < lastMainSize_ && newEndPos >= lastMainSize_) {
183 offset.end = lastMainSize_ - endPos;
184 }
185 if (endPos >= lastMainSize_ && newEndPos < lastMainSize_) {
186 offset.end = newEndPos - lastMainSize_;
187 }
188 }
189 return offset;
190 }
191
IsAllCrossReachEnd(float mainSize) const192 bool WaterFlowLayoutInfo::IsAllCrossReachEnd(float mainSize) const
193 {
194 bool result = true;
195 for (const auto& crossItems : *items_.rbegin()) {
196 if (crossItems.second.empty()) {
197 result = false;
198 break;
199 }
200 auto lastItem = crossItems.second.rbegin();
201 auto lastOffset = lastItem->second.first + lastItem->second.second;
202 if (LessNotEqual(lastOffset + currentOffset_, mainSize)) {
203 result = false;
204 break;
205 }
206 }
207 return result;
208 }
209
GetCrossIndexForNextItem(int32_t segmentIdx) const210 FlowItemIndex WaterFlowLayoutInfo::GetCrossIndexForNextItem(int32_t segmentIdx) const
211 {
212 FlowItemIndex position = { 0, -1 };
213 auto minHeight = -1.0f;
214 auto crossSize = static_cast<int32_t>(items_[segmentIdx].size());
215 for (int32_t i = 0; i < crossSize; ++i) {
216 const auto& crossItems = items_[segmentIdx].at(i);
217 if (crossItems.empty()) {
218 position.crossIndex = i;
219 position.lastItemIndex = -1;
220 break;
221 }
222 auto lastItem = crossItems.rbegin();
223 auto lastOffset = lastItem->second.first + lastItem->second.second;
224 if (NearEqual(minHeight, -1.0f)) {
225 minHeight = lastOffset;
226 position.crossIndex = i;
227 position.lastItemIndex = lastItem->first;
228 }
229 if (LessNotEqual(lastOffset, minHeight)) {
230 position.crossIndex = i;
231 position.lastItemIndex = lastItem->first;
232 minHeight = lastOffset;
233 // first item height in this cross is 0
234 if (NearZero(minHeight)) {
235 break;
236 }
237 }
238 }
239
240 return position;
241 }
242
Reset()243 void WaterFlowLayoutInfo::Reset()
244 {
245 itemEnd_ = false;
246 itemStart_ = false;
247 offsetEnd_ = false;
248 maxHeight_ = 0.0f;
249
250 jumpIndex_ = EMPTY_JUMP_INDEX;
251
252 startIndex_ = 0;
253 endIndex_ = -1;
254 targetIndex_.reset();
255 items_ = { ItemMap() };
256 itemInfos_.clear();
257 endPosArray_.clear();
258 segmentTails_.clear();
259 margins_.clear();
260 segmentStartPos_ = { 0.0f };
261 segmentCache_.clear();
262 }
263
Reset(int32_t resetFrom)264 void WaterFlowLayoutInfo::Reset(int32_t resetFrom)
265 {
266 TAG_LOGI(AceLogTag::ACE_WATERFLOW, "reset. updateIdx:%{public}d,endIndex:%{public}d", resetFrom, endIndex_);
267 int32_t itemCount = 0;
268 for (const auto& item : items_[0]) {
269 itemCount += static_cast<int32_t>(item.second.size());
270 }
271 if (resetFrom >= itemCount) {
272 return;
273 }
274 maxHeight_ = 0.0f;
275 ClearCacheAfterIndex(resetFrom - 1);
276 startIndex_ = std::max(resetFrom - 1, 0);
277 }
278
GetCrossCount() const279 int32_t WaterFlowLayoutInfo::GetCrossCount() const
280 {
281 return static_cast<int32_t>(items_[0].size());
282 }
283
GetMainCount() const284 int32_t WaterFlowLayoutInfo::GetMainCount() const
285 {
286 int32_t maxMainCount = 0;
287 for (const auto& crossItems : items_[0]) {
288 if (crossItems.second.empty()) {
289 continue;
290 }
291 auto mainCount = static_cast<int32_t>(std::count_if(crossItems.second.begin(), crossItems.second.end(),
292 [start = startIndex_, end = endIndex_](const std::pair<const int, std::pair<float, float>>& crossItem) {
293 return crossItem.first >= start && crossItem.first <= end;
294 }));
295 maxMainCount = std::max(maxMainCount, mainCount);
296 }
297 return maxMainCount;
298 }
299
ClearCacheAfterIndex(int32_t currentIndex)300 void WaterFlowLayoutInfo::ClearCacheAfterIndex(int32_t currentIndex)
301 {
302 size_t segment = static_cast<size_t>(GetSegment(currentIndex));
303 for (auto& crossItems : items_[segment]) {
304 if (crossItems.second.empty()) {
305 continue;
306 }
307 auto clearFrom = std::find_if(crossItems.second.begin(), crossItems.second.end(),
308 [currentIndex](const std::pair<const int, std::pair<float, float>>& crossItem) {
309 return crossItem.first > currentIndex;
310 });
311 crossItems.second.erase(clearFrom, crossItems.second.end());
312 }
313 for (size_t i = segment + 1; i < items_.size(); ++i) {
314 for (auto& col : items_[i]) {
315 col.second.clear();
316 }
317 }
318
319 if (static_cast<size_t>(currentIndex + 1) < itemInfos_.size()) {
320 itemInfos_.resize(currentIndex + 1);
321 }
322
323 auto it = std::upper_bound(endPosArray_.begin(), endPosArray_.end(), currentIndex,
324 [](int32_t index, const std::pair<float, int32_t>& pos) { return index < pos.second; });
325 endPosArray_.erase(it, endPosArray_.end());
326
327 if (segment + 1 < segmentStartPos_.size()) {
328 segmentStartPos_.resize(segment + 1);
329 if (currentIndex == segmentTails_[segment]) {
330 SetNextSegmentStartPos(currentIndex);
331 }
332 }
333 }
334
ReachStart(float prevOffset,bool firstLayout) const335 bool WaterFlowLayoutInfo::ReachStart(float prevOffset, bool firstLayout) const
336 {
337 auto scrollUpToReachTop = (LessNotEqual(prevOffset, 0.0) || firstLayout) && GreatOrEqual(currentOffset_, 0.0);
338 auto scrollDownToReachTop = GreatNotEqual(prevOffset, 0.0) && LessOrEqual(currentOffset_, 0.0);
339 return scrollUpToReachTop || scrollDownToReachTop;
340 }
341
ReachEnd(float prevOffset,bool firstLayout) const342 bool WaterFlowLayoutInfo::ReachEnd(float prevOffset, bool firstLayout) const
343 {
344 if (!offsetEnd_) {
345 return false;
346 }
347 float minOffset = lastMainSize_ - maxHeight_;
348 auto scrollDownToReachEnd =
349 (GreatNotEqual(prevOffset, minOffset) || firstLayout) && LessOrEqual(currentOffset_, minOffset);
350 auto scrollUpToReachEnd = LessNotEqual(prevOffset, minOffset) && GreatOrEqual(currentOffset_, minOffset);
351 return scrollDownToReachEnd || scrollUpToReachEnd;
352 }
353
FastSolveStartIndex() const354 int32_t WaterFlowLayoutInfo::FastSolveStartIndex() const
355 {
356 if (NearZero(currentOffset_) && !endPosArray_.empty() && NearZero(endPosArray_[0].first)) {
357 return endPosArray_[0].second;
358 }
359 auto it = std::upper_bound(endPosArray_.begin(), endPosArray_.end(), -currentOffset_,
360 [](float value, const std::pair<float, int32_t>& info) { return LessNotEqual(value, info.first); });
361 if (it == endPosArray_.end()) {
362 return std::max(static_cast<int32_t>(itemInfos_.size()) - 1, 0);
363 }
364 return it->second;
365 }
366
FastSolveEndIndex(float mainSize) const367 int32_t WaterFlowLayoutInfo::FastSolveEndIndex(float mainSize) const
368 {
369 if (itemInfos_.empty()) {
370 return -1;
371 }
372
373 const float endBound = mainSize - currentOffset_;
374 auto it = std::lower_bound(itemInfos_.begin(), itemInfos_.end(), endBound,
375 [](const ItemInfo& info, float value) { return LessNotEqual(info.mainOffset, value); });
376
377 // The last flowItem with the height of 0 should be regarded as endIndex_ when reach end.
378 while (it != itemInfos_.end() && NearZero(it->mainSize) && NearEqual(it->mainOffset, endBound)) {
379 ++it;
380 }
381 int32_t res = std::distance(itemInfos_.begin(), it) - 1;
382 return std::max(res, 0);
383 }
384
RecordItem(int32_t idx,const FlowItemPosition & pos,float height)385 void WaterFlowLayoutInfo::RecordItem(int32_t idx, const FlowItemPosition& pos, float height)
386 {
387 if (itemInfos_.size() != static_cast<size_t>(idx)) {
388 return;
389 }
390 items_[GetSegment(idx)][pos.crossIndex][idx] = { pos.startMainPos, height };
391 itemInfos_.emplace_back(pos.crossIndex, pos.startMainPos, height);
392 if (endPosArray_.empty() || LessNotEqual(endPosArray_.back().first, pos.startMainPos + height)) {
393 endPosArray_.emplace_back(pos.startMainPos + height, idx);
394 }
395
396 if (idx == segmentTails_[GetSegment(idx)]) {
397 SetNextSegmentStartPos(idx);
398 }
399 }
400
SetNextSegmentStartPos(int32_t itemIdx)401 void WaterFlowLayoutInfo::SetNextSegmentStartPos(int32_t itemIdx)
402 {
403 auto segment = static_cast<size_t>(GetSegment(itemIdx));
404 if (segmentStartPos_.size() > segment + 1) {
405 return;
406 }
407 if (segmentStartPos_.size() <= segment || margins_.size() <= segment + 1) {
408 return;
409 }
410
411 float nextStartPos = endPosArray_.empty() ? segmentStartPos_[segment] : endPosArray_.back().first;
412 while (segment < segmentTails_.size() - 1 && itemIdx == segmentTails_[segment]) {
413 // use while loop to skip empty segments
414 if (axis_ == Axis::VERTICAL) {
415 nextStartPos += margins_[segment].bottom.value_or(0.0f) + margins_[segment + 1].top.value_or(0.0f);
416 } else {
417 nextStartPos += margins_[segment].right.value_or(0.0f) + margins_[segment + 1].left.value_or(0.0f);
418 }
419 segmentStartPos_.push_back(nextStartPos);
420 ++segment;
421 }
422 }
423
Sync(float mainSize,bool overScroll)424 void WaterFlowLayoutInfo::Sync(float mainSize, bool overScroll)
425 {
426 // adjust offset when it can't overScroll at top
427 if (!overScroll) {
428 currentOffset_ = std::min(currentOffset_, 0.0f);
429 }
430 endIndex_ = FastSolveEndIndex(mainSize);
431
432 maxHeight_ = GetMaxMainHeight();
433
434 itemStart_ = GreatOrEqual(currentOffset_, 0.0f);
435 itemEnd_ = endIndex_ >= 0 && endIndex_ == childrenCount_ - 1;
436 offsetEnd_ = itemEnd_ && GreatOrEqual(mainSize - currentOffset_, maxHeight_);
437 // adjust offset when it can't overScroll at bottom
438 if (offsetEnd_ && !overScroll) {
439 currentOffset_ = std::min(-maxHeight_ + mainSize, 0.0f);
440 }
441
442 startIndex_ = FastSolveStartIndex();
443 }
444
InitSegments(const std::vector<WaterFlowSections::Section> & sections,int32_t start)445 void WaterFlowLayoutInfo::InitSegments(const std::vector<WaterFlowSections::Section>& sections, int32_t start)
446 {
447 size_t n = sections.size();
448 if (n == 0) {
449 Reset();
450 currentOffset_ = 0.0f;
451 return;
452 }
453 segmentTails_ = { sections[0].itemsCount - 1 };
454 for (size_t i = 1; i < n; ++i) {
455 segmentTails_.push_back(segmentTails_[i - 1] + sections[i].itemsCount);
456 }
457
458 segmentCache_.clear();
459 if (static_cast<size_t>(start) < segmentStartPos_.size()) {
460 segmentStartPos_.resize(start);
461 // startPos of next segment can only be determined after margins_ is reinitialized.
462 }
463
464 int32_t lastValidItem = (start > 0) ? segmentTails_[start - 1] : -1;
465 if (static_cast<size_t>(lastValidItem + 1) < itemInfos_.size()) {
466 itemInfos_.resize(lastValidItem + 1);
467 }
468
469 auto it = std::upper_bound(endPosArray_.begin(), endPosArray_.end(), lastValidItem,
470 [](int32_t index, const std::pair<float, int32_t>& pos) { return index < pos.second; });
471 endPosArray_.erase(it, endPosArray_.end());
472 items_.resize(n);
473 for (size_t i = static_cast<size_t>(start); i < n; ++i) {
474 items_[i].clear();
475 for (int32_t j = 0; j < sections[i].crossCount; ++j) {
476 items_[i][j] = {};
477 }
478 }
479
480 margins_.clear(); // to be initialized during layout
481 }
482
PrepareSegmentStartPos()483 void WaterFlowLayoutInfo::PrepareSegmentStartPos()
484 {
485 if (segmentStartPos_.size() <= 1) {
486 ResetSegmentStartPos();
487 }
488 int32_t lastItem = static_cast<int32_t>(itemInfos_.size()) - 1;
489 if (GetSegment(lastItem) >= static_cast<int32_t>(segmentTails_.size())) {
490 TAG_LOGW(AceLogTag::ACE_WATERFLOW, "Section data not initialized before layout");
491 return;
492 }
493 if (segmentTails_[GetSegment(lastItem)] == lastItem) {
494 SetNextSegmentStartPos(static_cast<int32_t>(itemInfos_.size()) - 1);
495 }
496 }
497
ResetSegmentStartPos()498 void WaterFlowLayoutInfo::ResetSegmentStartPos()
499 {
500 if (margins_.empty()) {
501 segmentStartPos_ = { 0.0f };
502 } else {
503 segmentStartPos_ = { (axis_ == Axis::VERTICAL ? margins_[0].top : margins_[0].left).value_or(0.0f) };
504 }
505 }
506
PrintWaterFlowItems() const507 void WaterFlowLayoutInfo::PrintWaterFlowItems() const
508 {
509 for (const auto& [key1, map1] : items_[0]) {
510 std::stringstream ss;
511 ss << key1 << ": {";
512 for (const auto& [key2, pair] : map1) {
513 ss << key2 << ": (" << pair.first << ", " << pair.second << ")";
514 if (&pair != &map1.rbegin()->second) {
515 ss << ", ";
516 }
517 }
518 ss << "}";
519 LOGI("%{public}s", ss.str().c_str());
520 }
521 }
522
JumpToTargetAlign(const std::pair<float,float> & item) const523 float WaterFlowLayoutInfo::JumpToTargetAlign(const std::pair<float, float>& item) const
524 {
525 float targetPosition = 0.0f;
526 ScrollAlign align = align_;
527 switch (align) {
528 case ScrollAlign::START:
529 targetPosition = -item.first;
530 break;
531 case ScrollAlign::END:
532 targetPosition = lastMainSize_ - (item.first + item.second);
533 break;
534 case ScrollAlign::AUTO:
535 if (currentOffset_ + item.first < 0) {
536 targetPosition = -item.first;
537 } else if (currentOffset_ + item.first + item.second > lastMainSize_) {
538 targetPosition = lastMainSize_ - (item.first + item.second);
539 } else {
540 targetPosition = currentOffset_;
541 }
542 break;
543 case ScrollAlign::CENTER:
544 targetPosition = -item.first + (lastMainSize_ - item.second) * HALF;
545 break;
546 default:
547 break;
548 }
549 return targetPosition;
550 }
551
JumpTo(const std::pair<float,float> & item)552 void WaterFlowLayoutInfo::JumpTo(const std::pair<float, float>& item)
553 {
554 currentOffset_ = JumpToTargetAlign(item);
555 if (extraOffset_.has_value()) {
556 currentOffset_ += extraOffset_.value();
557 extraOffset_.reset();
558 }
559 align_ = ScrollAlign::START;
560 jumpIndex_ = EMPTY_JUMP_INDEX;
561 }
562
UpdateOffset(float delta)563 void WaterFlowLayoutInfo::UpdateOffset(float delta)
564 {
565 currentOffset_ += delta;
566 }
567
CalcTargetPosition(int32_t idx,int32_t crossIdx) const568 float WaterFlowLayoutInfo::CalcTargetPosition(int32_t idx, int32_t crossIdx) const
569 {
570 return -JumpToTargetAlign(items_[GetSegment(idx)].at(crossIdx).at(idx));
571 }
572
OutOfBounds() const573 bool WaterFlowLayoutInfo::OutOfBounds() const
574 {
575 bool outOfStart = itemStart_ && Positive(currentOffset_);
576 bool outOfEnd = offsetEnd_ && LessNotEqual(currentOffset_ + maxHeight_, lastMainSize_);
577 // not outOfEnd when content size < mainSize but currentOffset_ == 0
578 if (LessNotEqual(maxHeight_, lastMainSize_)) {
579 outOfEnd &= Negative(currentOffset_);
580 }
581 return outOfStart || outOfEnd;
582 }
583
CalcOverScroll(float mainSize,float delta) const584 float WaterFlowLayoutInfo::CalcOverScroll(float mainSize, float delta) const
585 {
586 float res = 0;
587 if (itemStart_) {
588 res = currentOffset_ + delta;
589 }
590 if (offsetEnd_) {
591 res = mainSize - (GetMaxMainHeight() + currentOffset_ - delta);
592 }
593 return res;
594 }
595
EstimateContentHeight() const596 float WaterFlowLayoutInfo::EstimateContentHeight() const
597 {
598 auto childCount = 0;
599 if (!itemInfos_.empty()) {
600 // in segmented layout
601 childCount = static_cast<int32_t>(itemInfos_.size());
602 } else if (maxHeight_) {
603 // in original layout, already reach end.
604 return maxHeight_;
605 } else {
606 // in original layout
607 for (const auto& item : items_[0]) {
608 childCount += static_cast<int32_t>(item.second.size());
609 }
610 }
611 if (childCount == 0) {
612 return 0;
613 }
614 auto estimateHeight = GetMaxMainHeight() / childCount * childrenCount_;
615 return estimateHeight;
616 }
617
GetLastItem() const618 int32_t WaterFlowLayoutInfo::GetLastItem() const
619 {
620 int32_t res = -1;
621 if (items_.empty()) {
622 return res;
623 }
624 for (auto&& map : items_[0]) {
625 if (map.second.empty()) {
626 continue;
627 }
628 res = std::max(res, map.second.rbegin()->first);
629 }
630 return res;
631 }
632 } // namespace OHOS::Ace::NG
633