1 /*
2 * Copyright (c) 2022 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/list/list_layout_algorithm.h"
17
18 #include <algorithm>
19 #include <unordered_set>
20
21 #include "base/geometry/axis.h"
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/size_t.h"
24 #include "base/log/ace_trace.h"
25 #include "base/memory/ace_type.h"
26 #include "base/utils/time_util.h"
27 #include "base/utils/utils.h"
28 #include "core/components/common/layout/layout_param.h"
29 #include "core/components_ng/base/frame_node.h"
30 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h"
31 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
32 #include "core/components_ng/pattern/list/list_item_pattern.h"
33 #include "core/components_ng/pattern/list/list_layout_property.h"
34 #include "core/components_ng/pattern/list/list_pattern.h"
35 #include "core/components_ng/pattern/text_field/text_field_manager.h"
36 #include "core/components_ng/property/layout_constraint.h"
37 #include "core/components_ng/property/measure_property.h"
38 #include "core/components_ng/property/measure_utils.h"
39 #include "core/components_ng/property/property.h"
40 #include "core/components_v2/inspector/inspector_constants.h"
41 #include "core/components_v2/list/list_properties.h"
42 #include "core/pipeline_ng/pipeline_context.h"
43
44 namespace OHOS::Ace::NG {
45
UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)46 void ListLayoutAlgorithm::UpdateListItemConstraint(
47 Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
48 {
49 contentConstraint.parentIdealSize = selfIdealSize;
50 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis);
51 auto crossSize = selfIdealSize.CrossSize(axis);
52 if (crossSize.has_value()) {
53 contentConstraint.maxSize.SetCrossSize(crossSize.value(), axis);
54 contentConstraint.percentReference.SetCrossSize(crossSize.value(), axis);
55 }
56 }
57
Measure(LayoutWrapper * layoutWrapper)58 void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
59 {
60 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
61 CHECK_NULL_VOID(listLayoutProperty);
62
63 const auto& layoutConstraint = listLayoutProperty->GetLayoutConstraint().value();
64
65 // calculate idealSize and set FrameSize
66 axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
67
68 // calculate main size.
69 auto contentConstraint = listLayoutProperty->GetContentLayoutConstraint().value();
70 auto contentIdealSize = CreateIdealSize(
71 contentConstraint, axis_, listLayoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS));
72
73 const auto& padding = listLayoutProperty->CreatePaddingAndBorder();
74 paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
75 paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
76 contentMainSize_ = 0.0f;
77 contentStartOffset_ = listLayoutProperty->GetContentStartOffset().value_or(0.0f);
78 contentEndOffset_ = listLayoutProperty->GetContentEndOffset().value_or(0.0f);
79 totalItemCount_ = layoutWrapper->GetTotalChildCount();
80 if (!GetMainAxisSize(contentIdealSize, axis_)) {
81 if (totalItemCount_ == 0) {
82 contentMainSize_ = 0.0f;
83 } else {
84 // use parent max size first.
85 auto parentMaxSize = contentConstraint.maxSize;
86 contentMainSize_ = GetMainAxisSize(parentMaxSize, axis_) - paddingBeforeContent_ - paddingAfterContent_;
87 mainSizeIsDefined_ = false;
88 }
89 } else {
90 contentMainSize_ = GetMainAxisSize(contentIdealSize.ConvertToSizeT(), axis_);
91 mainSizeIsDefined_ = true;
92 }
93
94 if (totalItemCount_ > 0) {
95 OnSurfaceChanged(layoutWrapper);
96 CheckJumpToIndex();
97 currentOffset_ = currentDelta_;
98 startMainPos_ = currentOffset_;
99 endMainPos_ = currentOffset_ + contentMainSize_;
100 stickyStyle_ = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
101 childLayoutConstraint_ = listLayoutProperty->CreateChildConstraint();
102 auto mainPercentRefer = GetMainAxisSize(childLayoutConstraint_.percentReference, axis_);
103 auto space = listLayoutProperty->GetSpace().value_or(Dimension(0));
104 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0);
105 if (GreatOrEqual(spaceWidth_, contentMainSize_)) {
106 spaceWidth_ = 0.0f;
107 }
108 if (listLayoutProperty->GetDivider().has_value()) {
109 auto divider = listLayoutProperty->GetDivider().value();
110 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx();
111 if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) {
112 dividerSpace.reset();
113 }
114 if (dividerSpace.has_value()) {
115 spaceWidth_ = std::max(spaceWidth_, dividerSpace.value());
116 }
117 }
118 spaceWidth_ += chainInterval_;
119 CalculateLanes(listLayoutProperty, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_);
120 listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
121 // calculate child layout constraint.
122 UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_);
123 MeasureList(layoutWrapper);
124 } else {
125 itemPosition_.clear();
126 layoutWrapper->RemoveAllChildInRenderTree();
127 LOGI("child size is empty");
128 }
129
130 auto crossSize = contentIdealSize.CrossSize(axis_);
131 if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) {
132 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_);
133 crossMatchChild_ = true;
134 }
135 contentIdealSize.SetMainSize(contentMainSize_, axis_);
136 AddPaddingToSize(padding, contentIdealSize);
137 layoutWrapper->GetGeometryNode()->SetFrameSize(contentIdealSize.ConvertToSizeT());
138
139 // set list cache info.
140 layoutWrapper->SetCacheCount(listLayoutProperty->GetCachedCountValue(1) * GetLanes());
141
142 LOGD("new start index is %{public}d, new end index is %{public}d, offset is %{public}f, mainSize is %{public}f",
143 GetStartIndex(), GetEndIndex(), currentOffset_, contentMainSize_);
144 }
145
GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis) const146 float ListLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis) const
147 {
148 if (GetItemPosition().empty()) {
149 return 0.0f;
150 }
151 float maxCrossSize = 0.0f;
152 float crossSize = 0.0f;
153 float prevPos = GetItemPosition().begin()->second.startPos;
154 for (const auto& pos : GetItemPosition()) {
155 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false);
156 if (!wrapper) {
157 continue;
158 }
159 auto getGeometryNode = wrapper->GetGeometryNode();
160 if (!getGeometryNode) {
161 continue;
162 }
163 if (NearEqual(prevPos, pos.second.startPos)) {
164 crossSize += getGeometryNode->GetMarginFrameSize().CrossSize(axis);
165 } else {
166 crossSize = getGeometryNode->GetMarginFrameSize().CrossSize(axis);
167 }
168 prevPos = pos.second.startPos;
169 maxCrossSize = std::max(crossSize, maxCrossSize);
170 }
171 return maxCrossSize;
172 }
173
CalculateEstimateOffset(ScrollAlign align)174 void ListLayoutAlgorithm::CalculateEstimateOffset(ScrollAlign align)
175 {
176 if (itemPosition_.empty()) {
177 estimateOffset_ = 0.0f;
178 return;
179 }
180 float itemsHeight = (itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos) + spaceWidth_;
181 auto lines = static_cast<int32_t>(itemPosition_.size());
182 if (GetLanes() > 1) {
183 lines = (lines / GetLanes()) + (lines % GetLanes() > 0 ? 1 : 0);
184 }
185 if (lines > 0) {
186 float averageHeight = itemsHeight / static_cast<float>(lines);
187 switch (align) {
188 case ScrollAlign::START:
189 case ScrollAlign::NONE:
190 estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes());
191 break;
192 case ScrollAlign::CENTER:
193 estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes()) -
194 contentMainSize_ / 2.0f + (averageHeight - spaceWidth_) / 2.0f;
195 break;
196 case ScrollAlign::END:
197 estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes() + 1) -
198 spaceWidth_ - contentMainSize_;
199 break;
200 case ScrollAlign::AUTO:
201 switch (scrollAutoType_) {
202 case ScrollAutoType::NOT_CHANGE:
203 estimateOffset_ = averageHeight * static_cast<float>(itemPosition_.begin()->first /
204 GetLanes()) - itemPosition_.begin()->second.startPos;
205 break;
206 case ScrollAutoType::START:
207 estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes());
208 break;
209 case ScrollAutoType::END:
210 estimateOffset_ = averageHeight * static_cast<float>(jumpIndex_.value() / GetLanes() + 1) -
211 spaceWidth_ - contentMainSize_;
212 break;
213 }
214 break;
215 }
216 } else {
217 estimateOffset_ = 0.0f;
218 }
219 }
220
BeginLayoutForward(float startPos,LayoutWrapper * layoutWrapper)221 void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layoutWrapper)
222 {
223 LayoutForward(layoutWrapper, jumpIndex_.value(), startPos);
224 if (((jumpIndex_.value() > 0) || (!IsScrollSnapAlignCenter(layoutWrapper) && jumpIndex_.value() == 0)) &&
225 GreatNotEqual(GetStartPosition(), (contentStartOffset_ + startMainPos_))) {
226 LayoutBackward(layoutWrapper, jumpIndex_.value() - 1, GetStartPosition());
227 if (LessNotEqual(GetEndIndex(), totalItemCount_ - 1) &&
228 LessNotEqual(GetEndPosition(), endMainPos_)) {
229 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
230 }
231 }
232 }
233
BeginLayoutBackward(float startPos,LayoutWrapper * layoutWrapper)234 void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper)
235 {
236 LayoutBackward(layoutWrapper, jumpIndex_.value(), startPos);
237 if (LessOrEqual(jumpIndex_.value(), totalItemCount_ - 1) &&
238 LessNotEqual(GetEndPosition(), endMainPos_)) {
239 LayoutForward(layoutWrapper, jumpIndex_.value() + 1, GetEndPosition());
240 if (Positive(GetStartIndex()) && GreatNotEqual(GetStartPosition(), startMainPos_)) {
241 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
242 }
243 }
244 }
245
HandleJumpAuto(LayoutWrapper * layoutWrapper,int32_t & startIndex,int32_t & endIndex,float & startPos,float & endPos)246 void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper,
247 int32_t& startIndex, int32_t& endIndex, float& startPos, float& endPos)
248 {
249 bool isSmoothJump = false;
250 int32_t jumpIndex = 0;
251 if (jumpIndex_.has_value()) {
252 jumpIndex = jumpIndex_.value();
253 } else {
254 jumpIndex = targetIndex_.value();
255 isSmoothJump = true;
256 }
257 int32_t tempStartIndex = startIndex;
258 int32_t tempEndIndex = endIndex;
259 if (GreatNotEqual(GetLanes(), 1)) {
260 jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
261 tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
262 tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
263 }
264 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex);
265 CHECK_NULL_VOID(wrapper);
266 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
267 if (!isGroup) {
268 if (jumpIndex >= tempEndIndex) {
269 scrollAutoType_ = ScrollAutoType::END;
270 if (!isSmoothJump) {
271 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value()) + GetLanes() - 1;
272 startPos = contentMainSize_;
273 BeginLayoutBackward(startPos, layoutWrapper);
274 }
275 } else if (jumpIndex <= tempStartIndex) {
276 scrollAutoType_ = ScrollAutoType::START;
277 if (!isSmoothJump) {
278 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value());
279 startPos = 0.0f;
280 BeginLayoutForward(startPos, layoutWrapper);
281 }
282 }
283 } else if (jumpIndex <= tempStartIndex) {
284 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
285 SetListItemGroupParam(wrapper, contentMainSize_, false, listLayoutProperty, false);
286 wrapper->Measure(childLayoutConstraint_);
287 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
288 if (GreatNotEqual(contentMainSize_, mainLen)) {
289 scrollAutoType_ = ScrollAutoType::START;
290 if (!isSmoothJump) {
291 startPos = 0.0f;
292 BeginLayoutForward(startPos, layoutWrapper);
293 }
294 } else {
295 scrollAutoType_ = ScrollAutoType::END;
296 if (!isSmoothJump) {
297 startPos = contentMainSize_;
298 BeginLayoutBackward(startPos, layoutWrapper);
299 }
300 }
301 } else if (jumpIndex >= tempEndIndex) {
302 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
303 SetListItemGroupParam(wrapper, 0.0f, false, listLayoutProperty, false);
304 wrapper->Measure(childLayoutConstraint_);
305 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
306 if (GreatOrEqual(mainLen, contentMainSize_)) {
307 scrollAutoType_ = ScrollAutoType::START;
308 if (!isSmoothJump) {
309 startPos = 0.0f;
310 BeginLayoutForward(startPos, layoutWrapper);
311 }
312 } else {
313 scrollAutoType_ = ScrollAutoType::END;
314 if (!isSmoothJump) {
315 startPos = contentMainSize_;
316 BeginLayoutBackward(startPos, layoutWrapper);
317 }
318 }
319 }
320 }
321
HandleJumpEnd(LayoutWrapper * layoutWrapper)322 void ListLayoutAlgorithm::HandleJumpEnd(LayoutWrapper* layoutWrapper)
323 {
324 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value());
325 CHECK_NULL_VOID(wrapper);
326 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
327 if (!isGroup) {
328 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value()) + GetLanes() - 1;
329 }
330 BeginLayoutBackward(contentMainSize_, layoutWrapper);
331 }
332
NoNeedJump(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex)333 bool ListLayoutAlgorithm::NoNeedJump(LayoutWrapper* layoutWrapper, float startPos, float endPos,
334 int32_t startIndex, int32_t endIndex)
335 {
336 int32_t jumpIndex = 0;
337 if (jumpIndex_.has_value()) {
338 jumpIndex = jumpIndex_.value();
339 } else {
340 jumpIndex = targetIndex_.value();
341 }
342 int32_t tempStartIndex = startIndex;
343 int32_t tempEndIndex = endIndex;
344 if (GreatNotEqual(GetLanes(), 1)) {
345 jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex);
346 tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex);
347 tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex);
348 }
349 if (jumpIndex > tempStartIndex && jumpIndex < tempEndIndex) {
350 return true;
351 }
352 if (jumpIndex == tempStartIndex && jumpIndex == tempEndIndex) {
353 return true;
354 }
355 if ((jumpIndex == tempStartIndex) && GreatOrEqual(startPos, 0.0f)) {
356 return true;
357 }
358 if ((jumpIndex == tempEndIndex) && LessOrEqual(endPos, contentMainSize_)) {
359 return true;
360 }
361
362 return false;
363 }
364
MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex)365 float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex)
366 {
367 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex);
368 CHECK_NULL_RETURN(wrapper, 0.0f);
369 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
370 if (isGroup) {
371 auto listLayoutProperty =
372 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
373 // true: layout forward, true: layout all group items.
374 SetListItemGroupParam(wrapper, 0.0f, true, listLayoutProperty, true);
375 }
376 wrapper->Measure(childLayoutConstraint_);
377 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
378 return mainLen;
379 }
380
CheckJumpToIndex()381 void ListLayoutAlgorithm::CheckJumpToIndex()
382 {
383 if (jumpIndex_.has_value()) {
384 return;
385 }
386 if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) {
387 return;
388 }
389 for (const auto& pos : itemPosition_) {
390 if (pos.second.isGroup) {
391 return;
392 }
393 }
394 float totalHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
395 float averageHeight = totalHeight / itemPosition_.size();
396 int32_t targetIndex = itemPosition_.begin()->first;
397 if (NonNegative(currentDelta_)) {
398 int32_t items = currentDelta_ / averageHeight;
399 targetIndex += items;
400 currentDelta_ -= items * averageHeight;
401 } else {
402 int32_t items = -currentDelta_ / averageHeight;
403 targetIndex -= items;
404 currentDelta_ += items * averageHeight;
405 }
406 jumpIndex_ = std::clamp(targetIndex, 0, totalItemCount_ - 1);
407 }
408
MeasureList(LayoutWrapper * layoutWrapper)409 void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper)
410 {
411 int32_t startIndex = 0;
412 int32_t endIndex = 0;
413 int32_t midIndex = 0;
414 float midItemMidPos = 0.0f;
415 float startPos = 0.0f;
416 float endPos = 0.0f;
417 if (jumpIndex_) {
418 if (jumpIndex_.value() == LAST_ITEM) {
419 jumpIndex_ = totalItemCount_ - 1;
420 } else if ((jumpIndex_.value() < 0) || (jumpIndex_.value() >= totalItemCount_)) {
421 LOGW("jump index is illegal, %{public}d, %{public}d", jumpIndex_.value(), totalItemCount_);
422 jumpIndex_.reset();
423 }
424 }
425 if (!itemPosition_.empty()) {
426 startPos = itemPosition_.begin()->second.startPos;
427 endPos = itemPosition_.rbegin()->second.endPos;
428 startIndex = std::min(GetStartIndex(), totalItemCount_ - 1);
429 endIndex = std::min(GetEndIndex(), totalItemCount_ - 1);
430 if (GetStartIndex() > totalItemCount_ - 1 && !jumpIndex_.has_value()) {
431 jumpIndex_ = totalItemCount_ - 1;
432 scrollAlign_ = ScrollAlign::END;
433 }
434 if (IsScrollSnapAlignCenter(layoutWrapper) && overScrollFeature_) {
435 float itemHeight = 0.0f;
436 if (startIndex == 0) {
437 itemHeight = itemPosition_.begin()->second.endPos - startPos;
438 contentStartOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
439 }
440 if (endIndex == totalItemCount_ - 1) {
441 itemHeight = endPos - itemPosition_.rbegin()->second.startPos;
442 contentEndOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
443 }
444 }
445 if (IsScrollSnapAlignCenter(layoutWrapper)) {
446 midIndex = GetMidIndex(layoutWrapper, true);
447 midItemMidPos = (itemPosition_[midIndex].startPos + itemPosition_[midIndex].endPos) / 2.0f -
448 prevContentMainSize_ / 2.0f + contentMainSize_ / 2.0f;
449 midIndex = std::min(midIndex, totalItemCount_ - 1);
450 }
451 OffScreenLayoutDirection();
452 itemPosition_.clear();
453 layoutWrapper->RemoveAllChildInRenderTree();
454 }
455 if ((jumpIndex_ || targetIndex_) && scrollAlign_ == ScrollAlign::AUTO &&
456 NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex)) {
457 jumpIndex_.reset();
458 targetIndex_.reset();
459 }
460 if (jumpIndex_) {
461 LOGD("Jump index: %{public}d, offset is %{public}f, startMainPos: %{public}f, endMainPos: %{public}f",
462 jumpIndex_.value(), currentOffset_, startMainPos_, endMainPos_);
463 switch (scrollAlign_) {
464 case ScrollAlign::START:
465 case ScrollAlign::NONE:
466 case ScrollAlign::CENTER:
467 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value());
468 if (scrollAlign_ == ScrollAlign::START) {
469 startPos = contentStartOffset_;
470 } else {
471 float mainLen = MeasureAndGetChildHeight(layoutWrapper, jumpIndex_.value());
472 startPos = (contentMainSize_ - mainLen) / 2.0f;
473 }
474 BeginLayoutForward(startPos, layoutWrapper);
475 break;
476 case ScrollAlign::END:
477 HandleJumpEnd(layoutWrapper);
478 break;
479 case ScrollAlign::AUTO:
480 HandleJumpAuto(layoutWrapper, startIndex, endIndex, startPos, endPos);
481 break;
482 }
483 CalculateEstimateOffset(scrollAlign_);
484 } else if (targetIndex_.has_value()) {
485 if (scrollAlign_ == ScrollAlign::AUTO) {
486 HandleJumpAuto(layoutWrapper, startIndex, endIndex, startPos, endPos);
487 }
488 if (LessOrEqual(startIndex, targetIndex_.value())) {
489 LayoutForward(layoutWrapper, startIndex, startPos);
490 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
491 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
492 }
493 } else if (GreatNotEqual(startIndex, targetIndex_.value())) {
494 LayoutBackward(layoutWrapper, endIndex, endPos);
495 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
496 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
497 }
498 }
499 } else {
500 jumpIndexInGroup_.reset();
501 LOGD("StartIndex index: %{public}d, offset is %{public}f, startMainPos: %{public}f, endMainPos: %{public}f",
502 startIndex, currentOffset_, startMainPos_, endMainPos_);
503 bool overScrollTop = startIndex == 0 && GreatNotEqual(startPos, startMainPos_);
504 float midItemHeight = 0.0f;
505 if (IsScrollSnapAlignCenter(layoutWrapper)) {
506 midItemHeight = MeasureAndGetChildHeight(layoutWrapper, midIndex);
507 }
508 if ((!overScrollFeature_ && NonNegative(currentOffset_)) ||
509 (overScrollFeature_ && overScrollTop)) {
510 if (IsScrollSnapAlignCenter(layoutWrapper)) {
511 midIndex = GetLanesFloor(layoutWrapper, midIndex);
512 LayoutForward(layoutWrapper, midIndex, midItemMidPos - midItemHeight / 2.0f);
513 } else {
514 startIndex = GetLanesFloor(layoutWrapper, startIndex);
515 LayoutForward(layoutWrapper, startIndex, startPos);
516 }
517 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) {
518 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition());
519 }
520 } else {
521 if (overScrollFeature_ && !overScrollTop && !NearZero(prevContentMainSize_)) {
522 endPos += contentMainSize_ - prevContentMainSize_;
523 }
524 if (IsScrollSnapAlignCenter(layoutWrapper)) {
525 midIndex = GetLanesFloor(layoutWrapper, midIndex) + GetLanes() - 1;
526 LayoutBackward(layoutWrapper, midIndex, midItemMidPos + midItemHeight / 2.0f);
527 } else {
528 LayoutBackward(layoutWrapper, endIndex, endPos);
529 }
530 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) {
531 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition());
532 }
533 }
534 }
535 }
536
LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)537 int32_t ListLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper,
538 int32_t& currentIndex, float startPos, float& endPos)
539 {
540 if (currentIndex + 1 >= totalItemCount_) {
541 return 0;
542 }
543 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex + 1);
544 CHECK_NULL_RETURN(wrapper, 0);
545 ++currentIndex;
546 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
547 if (isGroup) {
548 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
549 SetListItemGroupParam(wrapper, startPos, true, listLayoutProperty, false);
550 }
551 {
552 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
553 wrapper->Measure(childLayoutConstraint_);
554 }
555 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
556 endPos = startPos + mainLen;
557 itemPosition_[currentIndex] = { startPos, endPos, isGroup };
558 return 1;
559 }
560
LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)561 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper,
562 int32_t& currentIndex, float endPos, float& startPos)
563 {
564 if (currentIndex - 1 < 0) {
565 return 0;
566 }
567 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1);
568 CHECK_NULL_RETURN(wrapper, 0);
569 --currentIndex;
570 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
571 if (isGroup) {
572 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
573 SetListItemGroupParam(wrapper, endPos, false, listLayoutProperty, false);
574 }
575 {
576 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex);
577 wrapper->Measure(childLayoutConstraint_);
578 }
579 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
580 startPos = endPos - mainLen;
581 itemPosition_[currentIndex] = { startPos, endPos, isGroup };
582 return 1;
583 }
584
LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)585 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos)
586 {
587 float currentEndPos = startPos;
588 float currentStartPos = 0.0f;
589 float endMainPos = overScrollFeature_ ?
590 std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_;
591 if (forwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
592 endMainPos = Infinity<float>();
593 }
594
595 auto currentIndex = startIndex - 1;
596 auto chainOffset = 0.0f;
597 do {
598 currentStartPos = currentEndPos;
599 int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos);
600 if (count == 0) {
601 break;
602 }
603 if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) {
604 currentEndPos += spaceWidth_;
605 }
606 LOGD("LayoutForward: %{public}d current start pos: %{public}f, current end pos: %{public}f", currentIndex,
607 currentStartPos, currentEndPos);
608 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
609 // reach the valid target index
610 if (forwardFeature_ && targetIndex_ && GreatNotEqual(currentIndex, targetIndex_.value())) {
611 endMainPos = GetEndPosition() + contentMainSize_;
612 targetIndex_.reset();
613 }
614 } while (LessNotEqual(currentEndPos + chainOffset, endMainPos));
615
616 if (overScrollFeature_ && canOverScroll_) {
617 LOGD("during over scroll, just return in LayoutForward");
618 return;
619 }
620
621 currentEndPos += chainOffset;
622 // adjust offset.
623 if (IsScrollSnapAlignCenter(layoutWrapper) && currentIndex == totalItemCount_ - 1) {
624 float itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
625 contentEndOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
626 }
627 if (LessNotEqual(currentEndPos, endMainPos_ - contentEndOffset_) && !itemPosition_.empty()) {
628 auto firstItemTop = itemPosition_.begin()->second.startPos;
629 auto itemTotalSize = currentEndPos - firstItemTop;
630 if (IsScrollSnapAlignCenter(layoutWrapper)) {
631 if (jumpIndex_.has_value() && (itemPosition_.find(jumpIndex_.value()) != itemPosition_.end())) {
632 auto jumpItemStartPos = itemPosition_[jumpIndex_.value()].startPos;
633 auto jumpItemEndPos = itemPosition_[jumpIndex_.value()].endPos;
634 currentOffset_ = jumpItemEndPos - (jumpItemEndPos - jumpItemStartPos) / 2.0f - contentMainSize_ / 2.0f;
635 }
636 startMainPos_ = currentOffset_;
637 } else if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) {
638 // all items size is less than list.
639 currentOffset_ = firstItemTop;
640 startMainPos_ = currentOffset_;
641 if (!mainSizeIsDefined_) {
642 // adapt child size.
643 LOGD("LayoutForward: adapt child total size");
644 contentMainSize_ = itemTotalSize;
645 }
646 } else {
647 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
648 if (!canOverScroll_ || jumpIndex_.has_value()) {
649 currentOffset_ = currentEndPos - contentMainSize_;
650 LOGD("LayoutForward: adjust offset to %{public}f", currentOffset_);
651 }
652 startMainPos_ = currentEndPos - contentMainSize_;
653 endMainPos_ = currentEndPos;
654 }
655 if (Negative(currentOffset_) && itemPosition_.begin()->second.isGroup) {
656 AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex());
657 }
658 }
659
660 // Mark inactive in wrapper.
661 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) {
662 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
663 if (GreatOrEqual(pos->second.endPos + chainOffset, startMainPos_)) {
664 if (pos->second.isGroup) {
665 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true);
666 }
667 break;
668 }
669 LOGI("recycle item:%{public}d", pos->first);
670 layoutWrapper->RemoveChildInRenderTree(pos->first);
671 itemPosition_.erase(pos++);
672 }
673 }
674
LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)675 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos)
676 {
677 float currentStartPos = endPos;
678 float currentEndPos = 0.0f;
679 float startMainPos = overScrollFeature_ ?
680 std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_;
681 if (backwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) {
682 startMainPos = -Infinity<float>();
683 }
684 auto currentIndex = endIndex + 1;
685 auto chainOffset = 0.0f;
686 do {
687 currentEndPos = currentStartPos;
688 int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos);
689 if (count == 0) {
690 break;
691 }
692 if (currentIndex > 0) {
693 currentStartPos = currentStartPos - spaceWidth_;
694 }
695 LOGD("LayoutBackward: %{public}d current start pos: %{public}f, current end pos: %{public}f", currentIndex,
696 currentStartPos, currentEndPos);
697 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f;
698 // reach the valid target index
699 if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) {
700 startMainPos = GetStartPosition() - contentMainSize_;
701 targetIndex_.reset();
702 }
703 } while (GreatNotEqual(currentStartPos + chainOffset, startMainPos));
704
705 currentStartPos += chainOffset;
706 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary
707 if (IsScrollSnapAlignCenter(layoutWrapper) && currentIndex == 0) {
708 float itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
709 contentStartOffset_ = (contentMainSize_ - itemHeight) / 2.0f;
710 }
711 if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_)) {
712 bool overBottom = LessNotEqual(GetEndPosition(), endMainPos_);
713 if (((!canOverScroll_ || overBottom) && !IsScrollSnapAlignCenter(layoutWrapper)) || jumpIndex_.has_value()) {
714 currentOffset_ = currentStartPos;
715 if (!mainSizeIsDefined_ && GetEndIndex() == totalItemCount_ - 1) {
716 auto itemTotalSize = GetEndPosition() - currentStartPos;
717 contentMainSize_ = std::min(contentMainSize_, itemTotalSize);
718 }
719 }
720 if (!IsScrollSnapAlignCenter(layoutWrapper) || jumpIndex_.has_value()) {
721 endMainPos_ = currentStartPos + contentMainSize_;
722 startMainPos_ = currentStartPos;
723 }
724 }
725
726 if (overScrollFeature_) {
727 LOGD("during over scroll, just return in LayoutBackward");
728 return;
729 }
730
731 // Mark inactive in wrapper.
732 std::list<int32_t> removeIndexes;
733 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) {
734 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f;
735 if (LessOrEqual(pos->second.startPos + chainOffset, endMainPos_)) {
736 if (pos->second.isGroup) {
737 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false);
738 }
739 break;
740 }
741 layoutWrapper->RemoveChildInRenderTree(pos->first);
742 removeIndexes.emplace_back(pos->first);
743 }
744 for (const auto& index : removeIndexes) {
745 itemPosition_.erase(index);
746 }
747 }
748
FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)749 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty)
750 {
751 if (!predictSnapOffset_.has_value()) {
752 return;
753 }
754
755 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
756 if (scrollSnapAlign == V2::ScrollSnapAlign::START) {
757 FixPredictSnapOffsetAlignStart();
758 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
759 FixPredictSnapOffsetAlignCenter();
760 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) {
761 FixPredictSnapOffsetAlignEnd();
762 } else {
763 predictSnapOffset_.reset();
764 }
765
766 return;
767 }
768
IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)769 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper)
770 {
771 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
772 CHECK_NULL_RETURN(listLayoutProperty, false);
773 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
774 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
775 return true;
776 }
777
778 return false;
779 }
780
FixPredictSnapOffsetAlignStart()781 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart()
782 {
783 if (itemPosition_.empty()) {
784 return;
785 }
786 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
787 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
788
789 if (LessNotEqual(predictEndPos, 0.0f)) {
790 if (isSpringEffect_) {
791 return;
792 }
793 predictEndPos = 0.0f;
794 } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
795 if (isSpringEffect_) {
796 return;
797 }
798 predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - contentMainSize_;
799 } else {
800 int32_t index;
801 for (index = 0; index <= GetMaxListItemIndex(); index++) {
802 if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) {
803 break;
804 }
805 }
806 predictEndPos = index * itemHeight;
807 if (LessNotEqual(predictEndPos, 0.0f)) {
808 predictEndPos = 0.0f;
809 } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
810 predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - contentMainSize_;
811 }
812 }
813
814 predictSnapOffset_ = totalOffset_ - predictEndPos;
815 }
816
FixPredictSnapOffsetAlignCenter()817 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter()
818 {
819 if (itemPosition_.empty()) {
820 return;
821 }
822 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
823 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
824
825 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
826 if (isSpringEffect_) {
827 return;
828 }
829 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
830 } else if (GreatNotEqual(
831 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
832 if (isSpringEffect_) {
833 return;
834 }
835 predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
836 } else {
837 int32_t index;
838 for (index = 0; index <= GetMaxListItemIndex(); index++) {
839 if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) <
840 itemHeight / 2.0f) {
841 break;
842 }
843 }
844 predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
845 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
846 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
847 } else if (GreatNotEqual(
848 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) {
849 predictEndPos =
850 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f;
851 }
852 }
853
854 predictSnapOffset_ = totalOffset_ - predictEndPos;
855 }
856
FixPredictSnapOffsetAlignEnd()857 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd()
858 {
859 if (itemPosition_.empty()) {
860 return;
861 }
862 auto predictEndPos = totalOffset_ - predictSnapOffset_.value();
863 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
864
865 if (LessNotEqual(predictEndPos, 0.0f)) {
866 if (isSpringEffect_) {
867 return;
868 }
869 predictEndPos = 0.0f;
870 } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
871 if (isSpringEffect_) {
872 return;
873 }
874 predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - contentMainSize_;
875 } else {
876 int32_t index;
877 for (index = 0; index <= GetMaxListItemIndex(); index++) {
878 if (std::abs(predictEndPos + contentMainSize_ - index * itemHeight) < itemHeight / 2.0f) {
879 break;
880 }
881 }
882 predictEndPos = index * itemHeight - contentMainSize_ - spaceWidth_;
883 if (LessNotEqual(predictEndPos, 0.0f)) {
884 predictEndPos = 0.0f;
885 } else if (GreatNotEqual(predictEndPos, itemHeight * GetMaxListItemIndex() + spaceWidth_)) {
886 predictEndPos = itemHeight * totalItemCount_ - spaceWidth_ - contentMainSize_;
887 }
888 }
889
890 predictSnapOffset_ = totalOffset_ - predictEndPos;
891 }
892
LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)893 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos,
894 int32_t& startIndex, float crossSize)
895 {
896 CHECK_NULL_VOID(wrapper);
897 auto offset = paddingOffset_;
898 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_);
899 float crossOffset = 0.0f;
900 if (GetLanes() > 1) {
901 int32_t laneIndex = 0;
902 if (pos.isGroup) {
903 startIndex = index + 1;
904 } else {
905 laneIndex = (index - startIndex) % GetLanes();
906 }
907
908 float laneGutter = GetLaneGutter();
909 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * GetLanes());
910 crossOffset += ((crossSize + laneGutter) / GetLanes() - laneGutter) * laneIndex + laneGutter * laneIndex;
911 } else {
912 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize);
913 }
914 auto chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(index) : 0.0f;
915 if (axis_ == Axis::VERTICAL) {
916 offset = offset + OffsetF(crossOffset, pos.startPos + chainOffset);
917 } else {
918 offset = offset + OffsetF(pos.startPos + chainOffset, crossOffset);
919 }
920 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
921 SetListItemIndex(wrapper, index);
922 }
923
Layout(LayoutWrapper * layoutWrapper)924 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
925 {
926 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty());
927 CHECK_NULL_VOID(listLayoutProperty);
928 auto axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
929 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
930 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
931 MinusPaddingToSize(padding, size);
932 paddingOffset_ = padding.Offset();
933 float crossSize = GetCrossAxisSize(size, axis_);
934 totalItemCount_ = layoutWrapper->GetTotalChildCount();
935 listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START);
936 int32_t startIndex = GetStartIndex();
937
938 totalOffset_ += currentOffset_;
939 FixPredictSnapOffset(listLayoutProperty);
940 // layout items.
941 for (auto& pos : itemPosition_) {
942 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
943 if (!wrapper) {
944 LOGI("wrapper is out of boundary");
945 continue;
946 }
947 pos.second.startPos -= currentOffset_;
948 pos.second.endPos -= currentOffset_;
949 LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize);
950 wrapper->Layout();
951 }
952 auto cacheCount = listLayoutProperty->GetCachedCountValue(1);
953 if (!itemPosition_.empty() && cacheCount > 0) {
954 auto items = LayoutCachedItem(layoutWrapper, cacheCount);
955 if (!items.empty()) {
956 PostIdleTask(layoutWrapper->GetHostNode(), { items, childLayoutConstraint_ });
957 }
958 }
959 }
960
CalculateLaneCrossOffset(float crossSize,float childCrossSize)961 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize)
962 {
963 float delta = crossSize - childCrossSize;
964 if (LessOrEqual(delta, 0)) {
965 return 0.0f;
966 }
967 switch (listItemAlign_) {
968 case OHOS::Ace::V2::ListItemAlign::START:
969 return 0.0f;
970 case OHOS::Ace::V2::ListItemAlign::CENTER:
971 return delta / 2.0f;
972 case OHOS::Ace::V2::ListItemAlign::END:
973 return delta;
974 default:
975 LOGW("Invalid ListItemAlign: %{public}d", listItemAlign_);
976 return 0.0f;
977 }
978 }
979
OnSurfaceChanged(LayoutWrapper * layoutWrapper)980 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper)
981 {
982 if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) {
983 return;
984 }
985 auto host = layoutWrapper->GetHostNode();
986 CHECK_NULL_VOID(host);
987 auto focusHub = host->GetFocusHub();
988 CHECK_NULL_VOID(focusHub);
989 // textField not in List
990 if (!focusHub->IsCurrentFocus()) {
991 return;
992 }
993 auto context = PipelineContext::GetCurrentContext();
994 CHECK_NULL_VOID(context);
995 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
996 CHECK_NULL_VOID_NOLOG(textFieldManager);
997 // only when textField is onFocus
998 auto textField = textFieldManager->GetOnFocusTextField().Upgrade();
999 CHECK_NULL_VOID_NOLOG(textField);
1000 auto textFieldHost = textField->GetHost();
1001 CHECK_NULL_VOID(textFieldHost);
1002 auto position = textFieldHost->GetTransformRelativeOffset().GetY() + textField->GetHostFrameSize()->Height();
1003 auto globalOffset = host->GetTransformRelativeOffset();
1004 auto offset = contentMainSize_ + globalOffset.GetY() - position;
1005 if (LessOrEqual(offset, 0.0)) {
1006 // negative offset to scroll down
1007 currentDelta_ -= static_cast<float>(offset);
1008 LOGI("update offset on virtual keyboard height change, %{public}f", offset);
1009 }
1010 }
1011
SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout)1012 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, float referencePos,
1013 bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout)
1014 {
1015 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true);
1016 CHECK_NULL_VOID(layoutAlgorithmWrapper);
1017 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1018 CHECK_NULL_VOID(itemGroup);
1019 itemGroup->SetListMainSize(startMainPos_, endMainPos_, referencePos, forwardLayout);
1020 itemGroup->SetListLayoutProperty(layoutProperty);
1021 if (jumpIndex_.has_value() && (!jumpIndexInGroup_.has_value())) {
1022 if (forwardLayout&& (scrollAlign_ == ScrollAlign::START ||
1023 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) {
1024 jumpIndexInGroup_ = 0;
1025 } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END ||
1026 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) {
1027 jumpIndexInGroup_ = LAST_ITEM;
1028 }
1029 }
1030
1031 if (groupNeedAllLayout) {
1032 itemGroup->SetNeedAllLayout();
1033 }
1034 if (targetIndex_) {
1035 itemGroup->SetTargetIndex(targetIndex_.value());
1036 }
1037 if (jumpIndexInGroup_.has_value()) {
1038 itemGroup->SetJumpIndex(jumpIndexInGroup_.value());
1039 jumpIndexInGroup_.reset();
1040 }
1041 layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF);
1042 }
1043
SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1044 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index)
1045 {
1046 auto host = layoutWrapper->GetHostNode();
1047 CHECK_NULL_VOID_NOLOG(host);
1048 auto listItem = host->GetPattern<ListItemPattern>();
1049 if (listItem) {
1050 listItem->SetIndexInList(index);
1051 return;
1052 }
1053 auto listItemGroup = host->GetPattern<ListItemGroupPattern>();
1054 CHECK_NULL_VOID_NOLOG(listItemGroup);
1055 listItemGroup->SetIndexInList(index);
1056 }
1057
CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const1058 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index,
1059 float referencePos, bool forwardLayout) const
1060 {
1061 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1062 CHECK_NULL_VOID(wrapper);
1063 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1064 CHECK_NULL_VOID(algorithmWrapper);
1065 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1066 CHECK_NULL_VOID(itemGroup);
1067 itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout);
1068 }
1069
AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index)1070 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index)
1071 {
1072 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1073 CHECK_NULL_VOID(wrapper);
1074 auto algorithmWrapper = wrapper->GetLayoutAlgorithm();
1075 CHECK_NULL_VOID(algorithmWrapper);
1076 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm());
1077 CHECK_NULL_VOID(itemGroup);
1078 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, false);
1079 itemGroup->Measure(AceType::RawPtr(wrapper));
1080 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis);
1081 float delta = 0.0f;
1082 for (auto& pos : itemPosition_) {
1083 if (pos.first == index) {
1084 delta = (pos.second.endPos - pos.second.startPos) - mainLen;
1085 pos.second.endPos = pos.second.startPos + mainLen;
1086 } else if (pos.first > index) {
1087 pos.second.startPos -= delta;
1088 pos.second.endPos -= delta;
1089 }
1090 }
1091 }
1092
OffScreenLayoutDirection()1093 void ListLayoutAlgorithm::OffScreenLayoutDirection()
1094 {
1095 if (!targetIndex_ || itemPosition_.empty() || (itemPosition_.find(targetIndex_.value()) != itemPosition_.end())) {
1096 forwardFeature_ = false;
1097 backwardFeature_ = false;
1098 return;
1099 }
1100 if (GreatNotEqual(targetIndex_.value(), GetEndIndex())) {
1101 forwardFeature_ = true;
1102 backwardFeature_ = false;
1103 } else {
1104 forwardFeature_ = false;
1105 backwardFeature_ = true;
1106 }
1107 }
1108
GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)1109 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize)
1110 {
1111 float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_;
1112 float midPos = contentSize / 2.0f;
1113 if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1114 GreatNotEqual(GetStartPosition(), startMainPos_)) {
1115 midPos = GetStartPosition() + contentSize / 2.0f;
1116 } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) &&
1117 LessNotEqual(GetEndPosition(), endMainPos_) &&
1118 (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) {
1119 midPos = GetEndPosition() - contentSize / 2.0f;
1120 }
1121 for (auto & pos : itemPosition_) {
1122 if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1123 return pos.first;
1124 }
1125 }
1126 return totalItemCount_ - 1;
1127 }
1128
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)1129 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
1130 {
1131 CHECK_NULL_VOID(wrapper);
1132 auto host = wrapper->GetHostNode();
1133 CHECK_NULL_VOID(host);
1134 host->ForceSyncGeometryNode();
1135 }
1136
LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)1137 std::list<int32_t> ListLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount)
1138 {
1139 std::list<int32_t> predictBuildList;
1140 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
1141 float crossSize = GetCrossAxisSize(size, axis_);
1142
1143 auto currIndex = itemPosition_.rbegin()->first + 1;
1144 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_;
1145 for (int32_t i = 0; i < cacheCount && currIndex + i < totalItemCount_; i++) {
1146 int32_t index = currIndex + i;
1147 auto wrapper = layoutWrapper->GetChildByIndex(index);
1148 if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
1149 predictBuildList.emplace_back(index);
1150 continue;
1151 }
1152 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1153 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1154 auto endPos = currPos + GetMainAxisSize(childSize, axis_);
1155 ListItemInfo pos = { currPos, endPos, isGroup };
1156 currPos = endPos + spaceWidth_;
1157 auto startIndex = index;
1158 LayoutItem(wrapper, index, pos, startIndex, crossSize);
1159 SyncGeometry(wrapper);
1160 wrapper->SetActive(false);
1161 }
1162
1163 currIndex = itemPosition_.begin()->first - 1;
1164 currPos = itemPosition_.begin()->second.startPos - spaceWidth_;
1165 for (int32_t i = 0; i < cacheCount && currIndex - i >= 0; i++) {
1166 int32_t index = currIndex - i;
1167 auto wrapper = layoutWrapper->GetChildByIndex(index);
1168 if (!wrapper || wrapper->CheckNeedForceMeasureAndLayout()) {
1169 predictBuildList.emplace_back(index);
1170 continue;
1171 }
1172 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1173 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
1174 auto startPos = currPos - GetMainAxisSize(childSize, axis_);
1175 ListItemInfo pos = { startPos, currPos, isGroup };
1176 currPos = startPos - spaceWidth_;
1177 auto startIndex = index;
1178 LayoutItem(wrapper, index, pos, startIndex, crossSize);
1179 SyncGeometry(wrapper);
1180 wrapper->SetActive(false);
1181 }
1182 return predictBuildList;
1183 }
1184
PredictBuildItem(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint)1185 void ListLayoutAlgorithm::PredictBuildItem(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint)
1186 {
1187 CHECK_NULL_VOID(wrapper);
1188 wrapper->SetActive(false);
1189 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
1190 if (!isGroup) {
1191 auto frameNode = wrapper->GetHostNode();
1192 CHECK_NULL_VOID(frameNode);
1193 frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
1194 FrameNode::ProcessOffscreenNode(frameNode);
1195 }
1196 }
1197
PostIdleTask(RefPtr<FrameNode> frameNode,const ListPredictLayoutParam & param)1198 void ListLayoutAlgorithm::PostIdleTask(RefPtr<FrameNode> frameNode, const ListPredictLayoutParam& param)
1199 {
1200 CHECK_NULL_VOID(frameNode);
1201 auto pattern = frameNode->GetPattern<ListPattern>();
1202 CHECK_NULL_VOID(pattern);
1203 if (pattern->GetPredictLayoutParam()) {
1204 pattern->SetPredictLayoutParam(param);
1205 return;
1206 }
1207 pattern->SetPredictLayoutParam(param);
1208 auto context = PipelineContext::GetCurrentContext();
1209 CHECK_NULL_VOID(context);
1210 context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode))](int64_t deadline, bool canUseLongPredictTask) {
1211 ACE_SCOPED_TRACE("List predict");
1212 auto frameNode = weak.Upgrade();
1213 CHECK_NULL_VOID(frameNode);
1214 auto pattern = frameNode->GetPattern<ListPattern>();
1215 CHECK_NULL_VOID(pattern);
1216 if (!pattern->GetPredictLayoutParam().has_value()) {
1217 return;
1218 }
1219 auto param = pattern->GetPredictLayoutParam().value();
1220 for (auto it = param.items.begin(); it != param.items.end();) {
1221 if (GetSysTimestamp() > deadline) {
1222 break;
1223 }
1224 auto wrapper = frameNode->GetOrCreateChildByIndex(*it, false);
1225 PredictBuildItem(wrapper, param.layoutConstraint);
1226 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1227 param.items.erase(it++);
1228 }
1229 pattern->SetPredictLayoutParam(std::nullopt);
1230 if (!param.items.empty()) {
1231 ListLayoutAlgorithm::PostIdleTask(frameNode, param);
1232 pattern->SetPredictLayoutParam(param);
1233 }
1234 });
1235 }
1236 } // namespace OHOS::Ace::NG
1237