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