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