1 /*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_ng/pattern/list/list_pattern.h"
17
18 #include <cstdint>
19 #include <string>
20
21 #include "base/geometry/axis.h"
22 #include "base/geometry/rect.h"
23 #include "base/log/dump_log.h"
24 #include "base/memory/referenced.h"
25 #include "base/utils/utils.h"
26 #include "core/animation/bilateral_spring_node.h"
27 #include "core/animation/spring_model.h"
28 #include "core/common/container.h"
29 #include "core/components/common/layout/constants.h"
30 #include "core/components/scroll/scroll_bar_theme.h"
31 #include "core/components_ng/base/inspector_filter.h"
32 #include "core/components_ng/pattern/list/list_height_offset_calculator.h"
33 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
34 #include "core/components_ng/pattern/list/list_item_pattern.h"
35 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
36 #include "core/components_ng/pattern/list/list_layout_algorithm.h"
37 #include "core/components_ng/pattern/list/list_layout_property.h"
38 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
39 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
40 #include "core/components_ng/pattern/scrollable/scrollable.h"
41 #include "core/components_ng/property/measure_utils.h"
42 #include "core/components_v2/inspector/inspector_constants.h"
43
44 namespace OHOS::Ace::NG {
45 namespace {
46 constexpr Dimension CHAIN_INTERVAL_DEFAULT = 20.0_vp;
47 constexpr double CHAIN_SPRING_MASS = 1.0;
48 constexpr double CHAIN_SPRING_DAMPING = 30.0;
49 constexpr double CHAIN_SPRING_STIFFNESS = 228;
50 constexpr float DEFAULT_MIN_SPACE_SCALE = 0.75f;
51 constexpr float DEFAULT_MAX_SPACE_SCALE = 2.0f;
52 } // namespace
53
OnModifyDone()54 void ListPattern::OnModifyDone()
55 {
56 Pattern::OnModifyDone();
57 auto host = GetHost();
58 CHECK_NULL_VOID(host);
59 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
60 CHECK_NULL_VOID(listLayoutProperty);
61 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
62 if (axis != GetAxis()) {
63 needReEstimateOffset_ = true;
64 SetAxis(axis);
65 ChangeAxis(GetHost());
66 }
67 if (!GetScrollableEvent()) {
68 AddScrollEvent();
69 auto scrollableEvent = GetScrollableEvent();
70 CHECK_NULL_VOID(scrollableEvent);
71 scrollable_ = scrollableEvent->GetScrollable();
72 }
73
74 SetEdgeEffect();
75
76 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
77 CHECK_NULL_VOID(paintProperty);
78 if (paintProperty->GetScrollBarProperty()) {
79 SetScrollBar(paintProperty->GetScrollBarProperty());
80 }
81
82 SetChainAnimation();
83 if (multiSelectable_ && !isMouseEventInit_) {
84 InitMouseEvent();
85 }
86 if (!multiSelectable_ && isMouseEventInit_) {
87 UninitMouseEvent();
88 }
89 auto focusHub = host->GetFocusHub();
90 CHECK_NULL_VOID(focusHub);
91 InitOnKeyEvent(focusHub);
92 Register2DragDropManager();
93 SetAccessibilityAction();
94 if (IsNeedInitClickEventRecorder()) {
95 Pattern::InitClickEventRecorder();
96 }
97 }
98
ChangeAxis(RefPtr<UINode> node)99 void ListPattern::ChangeAxis(RefPtr<UINode> node)
100 {
101 CHECK_NULL_VOID(node);
102 auto children = node->GetChildren();
103 for (const auto& child : children) {
104 if (AceType::InstanceOf<FrameNode>(child)) {
105 auto frameNode = AceType::DynamicCast<FrameNode>(child);
106 CHECK_NULL_VOID(frameNode);
107 auto listItemPattern = frameNode->GetPattern<ListItemPattern>();
108 if (listItemPattern) {
109 listItemPattern->ChangeAxis(GetAxis());
110 return;
111 }
112 auto listItemGroupPattern = frameNode->GetPattern<ListItemGroupPattern>();
113 if (listItemGroupPattern) {
114 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
115 ChangeAxis(child);
116 }
117 } else {
118 ChangeAxis(child);
119 }
120 }
121 }
122
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)123 bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
124 {
125 if (config.skipMeasure && config.skipLayout) {
126 return false;
127 }
128 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
129 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
130 auto listLayoutAlgorithm = DynamicCast<ListLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
131 CHECK_NULL_RETURN(listLayoutAlgorithm, false);
132 itemPosition_ = listLayoutAlgorithm->GetItemPosition();
133 maxListItemIndex_ = listLayoutAlgorithm->GetMaxListItemIndex();
134 spaceWidth_ = listLayoutAlgorithm->GetSpaceWidth();
135 auto predictSnapOffset = listLayoutAlgorithm->GetPredictSnapOffset();
136 auto predictSnapEndPos = listLayoutAlgorithm->GetPredictSnapEndPosition();
137 bool isJump = listLayoutAlgorithm->NeedEstimateOffset();
138 auto lanesLayoutAlgorithm = DynamicCast<ListLanesLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
139 if (lanesLayoutAlgorithm) {
140 lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
141 if (lanesLayoutAlgorithm->GetLanes() != lanes_) {
142 needReEstimateOffset_ = true;
143 auto item = swiperItem_.Upgrade();
144 if (item) {
145 item->ResetSwipeStatus();
146 }
147 }
148 lanes_ = lanesLayoutAlgorithm->GetLanes();
149 laneGutter_ = lanesLayoutAlgorithm->GetLaneGutter();
150 } else {
151 lanes_ = listLayoutAlgorithm->GetLanes();
152 }
153 float relativeOffset = UpdateTotalOffset(listLayoutAlgorithm, isJump);
154 auto isNeedUpdateIndex = true;
155 if (targetIndex_) {
156 AnimateToTarget(targetIndex_.value(), targetIndexInGroup_, scrollAlign_);
157 // AniamteToTarget does not need to update endIndex and startIndex in the first frame.
158 isNeedUpdateIndex = false;
159 targetIndex_.reset();
160 targetIndexInGroup_.reset();
161 }
162 if (predictSnapOffset.has_value()) {
163 if (scrollable_ && !(NearZero(predictSnapOffset.value()) && NearZero(scrollSnapVelocity_)) &&
164 !AnimateRunning()) {
165 scrollable_->StartScrollSnapMotion(predictSnapOffset.value(), scrollSnapVelocity_);
166 if (snapTrigOnScrollStart_) {
167 FireOnScrollStart();
168 }
169 } else if (!snapTrigOnScrollStart_) {
170 OnAnimateStop();
171 }
172 scrollSnapVelocity_ = 0.0f;
173 predictSnapOffset_.reset();
174 snapTrigOnScrollStart_ = false;
175 if (predictSnapEndPos.has_value()) {
176 predictSnapEndPos_ = predictSnapEndPos;
177 } else {
178 predictSnapEndPos_.reset();
179 }
180 }
181 if (predictSnapEndPos.has_value() && predictSnapEndPos_.has_value() &&
182 !NearEqual(predictSnapEndPos.value(), predictSnapEndPos_.value())) {
183 if (scrollable_) {
184 scrollable_->UpdateScrollSnapEndWithOffset(
185 predictSnapEndPos.value() - predictSnapEndPos_.value());
186 }
187 predictSnapEndPos_.reset();
188 }
189
190 if (isScrollEnd_) {
191 auto host = GetHost();
192 CHECK_NULL_RETURN(host, false);
193 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
194 // AccessibilityEventType::SCROLL_END
195 isScrollEnd_ = false;
196 }
197 currentDelta_ = 0.0f;
198 isNeedCheckOffset_ = false;
199 prevStartOffset_ = startMainPos_;
200 prevEndOffset_ = endMainPos_ - contentMainSize_ + contentEndOffset_;
201 contentMainSize_ = listLayoutAlgorithm->GetContentMainSize();
202 contentStartOffset_ = listLayoutAlgorithm->GetContentStartOffset();
203 contentEndOffset_ = listLayoutAlgorithm->GetContentEndOffset();
204 startMainPos_ = listLayoutAlgorithm->GetStartPosition();
205 endMainPos_ = listLayoutAlgorithm->GetEndPosition();
206 crossMatchChild_ = listLayoutAlgorithm->IsCrossMatchChild();
207 auto endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
208 CheckScrollable();
209
210 bool indexChanged = false;
211 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
212 if (isNeedUpdateIndex) {
213 indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
214 (endIndex_ != listLayoutAlgorithm->GetEndIndex()) ||
215 (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty)));
216 }
217 } else {
218 indexChanged =
219 (startIndex_ != listLayoutAlgorithm->GetStartIndex()) || (endIndex_ != listLayoutAlgorithm->GetEndIndex());
220 }
221 startIndexChanged_ = startIndex_ != listLayoutAlgorithm->GetStartIndex();
222 endIndexChanged_ = endIndex_ != listLayoutAlgorithm->GetEndIndex();
223 if (indexChanged) {
224 startIndex_ = listLayoutAlgorithm->GetStartIndex();
225 endIndex_ = listLayoutAlgorithm->GetEndIndex();
226 centerIndex_ = listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty));
227 }
228 ProcessEvent(indexChanged, relativeOffset, isJump);
229 HandleScrollBarOutBoundary();
230 UpdateScrollBarOffset();
231 if (config.frameSizeChange) {
232 if (GetScrollBar() != nullptr) {
233 GetScrollBar()->ScheduleDisappearDelayTask();
234 }
235 }
236 bool sizeDiminished =
237 !chainAnimation_ && IsOutOfBoundary(false) && (endOffset + relativeOffset - prevEndOffset_ < -0.1f);
238 CheckRestartSpring(sizeDiminished);
239
240 DrivenRender(dirty);
241
242 SetScrollSource(SCROLL_FROM_NONE);
243 isInitialized_ = true;
244 MarkSelectedItems();
245 UpdateListDirectionInCardStyle();
246 return true;
247 }
248
UpdateListDirectionInCardStyle()249 void ListPattern::UpdateListDirectionInCardStyle()
250 {
251 if (isNeedToUpdateListDirection_) {
252 auto layoutProperty = GetLayoutProperty<ListLayoutProperty>();
253 layoutProperty->UpdateListDirection(Axis::VERTICAL);
254 isNeedToUpdateListDirection_ = false;
255 }
256 }
257
GetScrollAlignByScrollSnapAlign() const258 ScrollAlign ListPattern::GetScrollAlignByScrollSnapAlign() const
259 {
260 auto scrollAlign = ScrollAlign::START;
261 auto host = GetHost();
262 CHECK_NULL_RETURN(host, scrollAlign);
263 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
264 CHECK_NULL_RETURN(listProperty, scrollAlign);
265 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
266 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
267 scrollAlign = ScrollAlign::CENTER;
268 }
269 return scrollAlign;
270 }
271
CalculateTargetPos(float startPos,float endPos)272 float ListPattern::CalculateTargetPos(float startPos, float endPos)
273 {
274 float topOffset = startPos - contentStartOffset_;
275 float bottomOffset = endPos - contentMainSize_ + contentEndOffset_;
276 if (GreatOrEqual(topOffset, 0.0f) && LessOrEqual(bottomOffset, 0.0f)) {
277 return 0.0f;
278 }
279 if ((NearEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) ||
280 (LessNotEqual(topOffset, 0.0f) && NearEqual(bottomOffset, 0.0f))) {
281 return 0.0f;
282 }
283 if (LessNotEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) {
284 if (GreatOrEqual(std::abs(topOffset), std::abs(bottomOffset))) {
285 return bottomOffset;
286 } else {
287 return topOffset;
288 }
289 }
290 if (GreatNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
291 return bottomOffset;
292 } else if (LessNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
293 return topOffset;
294 } else {
295 if (LessNotEqual(topOffset, 0.0f)) {
296 return topOffset;
297 } else {
298 return bottomOffset;
299 }
300 }
301 return 0.0f;
302 }
303
CreateNodePaintMethod()304 RefPtr<NodePaintMethod> ListPattern::CreateNodePaintMethod()
305 {
306 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
307 V2::ItemDivider divider;
308 if (!chainAnimation_ && listLayoutProperty->HasDivider()) {
309 divider = listLayoutProperty->GetDivider().value();
310 }
311 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
312 auto drawVertical = (axis == Axis::HORIZONTAL);
313 auto paint = MakeRefPtr<ListPaintMethod>(divider, drawVertical, lanes_, spaceWidth_);
314 paint->SetScrollBar(GetScrollBar());
315 CreateScrollBarOverlayModifier();
316 paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
317 paint->SetTotalItemCount(maxListItemIndex_ + 1);
318 auto layoutDirection = listLayoutProperty->GetNonAutoLayoutDirection();
319 if (layoutDirection == TextDirection::RTL) {
320 paint->SetDirection(true);
321 }
322 auto scrollEffect = GetScrollEdgeEffect();
323 if (scrollEffect && scrollEffect->IsFadeEffect()) {
324 paint->SetEdgeEffect(scrollEffect);
325 }
326 if (!listContentModifier_) {
327 auto host = GetHost();
328 CHECK_NULL_RETURN(host, paint);
329 auto renderContext = host->GetRenderContext();
330 CHECK_NULL_RETURN(renderContext, paint);
331 const auto& geometryNode = host->GetGeometryNode();
332 auto size = renderContext->GetPaintRectWithoutTransform().GetSize();
333 auto& padding = geometryNode->GetPadding();
334 if (padding) {
335 size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
336 }
337 OffsetF offset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
338 listContentModifier_ = AceType::MakeRefPtr<ListContentModifier>(offset, size);
339 }
340 paint->SetLaneGutter(laneGutter_);
341 paint->SetItemsPosition(itemPosition_, pressedItem_);
342 paint->SetContentModifier(listContentModifier_);
343 return paint;
344 }
345
UpdateStartListItemIndex()346 bool ListPattern::UpdateStartListItemIndex()
347 {
348 auto host = GetHost();
349 CHECK_NULL_RETURN(host, false);
350 auto startWrapper = host->GetOrCreateChildByIndex(startIndex_);
351 int32_t startArea = -1;
352 int32_t startItemIndexInGroup = -1;
353 bool startFlagChanged = (startInfo_.index != startIndex_);
354 bool startIsGroup = startWrapper && startWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
355 if (startIsGroup) {
356 auto startPattern = startWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
357 VisibleContentInfo startGroupInfo = startPattern->GetStartListItemIndex();
358 startFlagChanged = startFlagChanged || (startInfo_.area != startGroupInfo.area) ||
359 (startInfo_.indexInGroup != startGroupInfo.indexInGroup);
360 startArea = startGroupInfo.area;
361 startItemIndexInGroup = startGroupInfo.indexInGroup;
362 }
363 startInfo_ = { startIndex_, startArea, startItemIndexInGroup };
364 return startFlagChanged;
365 }
366
UpdateEndListItemIndex()367 bool ListPattern::UpdateEndListItemIndex()
368 {
369 auto host = GetHost();
370 CHECK_NULL_RETURN(host, false);
371 auto endWrapper = host->GetOrCreateChildByIndex(endIndex_);
372 int32_t endArea = -1;
373 int32_t endItemIndexInGroup = -1;
374 bool endFlagChanged = (endInfo_.index != endIndex_);
375 bool endIsGroup = endWrapper && endWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
376 if (endIsGroup) {
377 auto endPattern = endWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
378 VisibleContentInfo endGroupInfo = endPattern->GetEndListItemIndex();
379 endFlagChanged = endFlagChanged || (endInfo_.area != endGroupInfo.area) ||
380 (endInfo_.indexInGroup != endGroupInfo.indexInGroup);
381 endArea = endGroupInfo.area;
382 endItemIndexInGroup = endGroupInfo.indexInGroup;
383 }
384 endInfo_ = { endIndex_, endArea, endItemIndexInGroup };
385 return endFlagChanged;
386 }
387
ProcessEvent(bool indexChanged,float finalOffset,bool isJump)388 void ListPattern::ProcessEvent(bool indexChanged, float finalOffset, bool isJump)
389 {
390 auto host = GetHost();
391 CHECK_NULL_VOID(host);
392 auto listEventHub = host->GetEventHub<ListEventHub>();
393 CHECK_NULL_VOID(listEventHub);
394 paintStateFlag_ = !NearZero(finalOffset) && !isJump;
395 isFramePaintStateValid_ = true;
396 auto onScroll = listEventHub->GetOnScroll();
397 PrintOffsetLog(AceLogTag::ACE_LIST, host->GetId(), finalOffset);
398 if (onScroll) {
399 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
400 FireOnScroll(finalOffset, onScroll);
401 } else {
402 if (!NearZero(finalOffset)) {
403 auto offsetPX = Dimension(finalOffset);
404 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
405 auto source = GetScrollSource();
406 if (source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR ||
407 source == SCROLL_FROM_ANIMATION_CONTROLLER) {
408 source = SCROLL_FROM_NONE;
409 }
410 onScroll(offsetVP, GetScrollState(source));
411 }
412 }
413 }
414 FireObserverOnDidScroll(finalOffset);
415 auto onDidScroll = listEventHub->GetOnDidScroll();
416 if (onDidScroll) {
417 FireOnScroll(finalOffset, onDidScroll);
418 }
419 auto onScrollIndex = listEventHub->GetOnScrollIndex();
420 FireOnScrollIndex(indexChanged, onScrollIndex);
421 auto onScrollVisibleContentChange = listEventHub->GetOnScrollVisibleContentChange();
422 if (onScrollVisibleContentChange) {
423 bool startChanged = UpdateStartListItemIndex();
424 bool endChanged = UpdateEndListItemIndex();
425 if (startChanged || endChanged) {
426 onScrollVisibleContentChange(startInfo_, endInfo_);
427 }
428 }
429 auto onReachStart = listEventHub->GetOnReachStart();
430 FireOnReachStart(onReachStart);
431 auto onReachEnd = listEventHub->GetOnReachEnd();
432 FireOnReachEnd(onReachEnd);
433 OnScrollStop(listEventHub->GetOnScrollStop());
434 }
435
FireOnReachStart(const OnReachEvent & onReachStart)436 void ListPattern::FireOnReachStart(const OnReachEvent& onReachStart)
437 {
438 auto host = GetHost();
439 CHECK_NULL_VOID(host);
440 if (startIndex_ == 0) {
441 bool scrollUpToStart =
442 GreatNotEqual(prevStartOffset_, contentStartOffset_) && LessOrEqual(startMainPos_, contentStartOffset_);
443 bool scrollDownToStart =
444 (startIndexChanged_ || LessNotEqual(prevStartOffset_, contentStartOffset_) || !isInitialized_) &&
445 GreatOrEqual(startMainPos_, contentStartOffset_);
446 if (scrollUpToStart || scrollDownToStart) {
447 FireObserverOnReachStart();
448 CHECK_NULL_VOID(onReachStart);
449 ACE_SCOPED_TRACE("OnReachStart, scrollUpToStart:%u, scrollDownToStart:%u, id:%d, tag:List",
450 scrollUpToStart, scrollDownToStart, static_cast<int32_t>(host->GetAccessibilityId()));
451 onReachStart();
452 AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
453 }
454 }
455 }
456
FireOnReachEnd(const OnReachEvent & onReachEnd)457 void ListPattern::FireOnReachEnd(const OnReachEvent& onReachEnd)
458 {
459 auto host = GetHost();
460 CHECK_NULL_VOID(host);
461 if (endIndex_ == maxListItemIndex_) {
462 float endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
463 // deltaOffset passes through multiple items also needs to fire reachEnd
464 bool scrollUpToEnd =
465 (endIndexChanged_ || (Positive(prevEndOffset_) || !isInitialized_)) && NonPositive(endOffset);
466 bool scrollDownToEnd = Negative(prevEndOffset_) && NonNegative(endOffset);
467 auto scrollSource = GetScrollSource();
468 if (scrollUpToEnd || (scrollDownToEnd && scrollSource != SCROLL_FROM_NONE)) {
469 FireObserverOnReachEnd();
470 CHECK_NULL_VOID(onReachEnd);
471 ACE_SCOPED_TRACE("OnReachEnd, scrollUpToEnd:%u, scrollDownToEnd:%u, scrollSource:%d, id:%d, tag:List",
472 scrollUpToEnd, scrollDownToEnd, scrollSource, static_cast<int32_t>(host->GetAccessibilityId()));
473 onReachEnd();
474 AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
475 }
476 }
477 }
478
FireOnScrollIndex(bool indexChanged,const OnScrollIndexEvent & onScrollIndex)479 void ListPattern::FireOnScrollIndex(bool indexChanged, const OnScrollIndexEvent& onScrollIndex)
480 {
481 CHECK_NULL_VOID(indexChanged && onScrollIndex);
482 int32_t startIndex = startIndex_ == -1 ? 0 : startIndex_;
483 int32_t endIndex = endIndex_ == -1 ? 0 : endIndex_;
484 onScrollIndex(startIndex, endIndex, centerIndex_);
485 }
486
DrivenRender(const RefPtr<LayoutWrapper> & layoutWrapper)487 void ListPattern::DrivenRender(const RefPtr<LayoutWrapper>& layoutWrapper)
488 {
489 auto host = GetHost();
490 CHECK_NULL_VOID(host);
491 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
492 auto listPaintProperty = host->GetPaintProperty<ScrollablePaintProperty>();
493 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
494 auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
495 bool barNeedPaint = GetScrollBar() ? GetScrollBar()->NeedPaint() : false;
496 auto chainAnimation = listLayoutProperty->GetChainAnimation().value_or(false);
497 bool drivenRender = !(axis != Axis::VERTICAL || stickyStyle != V2::StickyStyle::NONE || barNeedPaint ||
498 chainAnimation || !isScrollable_);
499
500 auto renderContext = host->GetRenderContext();
501 CHECK_NULL_VOID(renderContext);
502 renderContext->MarkDrivenRender(drivenRender);
503 if (drivenRender && isFramePaintStateValid_) {
504 // Mark items
505 int32_t indexStep = 0;
506 int32_t startIndex = itemPosition_.empty() ? 0 : itemPosition_.begin()->first;
507 for (auto& pos : itemPosition_) {
508 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
509 CHECK_NULL_VOID(wrapper);
510 auto itemHost = wrapper->GetHostNode();
511 CHECK_NULL_VOID(itemHost);
512 auto itemRenderContext = itemHost->GetRenderContext();
513 CHECK_NULL_VOID(itemRenderContext);
514 itemRenderContext->MarkDrivenRenderItemIndex(startIndex + indexStep);
515 indexStep++;
516 }
517 renderContext->MarkDrivenRenderFramePaintState(paintStateFlag_);
518 isFramePaintStateValid_ = false;
519 }
520 }
521
CheckScrollable()522 void ListPattern::CheckScrollable()
523 {
524 auto host = GetHost();
525 CHECK_NULL_VOID(host);
526 auto hub = host->GetEventHub<EventHub>();
527 CHECK_NULL_VOID(hub);
528 auto gestureHub = hub->GetOrCreateGestureEventHub();
529 CHECK_NULL_VOID(gestureHub);
530 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
531 CHECK_NULL_VOID(listProperty);
532 if (itemPosition_.empty()) {
533 isScrollable_ = false;
534 } else {
535 if ((itemPosition_.begin()->first == 0) && (itemPosition_.rbegin()->first == maxListItemIndex_) &&
536 !IsScrollSnapAlignCenter()) {
537 isScrollable_ = GetAlwaysEnabled() || GreatNotEqual(endMainPos_ - startMainPos_,
538 contentMainSize_ - contentStartOffset_ - contentEndOffset_);
539 } else {
540 isScrollable_ = true;
541 }
542 }
543
544 SetScrollEnabled(isScrollable_);
545
546 if (!listProperty->GetScrollEnabled().value_or(isScrollable_)) {
547 SetScrollEnabled(false);
548 }
549 }
550
CreateLayoutAlgorithm()551 RefPtr<LayoutAlgorithm> ListPattern::CreateLayoutAlgorithm()
552 {
553 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
554 CHECK_NULL_RETURN(listLayoutProperty, nullptr);
555 RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm;
556 if (listLayoutProperty->HasLanes() || listLayoutProperty->HasLaneMinLength() ||
557 listLayoutProperty->HasLaneMaxLength()) {
558 auto lanesLayoutAlgorithm = MakeRefPtr<ListLanesLayoutAlgorithm>();
559 RefreshLanesItemRange();
560 lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
561 lanesLayoutAlgorithm->SetLanes(lanes_);
562 listLayoutAlgorithm.Swap(lanesLayoutAlgorithm);
563 } else {
564 listLayoutAlgorithm.Swap(MakeRefPtr<ListLayoutAlgorithm>());
565 }
566 if (!posMap_) {
567 posMap_ = MakeRefPtr<ListPositionMap>();
568 }
569 if (childrenSize_) {
570 listLayoutAlgorithm->SetListChildrenMainSize(childrenSize_);
571 listLayoutAlgorithm->SetListPositionMap(posMap_);
572 }
573 if (!isInitialized_) {
574 jumpIndex_ = listLayoutProperty->GetInitialIndex().value_or(0);
575 if (NeedScrollSnapAlignEffect()) {
576 scrollAlign_ = GetScrollAlignByScrollSnapAlign();
577 }
578 }
579 if (jumpIndex_) {
580 listLayoutAlgorithm->SetIndex(jumpIndex_.value());
581 listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
582 jumpIndex_.reset();
583 }
584 if (targetIndex_) {
585 listLayoutAlgorithm->SetTargetIndex(targetIndex_.value());
586 listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
587 }
588 if (jumpIndexInGroup_) {
589 listLayoutAlgorithm->SetIndexInGroup(jumpIndexInGroup_.value());
590 jumpIndexInGroup_.reset();
591 }
592 if (targetIndexInGroup_) {
593 listLayoutAlgorithm->SetTargetIndexInGroup(targetIndexInGroup_.value());
594 }
595 if (predictSnapOffset_.has_value()) {
596 listLayoutAlgorithm->SetPredictSnapOffset(predictSnapOffset_.value());
597 listLayoutAlgorithm->SetScrollSnapVelocity(scrollSnapVelocity_);
598 }
599 listLayoutAlgorithm->SetTotalOffset(GetTotalOffset());
600 listLayoutAlgorithm->SetCurrentDelta(currentDelta_);
601 listLayoutAlgorithm->SetIsNeedCheckOffset(isNeedCheckOffset_);
602 listLayoutAlgorithm->SetItemsPosition(itemPosition_);
603 listLayoutAlgorithm->SetPrevContentMainSize(contentMainSize_);
604 listLayoutAlgorithm->SetPrevContentStartOffset(contentStartOffset_);
605 listLayoutAlgorithm->SetPrevContentEndOffset(contentEndOffset_);
606 if (IsOutOfBoundary(false) && GetScrollSource() != SCROLL_FROM_AXIS) {
607 listLayoutAlgorithm->SetOverScrollFeature();
608 }
609 listLayoutAlgorithm->SetIsSpringEffect(IsScrollableSpringEffect());
610 listLayoutAlgorithm->SetCanOverScroll(CanOverScroll(GetScrollSource()));
611 if (chainAnimation_) {
612 SetChainAnimationLayoutAlgorithm(listLayoutAlgorithm, listLayoutProperty);
613 SetChainAnimationToPosMap();
614 }
615 if (predictSnapEndPos_.has_value()) {
616 listLayoutAlgorithm->SetPredictSnapEndPosition(predictSnapEndPos_.value());
617 }
618 return listLayoutAlgorithm;
619 }
620
SetChainAnimationToPosMap()621 void ListPattern::SetChainAnimationToPosMap()
622 {
623 CHECK_NULL_VOID(posMap_);
624 posMap_->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
625 auto list = weak.Upgrade();
626 CHECK_NULL_RETURN(list, 0.0f);
627 return list->GetChainDelta(index);
628 });
629 }
630
SetChainAnimationLayoutAlgorithm(RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm,RefPtr<ListLayoutProperty> listLayoutProperty)631 void ListPattern::SetChainAnimationLayoutAlgorithm(
632 RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm, RefPtr<ListLayoutProperty> listLayoutProperty)
633 {
634 CHECK_NULL_VOID(listLayoutAlgorithm);
635 CHECK_NULL_VOID(listLayoutProperty);
636 listLayoutAlgorithm->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
637 auto list = weak.Upgrade();
638 CHECK_NULL_RETURN(list, 0.0f);
639 return list->GetChainDelta(index);
640 });
641 if (!listLayoutProperty->GetSpace().has_value() && chainAnimation_) {
642 listLayoutAlgorithm->SetChainInterval(CHAIN_INTERVAL_DEFAULT.ConvertToPx());
643 }
644 }
645
IsScrollSnapAlignCenter() const646 bool ListPattern::IsScrollSnapAlignCenter() const
647 {
648 auto host = GetHost();
649 CHECK_NULL_RETURN(host, false);
650 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
651 CHECK_NULL_RETURN(listProperty, false);
652 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
653 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
654 return true;
655 }
656
657 return false;
658 }
659
NeedScrollSnapAlignEffect() const660 bool ListPattern::NeedScrollSnapAlignEffect() const
661 {
662 auto host = GetHost();
663 CHECK_NULL_RETURN(host, false);
664 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
665 CHECK_NULL_RETURN(listProperty, false);
666 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
667 if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
668 return false;
669 }
670
671 return true;
672 }
673
IsAtTop() const674 bool ListPattern::IsAtTop() const
675 {
676 bool groupAtStart = true;
677 bool groupAtEnd = true;
678 GetListItemGroupEdge(groupAtStart, groupAtEnd);
679 int32_t startIndex = startIndex_;
680 float startMainPos = startMainPos_;
681 return (startIndex == 0 && groupAtStart) &&
682 NonNegative(startMainPos - currentDelta_ + GetChainDelta(0) - contentStartOffset_);
683 }
684
IsAtBottom() const685 bool ListPattern::IsAtBottom() const
686 {
687 bool groupAtStart = true;
688 bool groupAtEnd = true;
689 GetListItemGroupEdge(groupAtStart, groupAtEnd);
690 int32_t endIndex = endIndex_;
691 float endMainPos = endMainPos_;
692 return (endIndex == maxListItemIndex_ && groupAtEnd) &&
693 LessOrEqual(endMainPos - currentDelta_ + GetChainDelta(endIndex), contentMainSize_ - contentEndOffset_);
694 }
695
GetListItemGroupEdge(bool & groupAtStart,bool & groupAtEnd) const696 void ListPattern::GetListItemGroupEdge(bool& groupAtStart, bool& groupAtEnd) const
697 {
698 if (itemPosition_.empty()) {
699 return;
700 }
701 if (startIndex_ == 0 && itemPosition_.begin()->second.isGroup) {
702 auto& groupInfo = itemPosition_.begin()->second.groupInfo;
703 groupAtStart = groupInfo && groupInfo.value().atStart;
704 }
705 if (endIndex_ == maxListItemIndex_ && itemPosition_.rbegin()->second.isGroup) {
706 auto& groupInfo = itemPosition_.rbegin()->second.groupInfo;
707 groupAtEnd = groupInfo && groupInfo.value().atEnd;
708 }
709 }
710
GetOffsetWithLimit(float offset) const711 float ListPattern::GetOffsetWithLimit(float offset) const
712 {
713 auto currentOffset = GetTotalOffset() + contentStartOffset_;
714 if (Positive(offset)) {
715 return std::min(currentOffset, offset);
716 } else if (Negative(offset)) {
717 auto remainHeight = GetTotalHeight() - currentOffset;
718 return std::max(offset, -remainHeight);
719 }
720 return 0;
721 }
722
GetOverScrollOffset(double delta) const723 OverScrollOffset ListPattern::GetOverScrollOffset(double delta) const
724 {
725 OverScrollOffset offset = { 0, 0 };
726 bool groupAtStart = true;
727 bool groupAtEnd = true;
728 GetListItemGroupEdge(groupAtStart, groupAtEnd);
729
730 int32_t startIndex = startIndex_;
731 float startMainPos = startMainPos_;
732 int32_t endIndex = endIndex_;
733 float endMainPos = endMainPos_;
734 if (startIndex == 0 && groupAtStart) {
735 offset.start = GetStartOverScrollOffset(delta, startMainPos);
736 }
737 if (endIndex == maxListItemIndex_ && groupAtEnd) {
738 offset.end = GetEndOverScrollOffset(delta, endMainPos, startMainPos);
739 }
740 return offset;
741 }
742
GetStartOverScrollOffset(float offset,float startMainPos) const743 float ListPattern::GetStartOverScrollOffset(float offset, float startMainPos) const
744 {
745 float startOffset = 0.0f;
746 float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(0, -offset) : 0.f;
747 auto startPos = startMainPos + ChainDelta - currentDelta_;
748 auto newStartPos = startPos + offset;
749 if (startPos > contentStartOffset_ && newStartPos > contentStartOffset_) {
750 startOffset = offset;
751 }
752 if (startPos > contentStartOffset_ && newStartPos <= contentStartOffset_) {
753 startOffset = contentStartOffset_ - startPos;
754 }
755 if (startPos <= contentStartOffset_ && newStartPos > contentStartOffset_) {
756 startOffset = newStartPos - contentStartOffset_;
757 }
758 return startOffset;
759 }
760
GetEndOverScrollOffset(float offset,float endMainPos,float startMainPos) const761 float ListPattern::GetEndOverScrollOffset(float offset, float endMainPos, float startMainPos) const
762 {
763 float endOffset = 0.0f;
764 float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(maxListItemIndex_, -offset) : 0.f;
765 auto endPos = endMainPos + ChainDelta - currentDelta_;
766 auto contentEndPos = contentMainSize_ - contentEndOffset_;
767 auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
768 if (GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
769 endPos = startMainPos + contentMainSize;
770 }
771 auto newEndPos = endPos + offset;
772 if (endPos < contentEndPos && newEndPos < contentEndPos) {
773 endOffset = offset;
774 }
775 if (endPos < contentEndPos && newEndPos >= contentEndPos) {
776 endOffset = contentEndPos - endPos;
777 }
778 if (endPos >= contentEndPos && newEndPos < contentEndPos) {
779 endOffset = newEndPos - contentEndPos;
780 }
781 return endOffset;
782 }
783
GetOutBoundaryOffset(bool useCurrentDelta) const784 OverScrollOffset ListPattern::GetOutBoundaryOffset(bool useCurrentDelta) const
785 {
786 OverScrollOffset offset = { 0, 0 };
787 bool groupAtStart = true;
788 bool groupAtEnd = true;
789 GetListItemGroupEdge(groupAtStart, groupAtEnd);
790
791 int32_t startIndex = startIndex_;
792 float startMainPos = startMainPos_;
793 int32_t endIndex = endIndex_;
794 float endMainPos = endMainPos_;
795 if (startIndex == 0 && groupAtStart) {
796 if (useCurrentDelta) {
797 offset.start = startMainPos - currentDelta_ + GetChainDelta(0) - contentStartOffset_;
798 } else {
799 offset.start = startMainPos + GetChainDelta(0) - contentStartOffset_;
800 }
801 offset.start = std::max(offset.start, 0.0);
802 }
803 if (endIndex >= maxListItemIndex_ && groupAtEnd) {
804 endMainPos = endMainPos + GetChainDelta(endIndex);
805 auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
806 if (startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
807 endMainPos = startMainPos + contentMainSize;
808 }
809 if (useCurrentDelta) {
810 offset.end = contentMainSize_ - contentEndOffset_ - (endMainPos - currentDelta_);
811 } else {
812 offset.end = contentMainSize_ - contentEndOffset_ - endMainPos;
813 }
814 offset.end = std::max(offset.end, 0.0);
815 }
816 return offset;
817 }
818
UpdateCurrentOffset(float offset,int32_t source)819 bool ListPattern::UpdateCurrentOffset(float offset, int32_t source)
820 {
821 // check edgeEffect is not springEffect
822 if (!jumpIndex_.has_value() && !targetIndex_.has_value() && !HandleEdgeEffect(offset, source, GetContentSize())) {
823 if (IsOutOfBoundary(false)) {
824 MarkDirtyNodeSelf();
825 }
826 return false;
827 }
828 SetScrollSource(source);
829 FireAndCleanScrollingListener();
830 auto lastDelta = currentDelta_;
831 currentDelta_ = currentDelta_ - offset;
832 if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING) {
833 isNeedCheckOffset_ = true;
834 }
835 if (!NearZero(offset)) {
836 MarkDirtyNodeSelf();
837 }
838 if (itemPosition_.empty() || !IsOutOfBoundary() || !isScrollable_) {
839 auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
840 currentDelta_ = lastDelta + userOffset;
841 return true;
842 }
843
844 if (GetScrollSource() == SCROLL_FROM_UPDATE) {
845 auto res = GetOutBoundaryOffset(true);
846 // over scroll in drag update from normal to over scroll.
847 float overScroll = std::max(res.start, res.end);
848 // adjust offset.
849 auto friction = ScrollablePattern::CalculateFriction(std::abs(overScroll) / contentMainSize_);
850 currentDelta_ = currentDelta_ * friction;
851 }
852
853 auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
854 currentDelta_ = lastDelta + userOffset;
855 return true;
856 }
857
MarkDirtyNodeSelf()858 void ListPattern::MarkDirtyNodeSelf()
859 {
860 auto host = GetHost();
861 CHECK_NULL_VOID(host);
862 if (!crossMatchChild_) {
863 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
864 } else {
865 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
866 }
867 }
868
OnScrollEndCallback()869 void ListPattern::OnScrollEndCallback()
870 {
871 if (AnimateStoped()) {
872 scrollStop_ = true;
873 MarkDirtyNodeSelf();
874 }
875 }
876
GetContentSize() const877 SizeF ListPattern::GetContentSize() const
878 {
879 auto host = GetHost();
880 CHECK_NULL_RETURN(host, SizeF());
881 auto geometryNode = host->GetGeometryNode();
882 CHECK_NULL_RETURN(geometryNode, SizeF());
883 auto renderContext = host->GetRenderContext();
884 CHECK_NULL_RETURN(renderContext, SizeF());
885 auto size = renderContext->GetPaintRectWithoutTransform().GetSize();
886 auto& padding = geometryNode->GetPadding();
887 if (padding) {
888 size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
889 }
890 return size;
891 }
892
IsOutOfBoundary(bool useCurrentDelta)893 bool ListPattern::IsOutOfBoundary(bool useCurrentDelta)
894 {
895 if (itemPosition_.empty()) {
896 return false;
897 }
898 auto res = GetOutBoundaryOffset(useCurrentDelta);
899 // over scroll in drag update from normal to over scroll.
900 return Positive(res.start) || Positive(res.end);
901 }
902
OnScrollCallback(float offset,int32_t source)903 bool ListPattern::OnScrollCallback(float offset, int32_t source)
904 {
905 if (source == SCROLL_FROM_START) {
906 auto item = swiperItem_.Upgrade();
907 if (item) {
908 item->ResetSwipeStatus();
909 }
910 FireOnScrollStart();
911 return true;
912 }
913 ProcessDragUpdate(offset, source);
914 return UpdateCurrentOffset(offset, source);
915 }
916
OnScrollSnapCallback(double targetOffset,double velocity)917 bool ListPattern::OnScrollSnapCallback(double targetOffset, double velocity)
918 {
919 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
920 CHECK_NULL_RETURN(listProperty, false);
921 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
922 if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
923 return false;
924 }
925 if (AnimateRunning()) {
926 return false;
927 }
928 if (!GetIsDragging()) {
929 snapTrigOnScrollStart_ = true;
930 }
931 predictSnapOffset_ = targetOffset;
932 scrollSnapVelocity_ = velocity;
933 MarkDirtyNodeSelf();
934 return true;
935 }
936
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)937 void ListPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
938 {
939 scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
940 auto list = weak.Upgrade();
941 CHECK_NULL_RETURN(list, 0.0);
942 return list->startMainPos_ - list->currentDelta_;
943 });
944 scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
945 auto list = weak.Upgrade();
946 auto endPos = list->endMainPos_;
947 auto startPos = list->startMainPos_;
948 float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
949 return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
950 });
951 scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
952 auto list = weak.Upgrade();
953 CHECK_NULL_RETURN(list, 0.0);
954 return list->contentStartOffset_;
955 });
956 scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
957 auto list = weak.Upgrade();
958 auto endPos = list->endMainPos_;
959 auto startPos = list->startMainPos_;
960 float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
961 return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
962 });
963 scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
964 auto list = weak.Upgrade();
965 CHECK_NULL_RETURN(list, 0.0);
966 return list->contentStartOffset_;
967 });
968 }
969
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)970 void ListPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
971 {
972 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
973 auto pattern = wp.Upgrade();
974 CHECK_NULL_RETURN(pattern, false);
975 return pattern->OnKeyEvent(event);
976 };
977 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
978 }
979
OnKeyEvent(const KeyEvent & event)980 bool ListPattern::OnKeyEvent(const KeyEvent& event)
981 {
982 if (event.action != KeyAction::DOWN) {
983 return false;
984 }
985 if (event.code == KeyCode::KEY_PAGE_DOWN) {
986 ScrollPage(false);
987 return true;
988 }
989 if (event.code == KeyCode::KEY_PAGE_UP) {
990 ScrollPage(true);
991 return true;
992 }
993 return HandleDirectionKey(event);
994 }
995
HandleDirectionKey(const KeyEvent & event)996 bool ListPattern::HandleDirectionKey(const KeyEvent& event)
997 {
998 return false;
999 }
1000
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)1001 WeakPtr<FocusHub> ListPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
1002 {
1003 auto curFocus = currentFocusNode.Upgrade();
1004 CHECK_NULL_RETURN(curFocus, nullptr);
1005 auto curFrame = curFocus->GetFrameNode();
1006 CHECK_NULL_RETURN(curFrame, nullptr);
1007 auto curPattern = curFrame->GetPattern();
1008 CHECK_NULL_RETURN(curPattern, nullptr);
1009 auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
1010 CHECK_NULL_RETURN(curItemPattern, nullptr);
1011 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
1012 CHECK_NULL_RETURN(listProperty, nullptr);
1013
1014 auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
1015 auto curIndex = curItemPattern->GetIndexInList();
1016 auto curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
1017 auto curListItemGroupPara = GetListItemGroupParameter(curFrame);
1018 if (curIndex < 0 || curIndex > maxListItemIndex_) {
1019 return nullptr;
1020 }
1021
1022 auto moveStep = 0;
1023 auto nextIndex = curIndex;
1024 auto nextIndexInGroup = curIndexInGroup;
1025 if (lanes_ <= 1) {
1026 if ((isVertical && step == FocusStep::UP_END) || (!isVertical && step == FocusStep::LEFT_END)) {
1027 moveStep = 1;
1028 nextIndex = 0;
1029 nextIndexInGroup = -1;
1030 } else if ((isVertical && step == FocusStep::DOWN_END) || (!isVertical && step == FocusStep::RIGHT_END)) {
1031 moveStep = -1;
1032 nextIndex = maxListItemIndex_;
1033 nextIndexInGroup = -1;
1034 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT) ||
1035 (step == FocusStep::TAB)) {
1036 moveStep = 1;
1037 if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1038 nextIndex = curIndex + moveStep;
1039 nextIndexInGroup = -1;
1040 } else {
1041 nextIndexInGroup = curIndexInGroup + moveStep;
1042 }
1043 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT) ||
1044 (step == FocusStep::SHIFT_TAB)) {
1045 moveStep = -1;
1046 if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1047 nextIndex = curIndex + moveStep;
1048 nextIndexInGroup = -1;
1049 } else {
1050 nextIndexInGroup = curIndexInGroup + moveStep;
1051 }
1052 }
1053 } else {
1054 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
1055 moveStep = 1;
1056 nextIndex = 0;
1057 nextIndexInGroup = -1;
1058 } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
1059 moveStep = -1;
1060 nextIndex = maxListItemIndex_;
1061 nextIndexInGroup = -1;
1062 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
1063 if (curIndexInGroup == -1) {
1064 moveStep = lanes_;
1065 nextIndex = curIndex + moveStep;
1066 nextIndexInGroup = -1;
1067 } else {
1068 moveStep = curListItemGroupPara.lanes;
1069 nextIndexInGroup = curIndexInGroup + moveStep;
1070 }
1071 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
1072 if (curIndexInGroup == -1) {
1073 moveStep = -lanes_;
1074 nextIndex = curIndex + moveStep;
1075 nextIndexInGroup = -1;
1076 } else {
1077 moveStep = -curListItemGroupPara.lanes;
1078 nextIndexInGroup = curIndexInGroup + moveStep;
1079 }
1080 } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1081 moveStep = 1;
1082 if (((curIndexInGroup == -1) && ((curIndex - (lanes_ - 1)) % lanes_ != 0)) ||
1083 ((curIndexInGroup != -1) &&
1084 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes == 0))) {
1085 nextIndex = curIndex + moveStep;
1086 nextIndexInGroup = -1;
1087 } else if ((curIndexInGroup != -1) &&
1088 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes != 0)) {
1089 nextIndexInGroup = curIndexInGroup + moveStep;
1090 }
1091 } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1092 moveStep = -1;
1093 if (((curIndexInGroup == -1) && (curIndex % lanes_ != 0)) ||
1094 ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes == 0))) {
1095 nextIndex = curIndex + moveStep;
1096 nextIndexInGroup = -1;
1097 } else if ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes != 0)) {
1098 nextIndexInGroup = curIndexInGroup + moveStep;
1099 }
1100 } else if (step == FocusStep::TAB) {
1101 moveStep = 1;
1102 if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1103 nextIndex = curIndex + moveStep;
1104 nextIndexInGroup = -1;
1105 } else {
1106 nextIndexInGroup = curIndexInGroup + moveStep;
1107 }
1108 } else if (step == FocusStep::SHIFT_TAB) {
1109 moveStep = -1;
1110 if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1111 nextIndex = curIndex + moveStep;
1112 nextIndexInGroup = -1;
1113 } else {
1114 nextIndexInGroup = curIndexInGroup + moveStep;
1115 }
1116 }
1117 }
1118 while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
1119 if ((nextIndex == curIndex) && (curIndexInGroup == nextIndexInGroup)) {
1120 return nullptr;
1121 }
1122 auto nextFocusNode =
1123 ScrollAndFindFocusNode(nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep, step);
1124 if (nextFocusNode.Upgrade()) {
1125 return nextFocusNode;
1126 }
1127 if (nextIndexInGroup > -1) {
1128 nextIndexInGroup += moveStep;
1129 } else {
1130 nextIndex += moveStep;
1131 }
1132 }
1133 return nullptr;
1134 }
1135
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarGroupIndex)1136 WeakPtr<FocusHub> ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarGroupIndex)
1137 {
1138 auto listFrame = GetHost();
1139 CHECK_NULL_RETURN(listFrame, nullptr);
1140 auto listFocus = listFrame->GetFocusHub();
1141 CHECK_NULL_RETURN(listFocus, nullptr);
1142 auto childFocusList = listFocus->GetChildren();
1143 for (const auto& childFocus : childFocusList) {
1144 if (!childFocus->IsFocusable()) {
1145 continue;
1146 }
1147 auto childFrame = childFocus->GetFrameNode();
1148 if (!childFrame) {
1149 continue;
1150 }
1151 auto childPattern = childFrame->GetPattern();
1152 if (!childPattern) {
1153 continue;
1154 }
1155 auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
1156 if (!childItemPattern) {
1157 continue;
1158 }
1159 auto curIndex = childItemPattern->GetIndexInList();
1160 auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
1161 if (curIndex == tarMainIndex && curIndexInGroup == tarGroupIndex) {
1162 return AceType::WeakClaim(AceType::RawPtr(childFocus));
1163 }
1164 }
1165 return nullptr;
1166 }
1167
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1168 bool ListPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1169 {
1170 CHECK_NULL_RETURN(focusFrameNode, false);
1171 auto focusPattern = focusFrameNode->GetPattern<ListItemPattern>();
1172 CHECK_NULL_RETURN(focusPattern, false);
1173 auto curIndex = focusPattern->GetIndexInList();
1174 ScrollToIndex(curIndex, smooth_, ScrollAlign::AUTO);
1175 auto pipeline = GetContext();
1176 if (pipeline) {
1177 pipeline->FlushUITasks();
1178 }
1179 return true;
1180 }
1181
GetScrollOffsetAbility()1182 std::pair<std::function<bool(float)>, Axis> ListPattern::GetScrollOffsetAbility()
1183 {
1184 return { [wp = WeakClaim(this)](float moveOffset) -> bool {
1185 auto pattern = wp.Upgrade();
1186 CHECK_NULL_RETURN(pattern, false);
1187 pattern->ScrollBy(-moveOffset);
1188 return true;
1189 },
1190 GetAxis() };
1191 }
1192
GetScrollIndexAbility()1193 std::function<bool(int32_t)> ListPattern::GetScrollIndexAbility()
1194 {
1195 return [wp = WeakClaim(this)](int32_t index) -> bool {
1196 auto pattern = wp.Upgrade();
1197 CHECK_NULL_RETURN(pattern, false);
1198 if (index == FocusHub::SCROLL_TO_HEAD) {
1199 pattern->ScrollToIndex(0, false, ScrollAlign::START);
1200 } else if (index == FocusHub::SCROLL_TO_TAIL) {
1201 pattern->ScrollToIndex(ListLayoutAlgorithm::LAST_ITEM, false, ScrollAlign::END);
1202 } else {
1203 pattern->ScrollToIndex(index, false, ScrollAlign::AUTO);
1204 }
1205 return true;
1206 };
1207 }
1208
ScrollAndFindFocusNode(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step)1209 WeakPtr<FocusHub> ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1210 int32_t curIndexInGroup, int32_t moveStep, FocusStep step)
1211 {
1212 auto isScrollIndex = ScrollListForFocus(nextIndex, curIndex, nextIndexInGroup);
1213 auto groupIndexInGroup = ScrollListItemGroupForFocus(nextIndex, nextIndexInGroup,
1214 curIndexInGroup, moveStep, step, isScrollIndex);
1215
1216 return groupIndexInGroup ? GetChildFocusNodeByIndex(nextIndex, nextIndexInGroup) : nullptr;
1217 }
1218
ScrollListForFocus(int32_t nextIndex,int32_t curIndex,int32_t nextIndexInGroup)1219 bool ListPattern::ScrollListForFocus(int32_t nextIndex, int32_t curIndex, int32_t nextIndexInGroup)
1220 {
1221 auto isScrollIndex = false;
1222 auto pipeline = PipelineContext::GetCurrentContext();
1223 CHECK_NULL_RETURN(pipeline, isScrollIndex);
1224 if (nextIndex < startIndex_) {
1225 if (nextIndexInGroup == -1) {
1226 isScrollIndex = true;
1227 ScrollToIndex(nextIndex, smooth_, ScrollAlign::START);
1228 pipeline->FlushUITasks();
1229 } else {
1230 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1231 pipeline->FlushUITasks();
1232 }
1233 } else if (nextIndex > endIndex_) {
1234 if (nextIndexInGroup == -1) {
1235 isScrollIndex = true;
1236 ScrollToIndex(nextIndex, smooth_, ScrollAlign::END);
1237 pipeline->FlushUITasks();
1238 } else {
1239 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1240 pipeline->FlushUITasks();
1241 }
1242 }
1243 return isScrollIndex;
1244 }
1245
ScrollListItemGroupForFocus(int32_t nextIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step,bool isScrollIndex)1246 bool ListPattern::ScrollListItemGroupForFocus(int32_t nextIndex, int32_t& nextIndexInGroup, int32_t curIndexInGroup,
1247 int32_t moveStep, FocusStep step, bool isScrollIndex)
1248 {
1249 auto groupIndexInGroup = true;
1250 auto pipeline = PipelineContext::GetCurrentContext();
1251 CHECK_NULL_RETURN(pipeline, groupIndexInGroup);
1252 RefPtr<FrameNode> nextIndexNode;
1253 auto isNextInGroup = IsListItemGroup(nextIndex, nextIndexNode);
1254 CHECK_NULL_RETURN(nextIndexNode, groupIndexInGroup);
1255 if (!isNextInGroup) {
1256 nextIndexInGroup = -1;
1257 return groupIndexInGroup;
1258 }
1259 auto nextListItemGroupPara = GetListItemGroupParameter(nextIndexNode);
1260 if (nextIndexInGroup == -1) {
1261 auto scrollAlign = ScrollAlign::END;
1262 nextIndexInGroup = moveStep < 0 ? nextListItemGroupPara.itemEndIndex : 0;
1263 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END) || (step == FocusStep::DOWN_END) ||
1264 (step == FocusStep::RIGHT_END)) {
1265 scrollAlign = moveStep < 0 ? ScrollAlign::END : ScrollAlign::START;
1266 } else {
1267 scrollAlign = moveStep < 0 ? ScrollAlign::START : ScrollAlign::END;
1268 }
1269 if ((nextIndexInGroup < nextListItemGroupPara.displayStartIndex) ||
1270 (nextIndexInGroup > nextListItemGroupPara.displayEndIndex) || (isScrollIndex)) {
1271 ScrollToIndex(nextIndex, nextIndexInGroup, scrollAlign);
1272 pipeline->FlushUITasks();
1273 }
1274 } else if (nextIndexInGroup > nextListItemGroupPara.itemEndIndex) {
1275 nextIndexInGroup = -1;
1276 groupIndexInGroup = false;
1277 } else {
1278 if ((nextIndexInGroup < curIndexInGroup) && (nextIndexInGroup < nextListItemGroupPara.displayStartIndex)) {
1279 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1280 pipeline->FlushUITasks();
1281 } else if ((nextIndexInGroup > curIndexInGroup) && (nextIndexInGroup > nextListItemGroupPara.displayEndIndex)) {
1282 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1283 pipeline->FlushUITasks();
1284 }
1285 }
1286 return groupIndexInGroup;
1287 }
1288
OnAnimateStop()1289 void ListPattern::OnAnimateStop()
1290 {
1291 if (!GetIsDragging() || GetScrollAbort()) {
1292 scrollStop_ = true;
1293 MarkDirtyNodeSelf();
1294 isScrollEnd_ = true;
1295 }
1296 scrollTarget_.reset();
1297 }
1298
ScrollTo(float position)1299 void ListPattern::ScrollTo(float position)
1300 {
1301 StopAnimate();
1302 jumpIndex_.reset();
1303 targetIndex_.reset();
1304 currentDelta_ = 0.0f;
1305 UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1306 MarkDirtyNodeSelf();
1307 isScrollEnd_ = true;
1308 }
1309
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)1310 void ListPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
1311 {
1312 SetScrollSource(SCROLL_FROM_JUMP);
1313 if (!smooth) {
1314 StopAnimate();
1315 }
1316 if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1317 currentDelta_ = 0.0f;
1318 smooth_ = smooth;
1319 if (smooth_) {
1320 SetExtraOffset(extraOffset);
1321 if (!AnimateToTarget(index, std::nullopt, align)) {
1322 targetIndex_ = index;
1323 scrollAlign_ = align;
1324 }
1325 } else {
1326 if (extraOffset.has_value()) {
1327 currentDelta_ = extraOffset.value();
1328 }
1329 jumpIndex_ = index;
1330 scrollAlign_ = align;
1331 }
1332 MarkDirtyNodeSelf();
1333 }
1334 isScrollEnd_ = true;
1335 FireAndCleanScrollingListener();
1336 }
1337
CheckTargetValid(int32_t index,int32_t indexInGroup)1338 bool ListPattern::CheckTargetValid(int32_t index, int32_t indexInGroup)
1339 {
1340 auto host = GetHost();
1341 auto totalItemCount = host->GetTotalChildCount();
1342 if ((index < 0) || (index >= totalItemCount)) {
1343 return false;
1344 }
1345 auto groupWrapper = host->GetOrCreateChildByIndex(index);
1346 CHECK_NULL_RETURN(groupWrapper, false);
1347 if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
1348 return false;
1349 }
1350 auto groupNode = groupWrapper->GetHostNode();
1351 CHECK_NULL_RETURN(groupNode, false);
1352 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1353 CHECK_NULL_RETURN(groupPattern, false);
1354 auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
1355 if ((indexInGroup < 0) || (indexInGroup >= groupItemCount)) {
1356 return false;
1357 }
1358 return true;
1359 }
1360
ScrollToItemInGroup(int32_t index,int32_t indexInGroup,bool smooth,ScrollAlign align)1361 void ListPattern::ScrollToItemInGroup(int32_t index, int32_t indexInGroup, bool smooth, ScrollAlign align)
1362 {
1363 SetScrollSource(SCROLL_FROM_JUMP);
1364 StopAnimate();
1365 if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1366 currentDelta_ = 0.0f;
1367 smooth_ = smooth;
1368 if (smooth_) {
1369 if (!AnimateToTarget(index, indexInGroup, align)) {
1370 if (CheckTargetValid(index, indexInGroup)) {
1371 targetIndex_ = index;
1372 currentDelta_ = 0;
1373 targetIndexInGroup_ = indexInGroup;
1374 scrollAlign_ = align;
1375 }
1376 }
1377 } else {
1378 jumpIndex_ = index;
1379 jumpIndexInGroup_ = indexInGroup;
1380 scrollAlign_ = align;
1381 }
1382 MarkDirtyNodeSelf();
1383 }
1384 isScrollEnd_ = true;
1385 FireAndCleanScrollingListener();
1386 }
1387
GetListItemAnimatePos(float startPos,float endPos,ScrollAlign align,float & targetPos)1388 bool ListPattern::GetListItemAnimatePos(float startPos, float endPos, ScrollAlign align, float& targetPos)
1389 {
1390 switch (align) {
1391 case ScrollAlign::START:
1392 case ScrollAlign::NONE:
1393 targetPos = startPos;
1394 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1395 targetPos -= contentStartOffset_;
1396 }
1397 break;
1398 case ScrollAlign::CENTER:
1399 targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1400 break;
1401 case ScrollAlign::END:
1402 targetPos = endPos - contentMainSize_;
1403 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1404 targetPos += contentEndOffset_;
1405 }
1406 break;
1407 case ScrollAlign::AUTO:
1408 targetPos = CalculateTargetPos(startPos, endPos);
1409 break;
1410 }
1411 return true;
1412 }
1413
GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index,float startPos,float endPos,ScrollAlign align,float & targetPos)1414 bool ListPattern::GetListItemGroupAnimatePosWithoutIndexInGroup(
1415 int32_t index, float startPos, float endPos, ScrollAlign align, float& targetPos)
1416 {
1417 auto host = GetHost();
1418 CHECK_NULL_RETURN(host, false);
1419 auto groupWrapper = host->GetChildByIndex(index);
1420 CHECK_NULL_RETURN(groupWrapper, false);
1421 auto groupNode = groupWrapper->GetHostNode();
1422 CHECK_NULL_RETURN(groupNode, false);
1423 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1424 CHECK_NULL_RETURN(groupPattern, false);
1425 auto groupLayoutProperty = groupNode->GetLayoutProperty<ListItemGroupLayoutProperty>();
1426 CHECK_NULL_RETURN(groupLayoutProperty, false);
1427 auto visible = groupLayoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
1428
1429 switch (align) {
1430 case ScrollAlign::START:
1431 case ScrollAlign::NONE:
1432 if (visible != VisibleType::GONE && !groupPattern->IsDisplayStart()) {
1433 return false;
1434 }
1435 targetPos = startPos;
1436 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1437 targetPos -= contentStartOffset_;
1438 }
1439 break;
1440 case ScrollAlign::CENTER:
1441 if (visible != VisibleType::GONE && (!groupPattern->IsDisplayStart() || !groupPattern->IsDisplayEnd())) {
1442 return false;
1443 }
1444 targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1445 break;
1446 case ScrollAlign::END:
1447 if (visible != VisibleType::GONE && !groupPattern->IsDisplayEnd()) {
1448 return false;
1449 }
1450 targetPos = endPos - contentMainSize_;
1451 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1452 targetPos += contentEndOffset_;
1453 }
1454 break;
1455 case ScrollAlign::AUTO:
1456 if (targetIndex_.has_value()) {
1457 targetPos = CalculateTargetPos(startPos, endPos);
1458 return true;
1459 }
1460 return false;
1461 }
1462
1463 return true;
1464 }
1465
GetListItemGroupAnimatePosWithIndexInGroup(int32_t index,int32_t indexInGroup,float startPos,ScrollAlign align,float & targetPos)1466 bool ListPattern::GetListItemGroupAnimatePosWithIndexInGroup(
1467 int32_t index, int32_t indexInGroup, float startPos, ScrollAlign align, float& targetPos)
1468 {
1469 auto host = GetHost();
1470 CHECK_NULL_RETURN(host, false);
1471 auto groupWrapper = host->GetChildByIndex(index);
1472 CHECK_NULL_RETURN(groupWrapper, false);
1473 auto groupNode = groupWrapper->GetHostNode();
1474 CHECK_NULL_RETURN(groupNode, false);
1475 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1476 CHECK_NULL_RETURN(groupPattern, false);
1477 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
1478 CHECK_NULL_RETURN(listLayoutProperty, false);
1479 auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1480 auto itemsPosInGroup = groupPattern->GetItemPosition();
1481 auto it = itemsPosInGroup.find(indexInGroup);
1482 if (it == itemsPosInGroup.end()) {
1483 return false;
1484 }
1485 auto axis = GetAxis();
1486 std::optional<float> padding;
1487 std::optional<float> margin;
1488 if (axis == Axis::HORIZONTAL) {
1489 padding = IsReverse() ? groupWrapper->GetGeometryNode()->GetPadding()->right
1490 : groupWrapper->GetGeometryNode()->GetPadding()->left;
1491 margin = IsReverse() ? groupWrapper->GetGeometryNode()->GetMargin()->right
1492 : groupWrapper->GetGeometryNode()->GetMargin()->left;
1493 } else {
1494 padding = groupWrapper->GetGeometryNode()->GetPadding()->top;
1495 margin = groupWrapper->GetGeometryNode()->GetMargin()->top;
1496 }
1497 auto marginValue = margin.value_or(0.f);
1498 auto paddingValue = padding.value_or(0.f);
1499 if (align == ScrollAlign::CENTER) {
1500 targetPos = paddingValue + marginValue + startPos + (it->second.startPos + it->second.endPos) / 2.0f -
1501 contentMainSize_ / 2.0f;
1502 } else {
1503 float itemStartPos = paddingValue + marginValue + startPos + it->second.startPos;
1504 float itemEndPos = paddingValue + marginValue + startPos + it->second.endPos;
1505 if (stickyStyle == V2::StickyStyle::HEADER || stickyStyle == V2::StickyStyle::BOTH) {
1506 itemStartPos -= groupPattern->GetHeaderMainSize();
1507 }
1508 if (stickyStyle == V2::StickyStyle::FOOTER || stickyStyle == V2::StickyStyle::BOTH) {
1509 itemEndPos += groupPattern->GetFooterMainSize();
1510 }
1511 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1512 itemStartPos -= contentStartOffset_;
1513 itemEndPos += contentEndOffset_;
1514 }
1515 if (align == ScrollAlign::AUTO) {
1516 targetPos = CalculateTargetPos(itemStartPos, itemEndPos);
1517 } else {
1518 targetPos = align == ScrollAlign::END ? itemEndPos - contentMainSize_ : itemStartPos;
1519 }
1520 }
1521 return true;
1522 }
1523
AnimateToTarget(int32_t index,std::optional<int32_t> indexInGroup,ScrollAlign align)1524 bool ListPattern::AnimateToTarget(int32_t index, std::optional<int32_t> indexInGroup, ScrollAlign align)
1525 {
1526 auto iter = itemPosition_.find(index);
1527 if (iter == itemPosition_.end()) {
1528 return false;
1529 }
1530 float targetPos = 0.0f;
1531 if (iter->second.isGroup) {
1532 if (indexInGroup.has_value()) {
1533 if (!GetListItemGroupAnimatePosWithIndexInGroup(index, indexInGroup.value(), iter->second.startPos,
1534 align, targetPos)) {
1535 return false;
1536 }
1537 } else {
1538 if (!GetListItemGroupAnimatePosWithoutIndexInGroup(index, iter->second.startPos, iter->second.endPos,
1539 align, targetPos)) {
1540 return false;
1541 }
1542 }
1543 } else {
1544 if (indexInGroup.has_value()) {
1545 return false;
1546 }
1547 GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
1548 }
1549 float extraOffset = 0.0f;
1550 if (GetExtraOffset().has_value()) {
1551 extraOffset = GetExtraOffset().value();
1552 targetPos += extraOffset;
1553 ResetExtraOffset();
1554 }
1555 if (!NearZero(targetPos)) {
1556 AnimateTo(targetPos + currentOffset_, -1, nullptr, true);
1557 if (predictSnapOffset_.has_value() && AnimateRunning()) {
1558 scrollSnapVelocity_ = 0.0f;
1559 predictSnapOffset_.reset();
1560 snapTrigOnScrollStart_ = false;
1561 }
1562 if (!indexInGroup.has_value()) {
1563 scrollTarget_ = { index, extraOffset, align, targetPos + currentOffset_ };
1564 }
1565 }
1566 return true;
1567 }
1568
ScrollPage(bool reverse,AccessibilityScrollType scrollType)1569 bool ListPattern::ScrollPage(bool reverse, AccessibilityScrollType scrollType)
1570 {
1571 LOGI("ScrollPage:%{public}d", reverse);
1572 StopAnimate();
1573 float distance = reverse ? contentMainSize_ : -contentMainSize_;
1574 if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
1575 distance = distance / 2.f;
1576 }
1577 UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
1578 isScrollEnd_ = true;
1579 return true;
1580 }
1581
ScrollBy(float offset)1582 void ListPattern::ScrollBy(float offset)
1583 {
1584 StopAnimate();
1585 UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
1586 isScrollEnd_ = true;
1587 }
1588
GetCurrentOffset() const1589 Offset ListPattern::GetCurrentOffset() const
1590 {
1591 if (GetAxis() == Axis::HORIZONTAL) {
1592 return { GetTotalOffset(), 0.0 };
1593 }
1594 return { 0.0, GetTotalOffset() };
1595 }
1596
HandleScrollBarOutBoundary()1597 void ListPattern::HandleScrollBarOutBoundary()
1598 {
1599 if (itemPosition_.empty()) {
1600 return;
1601 }
1602 if (!GetScrollBar() && !GetScrollBarProxy()) {
1603 return;
1604 }
1605 if (!IsOutOfBoundary(false) || !isScrollable_) {
1606 ScrollablePattern::HandleScrollBarOutBoundary(0);
1607 return;
1608 }
1609 float overScroll = 0.0f;
1610 if (!IsScrollSnapAlignCenter()) {
1611 if ((itemPosition_.begin()->first == 0) && GreatNotEqual(startMainPos_, contentStartOffset_)) {
1612 overScroll = startMainPos_ - contentStartOffset_;
1613 } else {
1614 overScroll = contentMainSize_ - contentEndOffset_ - endMainPos_;
1615 }
1616 } else {
1617 float itemHeight = itemPosition_[centerIndex_].endPos - itemPosition_[centerIndex_].startPos;
1618 if (startIndex_ == 0 && Positive(startMainPos_ + itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1619 overScroll = startMainPos_ + itemHeight / 2.0f - contentMainSize_ / 2.0f;
1620 } else if ((endIndex_ == maxListItemIndex_) &&
1621 LessNotEqual(endMainPos_ - itemHeight / 2.0f, contentMainSize_ / 2.0f)) {
1622 overScroll = endMainPos_ - itemHeight / 2.0f - contentMainSize_ / 2.0f;
1623 }
1624 }
1625 ScrollablePattern::HandleScrollBarOutBoundary(overScroll);
1626 }
1627
GetItemRect(int32_t index) const1628 Rect ListPattern::GetItemRect(int32_t index) const
1629 {
1630 if (index < 0 || index < startIndex_ || index > endIndex_) {
1631 return Rect();
1632 }
1633 auto host = GetHost();
1634 CHECK_NULL_RETURN(host, Rect());
1635 auto item = host->GetChildByIndex(index);
1636 CHECK_NULL_RETURN(item, Rect());
1637 auto itemGeometry = item->GetGeometryNode();
1638 CHECK_NULL_RETURN(itemGeometry, Rect());
1639 return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
1640 itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
1641 }
1642
GetItemRectInGroup(int32_t index,int32_t indexInGroup) const1643 Rect ListPattern::GetItemRectInGroup(int32_t index, int32_t indexInGroup) const
1644 {
1645 if (index < 0 || indexInGroup < 0 || index < startIndex_ || index > endIndex_) {
1646 return Rect();
1647 }
1648 auto host = GetHost();
1649 CHECK_NULL_RETURN(host, Rect());
1650 auto itemGroupWrapper = host->GetChildByIndex(index);
1651 CHECK_NULL_RETURN(itemGroupWrapper, Rect());
1652 auto itemGroup = itemGroupWrapper->GetHostNode();
1653 CHECK_NULL_RETURN(itemGroup, Rect());
1654 if (!(itemGroup->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG)) {
1655 return Rect();
1656 }
1657 auto itemGroupGeometry = itemGroup->GetGeometryNode();
1658 CHECK_NULL_RETURN(itemGroupGeometry, Rect());
1659 auto groupPattern = itemGroup->GetPattern<ListItemGroupPattern>();
1660 CHECK_NULL_RETURN(groupPattern, Rect());
1661 if (indexInGroup < groupPattern->GetDisplayStartIndexInGroup() ||
1662 indexInGroup > groupPattern->GetDisplayEndIndexInGroup()) {
1663 return Rect();
1664 }
1665 auto groupItem = itemGroup->GetChildByIndex(indexInGroup + groupPattern->GetItemStartIndex());
1666 CHECK_NULL_RETURN(groupItem, Rect());
1667 auto groupItemGeometry = groupItem->GetGeometryNode();
1668 CHECK_NULL_RETURN(groupItemGeometry, Rect());
1669 return Rect(itemGroupGeometry->GetFrameRect().GetX() + groupItemGeometry->GetFrameRect().GetX(),
1670 itemGroupGeometry->GetFrameRect().GetY() + groupItemGeometry->GetFrameRect().GetY(),
1671 groupItemGeometry->GetFrameRect().Width(), groupItemGeometry->GetFrameRect().Height());
1672 }
1673
UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,bool isJump)1674 float ListPattern::UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, bool isJump)
1675 {
1676 float relativeOffset = listLayoutAlgorithm->GetCurrentOffset();
1677 float prevOffset = currentOffset_;
1678 if (childrenSize_) {
1679 listTotalHeight_ = posMap_->GetTotalHeight();
1680 currentOffset_ = itemPosition_.empty() ? 0.0f :
1681 posMap_->GetPos(itemPosition_.begin()->first, itemPosition_.begin()->second.startPos);
1682 } else {
1683 if (isJump || needReEstimateOffset_) {
1684 auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis());
1685 calculate.GetEstimateHeightAndOffset(GetHost());
1686 currentOffset_ = calculate.GetEstimateOffset();
1687 relativeOffset = 0;
1688 needReEstimateOffset_ = false;
1689 posMap_->ClearPosMap();
1690 }
1691 CalculateCurrentOffset(relativeOffset, listLayoutAlgorithm->GetRecycledItemPosition());
1692 }
1693 if (scrollTarget_) {
1694 auto& target = scrollTarget_.value();
1695 auto posInfo = posMap_->GetPositionInfo(target.index);
1696 if (!Negative(posInfo.mainPos)) {
1697 float startPos = posInfo.mainPos - currentOffset_;
1698 float targetPos = 0.0f;
1699 GetListItemAnimatePos(startPos, startPos + posInfo.mainSize, target.align, targetPos);
1700 targetPos += currentOffset_ + target.extraOffset;
1701 const float epsilon = 0.1f;
1702 if (!NearEqual(relativeOffset + prevOffset, currentOffset_, epsilon) ||
1703 !NearEqual(target.targetOffset, targetPos, epsilon)) {
1704 target.targetOffset = targetPos;
1705 AnimateTo(targetPos, -1, nullptr, true);
1706 }
1707 }
1708 }
1709 return currentOffset_ - prevOffset;
1710 }
1711
CalculateCurrentOffset(float delta,const ListLayoutAlgorithm::PositionMap & recycledItemPosition)1712 void ListPattern::CalculateCurrentOffset(float delta, const ListLayoutAlgorithm::PositionMap& recycledItemPosition)
1713 {
1714 posMap_->UpdateTotalCount(maxListItemIndex_ + 1);
1715 if (itemPosition_.empty()) {
1716 return;
1717 }
1718 auto itemPos = itemPosition_;
1719 for (auto& [index, pos] : recycledItemPosition) {
1720 itemPos.try_emplace(index, pos);
1721 }
1722 float startPos = itemPos.begin()->second.startPos;
1723 int32_t startIndex = itemPos.begin()->first;
1724 auto& groupInfo = itemPos.begin()->second.groupInfo;
1725 bool groupAtStart = (!groupInfo || groupInfo.value().atStart);
1726 if (startIndex == 0 && groupAtStart) {
1727 currentOffset_ = -startPos;
1728 } else {
1729 posMap_->UpdatePosMapStart(delta, currentOffset_, spaceWidth_, startIndex, startPos, groupAtStart);
1730 }
1731 for (auto& [index, pos] : itemPos) {
1732 float height = pos.endPos - pos.startPos;
1733 if (pos.groupInfo) {
1734 bool groupAtStart = pos.groupInfo.value().atStart;
1735 if (groupAtStart) {
1736 posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height });
1737 } else {
1738 posMap_->UpdatePosWithCheck(index, { currentOffset_ + pos.startPos, height });
1739 }
1740 } else {
1741 posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height });
1742 }
1743 }
1744 auto& endGroupInfo = itemPos.rbegin()->second.groupInfo;
1745 bool groupAtEnd = (!endGroupInfo || endGroupInfo.value().atEnd);
1746 posMap_->UpdatePosMapEnd(itemPos.rbegin()->first, spaceWidth_, groupAtEnd);
1747 }
1748
UpdateScrollBarOffset()1749 void ListPattern::UpdateScrollBarOffset()
1750 {
1751 CheckScrollBarOff();
1752 if (itemPosition_.empty()) {
1753 return;
1754 }
1755 if (!GetScrollBar() && !GetScrollBarProxy()) {
1756 return;
1757 }
1758 float currentOffset = 0.0f;
1759 float estimatedHeight = 0.0f;
1760 if (childrenSize_) {
1761 currentOffset = currentOffset_;
1762 estimatedHeight = listTotalHeight_;
1763 } else {
1764 auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis());
1765 calculate.GetEstimateHeightAndOffset(GetHost());
1766 currentOffset = calculate.GetEstimateOffset();
1767 estimatedHeight = calculate.GetEstimateHeight();
1768 }
1769 if (GetAlwaysEnabled()) {
1770 estimatedHeight = estimatedHeight - spaceWidth_;
1771 }
1772 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1773 currentOffset += contentStartOffset_;
1774 estimatedHeight += contentStartOffset_ + contentEndOffset_;
1775 }
1776
1777 // calculate padding offset of list
1778 auto host = GetHost();
1779 CHECK_NULL_VOID(host);
1780 auto layoutPriority = host->GetLayoutProperty();
1781 CHECK_NULL_VOID(layoutPriority);
1782 auto padding = layoutPriority->CreatePaddingAndBorder();
1783 auto paddingMain = GetAxis() == Axis::VERTICAL ? padding.Height() : padding.Width();
1784 const auto& geometryNode = host->GetGeometryNode();
1785 auto frameSize = geometryNode->GetFrameSize();
1786 Size size(frameSize.Width(), frameSize.Height());
1787 UpdateScrollBarRegion(currentOffset, estimatedHeight + paddingMain, size, Offset(0.0f, 0.0f));
1788 }
1789
GetTotalHeight() const1790 float ListPattern::GetTotalHeight() const
1791 {
1792 auto currentOffset = GetTotalOffset();
1793 if (endIndex_ >= maxListItemIndex_) {
1794 return currentOffset + endMainPos_ + contentEndOffset_;
1795 }
1796 if (itemPosition_.empty()) {
1797 return 0.0f;
1798 }
1799 int32_t remainCount = maxListItemIndex_ - endIndex_;
1800 float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1801 float remainOffset = itemsSize / itemPosition_.size() * remainCount - spaceWidth_;
1802 return currentOffset + endMainPos_ + remainOffset + contentEndOffset_;
1803 }
1804
TriggerModifyDone()1805 void ListPattern::TriggerModifyDone()
1806 {
1807 OnModifyDone();
1808 }
1809
SetChainAnimationCallback()1810 void ListPattern::SetChainAnimationCallback()
1811 {
1812 CHECK_NULL_VOID(chainAnimation_);
1813 chainAnimation_->SetAnimationCallback([weak = AceType::WeakClaim(this)]() {
1814 auto list = weak.Upgrade();
1815 CHECK_NULL_VOID(list);
1816 list->MarkDirtyNodeSelf();
1817 });
1818 auto scrollEffect = AceType::DynamicCast<ScrollSpringEffect>(GetScrollEdgeEffect());
1819 CHECK_NULL_VOID(scrollEffect);
1820 scrollEffect->SetOnWillStartSpringCallback([weak = AceType::WeakClaim(this)]() {
1821 auto list = weak.Upgrade();
1822 CHECK_NULL_VOID(list);
1823 if (!list->dragFromSpring_ && list->chainAnimation_) {
1824 auto delta = list->chainAnimation_->SetControlIndex(list->IsAtTop() ? 0 : list->maxListItemIndex_);
1825 list->currentDelta_ -= delta;
1826 list->dragFromSpring_ = true;
1827 }
1828 });
1829 }
1830
SetChainAnimation()1831 void ListPattern::SetChainAnimation()
1832 {
1833 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1834 CHECK_NULL_VOID(listLayoutProperty);
1835 auto edgeEffect = GetEdgeEffect();
1836 int32_t lanes = std::max(listLayoutProperty->GetLanes().value_or(1), 1);
1837 bool autoLanes = listLayoutProperty->HasLaneMinLength() || listLayoutProperty->HasLaneMaxLength();
1838 bool animation = listLayoutProperty->GetChainAnimation().value_or(false);
1839 bool enable = edgeEffect == EdgeEffect::SPRING && lanes == 1 && !autoLanes && animation;
1840 if (!enable) {
1841 chainAnimation_.Reset();
1842 return;
1843 }
1844 auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1845 if (Negative(space)) {
1846 space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
1847 }
1848 if (!chainAnimation_ || (chainAnimation_ && space != chainAnimation_->GetSpace())) {
1849 springProperty_ =
1850 AceType::MakeRefPtr<SpringProperty>(CHAIN_SPRING_MASS, CHAIN_SPRING_STIFFNESS, CHAIN_SPRING_DAMPING);
1851 if (chainAnimationOptions_.has_value()) {
1852 float maxSpace = chainAnimationOptions_.value().maxSpace.ConvertToPx();
1853 float minSpace = chainAnimationOptions_.value().minSpace.ConvertToPx();
1854 minSpace = Negative(minSpace) ? 0.0f : minSpace;
1855 minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
1856 maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
1857 springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1858 springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1859 chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1860 auto conductivity = chainAnimationOptions_.value().conductivity;
1861 if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1862 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1863 }
1864 chainAnimation_->SetConductivity(conductivity);
1865 auto intensity = chainAnimationOptions_.value().intensity;
1866 if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1867 intensity = ChainAnimation::DEFAULT_INTENSITY;
1868 }
1869 chainAnimation_->SetIntensity(intensity);
1870 auto effect = chainAnimationOptions_.value().edgeEffect;
1871 chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1872 } else {
1873 auto minSpace = space * DEFAULT_MIN_SPACE_SCALE;
1874 auto maxSpace = space * DEFAULT_MAX_SPACE_SCALE;
1875 chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1876 }
1877 SetChainAnimationCallback();
1878 }
1879 }
1880
SetChainAnimationOptions(const ChainAnimationOptions & options)1881 void ListPattern::SetChainAnimationOptions(const ChainAnimationOptions& options)
1882 {
1883 chainAnimationOptions_ = options;
1884 if (chainAnimation_) {
1885 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1886 CHECK_NULL_VOID(listLayoutProperty);
1887 auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1888 if (Negative(space)) {
1889 space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
1890 }
1891 float maxSpace = options.maxSpace.ConvertToPx();
1892 float minSpace = options.minSpace.ConvertToPx();
1893 minSpace = Negative(minSpace) ? 0.0f : minSpace;
1894 minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
1895 maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
1896 chainAnimation_->SetSpace(space, maxSpace, minSpace);
1897 auto conductivity = chainAnimationOptions_.value().conductivity;
1898 if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1899 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1900 }
1901 chainAnimation_->SetConductivity(conductivity);
1902 auto intensity = chainAnimationOptions_.value().intensity;
1903 if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1904 intensity = ChainAnimation::DEFAULT_INTENSITY;
1905 }
1906 chainAnimation_->SetIntensity(intensity);
1907 auto effect = options.edgeEffect;
1908 chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1909 }
1910 if (springProperty_) {
1911 springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1912 springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1913 }
1914 }
1915
OnTouchDown(const TouchEventInfo & info)1916 void ListPattern::OnTouchDown(const TouchEventInfo& info)
1917 {
1918 ScrollablePattern::OnTouchDown(info);
1919 auto& touches = info.GetTouches();
1920 if (touches.empty()) {
1921 return;
1922 }
1923 auto offset = touches.front().GetLocalLocation();
1924 float startPosition = GetAxis() == Axis::HORIZONTAL ? offset.GetX() : offset.GetY();
1925 ProcessDragStart(startPosition);
1926 }
1927
ProcessDragStart(float startPosition)1928 void ListPattern::ProcessDragStart(float startPosition)
1929 {
1930 CHECK_NULL_VOID(chainAnimation_);
1931 auto host = GetHost();
1932 CHECK_NULL_VOID(host);
1933 auto globalOffset = host->GetTransformRelativeOffset();
1934 int32_t index = -1;
1935 auto offset = startPosition - GetMainAxisOffset(globalOffset, GetAxis());
1936 auto it = std::find_if(
1937 itemPosition_.begin(), itemPosition_.end(), [offset](auto pos) { return offset <= pos.second.endPos; });
1938 if (it != itemPosition_.end()) {
1939 index = it->first;
1940 } else if (!itemPosition_.empty()) {
1941 index = itemPosition_.rbegin()->first + 1;
1942 }
1943 dragFromSpring_ = false;
1944 float delta = chainAnimation_->SetControlIndex(index);
1945 currentDelta_ -= delta;
1946 chainAnimation_->SetMaxIndex(maxListItemIndex_);
1947 }
1948
ProcessDragUpdate(float dragOffset,int32_t source)1949 void ListPattern::ProcessDragUpdate(float dragOffset, int32_t source)
1950 {
1951 CHECK_NULL_VOID(chainAnimation_);
1952 if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR_FLING) {
1953 return;
1954 }
1955 if (NeedScrollSnapAlignEffect()) {
1956 auto delta = 0.0f;
1957 if (chainAnimation_->GetControlIndex() < startIndex_ - 1) {
1958 delta = chainAnimation_->SetControlIndex(std::max(startIndex_ - 1, 0));
1959 }
1960 if (chainAnimation_->GetControlIndex() > endIndex_ + 1) {
1961 delta = chainAnimation_->SetControlIndex(std::min(endIndex_ + 1, maxListItemIndex_));
1962 }
1963 if (!NearZero(delta)) {
1964 auto scrollableEvent = GetScrollableEvent();
1965 CHECK_NULL_VOID(scrollableEvent);
1966 auto scrollable = scrollableEvent->GetScrollable();
1967 CHECK_NULL_VOID(scrollable);
1968 scrollable->UpdateScrollSnapStartOffset(delta);
1969 currentDelta_ -= delta;
1970 }
1971 }
1972 float overOffset = 0.0f;
1973 if (!itemPosition_.empty()) {
1974 auto res = GetOutBoundaryOffset(false);
1975 overOffset = std::max(res.start, res.end);
1976 if (!NearZero(res.end)) {
1977 overOffset = -overOffset;
1978 }
1979 }
1980 if (source == SCROLL_FROM_UPDATE && !NearZero(overOffset)) {
1981 dragOffset = 0.0f;
1982 }
1983 chainAnimation_->SetDelta(-dragOffset, overOffset);
1984 if (source == SCROLL_FROM_UPDATE && GetCanOverScroll()) {
1985 float tempDelta = currentDelta_;
1986 currentDelta_ -= dragOffset;
1987 bool isAtEdge = IsAtTop() || IsAtBottom();
1988 currentDelta_ = tempDelta;
1989 SetCanOverScroll(isAtEdge);
1990 }
1991 }
1992
GetChainDelta(int32_t index) const1993 float ListPattern::GetChainDelta(int32_t index) const
1994 {
1995 CHECK_NULL_RETURN(chainAnimation_, 0.0f);
1996 return chainAnimation_->GetValue(index);
1997 }
1998
MultiSelectWithoutKeyboard(const RectF & selectedZone)1999 void ListPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
2000 {
2001 auto host = GetHost();
2002 CHECK_NULL_VOID(host);
2003 std::list<RefPtr<FrameNode>> childrens;
2004 host->GenerateOneDepthVisibleFrame(childrens);
2005 for (const auto& item : childrens) {
2006 if (item->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2007 auto itemGroupPattern = item->GetPattern<ListItemGroupPattern>();
2008 CHECK_NULL_VOID(itemGroupPattern);
2009 auto itemGroupGeometry = item->GetGeometryNode();
2010 CHECK_NULL_VOID(itemGroupGeometry);
2011 auto itemGroupRect = itemGroupGeometry->GetFrameRect();
2012 if (!selectedZone.IsIntersectWith(itemGroupRect)) {
2013 continue;
2014 }
2015 HandleCardModeSelectedEvent(selectedZone, item, itemGroupRect.Top());
2016 continue;
2017 }
2018 auto itemPattern = item->GetPattern<ListItemPattern>();
2019 CHECK_NULL_VOID(itemPattern);
2020 if (!itemPattern->Selectable()) {
2021 continue;
2022 }
2023
2024 auto itemGeometry = item->GetGeometryNode();
2025 CHECK_NULL_VOID(itemGeometry);
2026
2027 auto itemRect = itemGeometry->GetFrameRect();
2028 if (!selectedZone.IsIntersectWith(itemRect)) {
2029 itemPattern->MarkIsSelected(false);
2030 } else {
2031 itemPattern->MarkIsSelected(true);
2032 }
2033 }
2034
2035 DrawSelectedZone(selectedZone);
2036 }
2037
HandleCardModeSelectedEvent(const RectF & selectedZone,const RefPtr<FrameNode> & itemGroupNode,float itemGroupTop)2038 void ListPattern::HandleCardModeSelectedEvent(
2039 const RectF& selectedZone, const RefPtr<FrameNode>& itemGroupNode, float itemGroupTop)
2040 {
2041 CHECK_NULL_VOID(itemGroupNode);
2042 std::list<RefPtr<FrameNode>> childrens;
2043 itemGroupNode->GenerateOneDepthVisibleFrame(childrens);
2044 for (const auto& item : childrens) {
2045 auto itemPattern = item->GetPattern<ListItemPattern>();
2046 if (!itemPattern) {
2047 continue;
2048 }
2049 if (!itemPattern->Selectable()) {
2050 continue;
2051 }
2052 auto itemGeometry = item->GetGeometryNode();
2053 CHECK_NULL_VOID(itemGeometry);
2054 auto context = item->GetRenderContext();
2055 CHECK_NULL_VOID(context);
2056 auto itemRect = itemGeometry->GetFrameRect();
2057 RectF itemRectInGroup(itemRect.GetX(), itemRect.GetY() + itemGroupTop, itemRect.Width(), itemRect.Height());
2058 if (!selectedZone.IsIntersectWith(itemRectInGroup)) {
2059 itemPattern->MarkIsSelected(false);
2060 } else {
2061 itemPattern->MarkIsSelected(true);
2062 }
2063 }
2064 }
2065
ClearMultiSelect()2066 void ListPattern::ClearMultiSelect()
2067 {
2068 auto host = GetHost();
2069 CHECK_NULL_VOID(host);
2070 std::list<RefPtr<FrameNode>> children;
2071 host->GenerateOneDepthAllFrame(children);
2072 for (const auto& child : children) {
2073 if (!child) {
2074 continue;
2075 }
2076 auto itemPattern = child->GetPattern<ListItemPattern>();
2077 if (itemPattern) {
2078 itemPattern->MarkIsSelected(false);
2079 continue;
2080 }
2081 auto itemGroupPattern = child->GetPattern<ListItemGroupPattern>();
2082 if (itemGroupPattern) {
2083 std::list<RefPtr<FrameNode>> itemChildren;
2084 child->GenerateOneDepthAllFrame(itemChildren);
2085 for (const auto& item : itemChildren) {
2086 if (!item) {
2087 continue;
2088 }
2089 itemPattern = item->GetPattern<ListItemPattern>();
2090 if (itemPattern) {
2091 itemPattern->MarkIsSelected(false);
2092 }
2093 }
2094 }
2095 }
2096
2097 ClearSelectedZone();
2098 }
2099
IsItemSelected(const GestureEvent & info)2100 bool ListPattern::IsItemSelected(const GestureEvent& info)
2101 {
2102 auto host = GetHost();
2103 CHECK_NULL_RETURN(host, false);
2104 auto node = host->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
2105 CHECK_NULL_RETURN(node, false);
2106 auto itemPattern = node->GetPattern<ListItemPattern>();
2107 if (itemPattern) {
2108 return itemPattern->IsSelected();
2109 }
2110 auto itemGroupPattern = node->GetPattern<ListItemGroupPattern>();
2111 if (itemGroupPattern) {
2112 auto itemNode = node->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
2113 CHECK_NULL_RETURN(itemNode, false);
2114 itemPattern = itemNode->GetPattern<ListItemPattern>();
2115 CHECK_NULL_RETURN(itemPattern, false);
2116 return itemPattern->IsSelected();
2117 }
2118 return false;
2119 }
2120
SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)2121 void ListPattern::SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)
2122 {
2123 // swiper item only can be replaced when no other items be dragged
2124 if (canReplaceSwiperItem_) {
2125 if (swiperItem != swiperItem_) {
2126 auto item = swiperItem_.Upgrade();
2127 if (item) {
2128 item->ResetSwipeStatus();
2129 }
2130 swiperItem_ = std::move(swiperItem);
2131 }
2132 canReplaceSwiperItem_ = false;
2133 }
2134 FireAndCleanScrollingListener();
2135 }
2136
GetItemIndexByPosition(float xOffset,float yOffset)2137 int32_t ListPattern::GetItemIndexByPosition(float xOffset, float yOffset)
2138 {
2139 auto host = GetHost();
2140 auto globalOffset = host->GetTransformRelativeOffset();
2141 float relativeX = xOffset - globalOffset.GetX();
2142 float relativeY = yOffset - globalOffset.GetY();
2143 float mainOffset = GetAxis() == Axis::VERTICAL ? relativeY : relativeX;
2144 float crossOffset = GetAxis() == Axis::VERTICAL ? relativeX : relativeY;
2145 float crossSize = GetCrossAxisSize(GetContentSize(), GetAxis());
2146 int32_t lanesOffset = 0;
2147 if (lanes_ > 1) {
2148 lanesOffset = static_cast<int32_t>(crossOffset / (crossSize / lanes_));
2149 }
2150 for (auto& pos : itemPosition_) {
2151 if (mainOffset <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
2152 return std::min(pos.first + lanesOffset, maxListItemIndex_ + 1);
2153 }
2154 }
2155 if (!itemPosition_.empty()) {
2156 return itemPosition_.rbegin()->first + 1;
2157 }
2158 return 0;
2159 }
2160
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const2161 void ListPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
2162 {
2163 ScrollablePattern::ToJsonValue(json, filter);
2164 /* no fixed attr below, just return */
2165 if (filter.IsFastFilter()) {
2166 return;
2167 }
2168 json->PutExtAttr("multiSelectable", multiSelectable_, filter);
2169 json->PutExtAttr("startIndex", startIndex_, filter);
2170 if (!itemPosition_.empty()) {
2171 json->PutExtAttr("itemStartPos", itemPosition_.begin()->second.startPos, filter);
2172 }
2173 auto nestedScrollOptions = JsonUtil::Create(true);
2174 auto nestedScroll = GetNestedScroll();
2175 nestedScrollOptions->Put("scrollForward", nestedScroll.GetNestedScrollModeStr(nestedScroll.forward).c_str());
2176 nestedScrollOptions->Put("scrollBackward", nestedScroll.GetNestedScrollModeStr(nestedScroll.backward).c_str());
2177 json->PutExtAttr("nestedScroll", nestedScrollOptions, filter);
2178 json->PutExtAttr("maintainVisibleContentPosition", maintainVisibleContentPosition_, filter);
2179 }
2180
FromJson(const std::unique_ptr<JsonValue> & json)2181 void ListPattern::FromJson(const std::unique_ptr<JsonValue>& json)
2182 {
2183 ScrollToIndex(json->GetInt("startIndex"));
2184 if (json->Contains("itemStartPos")) {
2185 ScrollBy(-json->GetDouble("itemStartPos"));
2186 }
2187 auto host = GetHost();
2188 CHECK_NULL_VOID(host);
2189 host->GetRenderContext()->UpdateClipEdge(true);
2190 ScrollablePattern::FromJson(json);
2191 }
2192
GetListItemGroupParameter(const RefPtr<FrameNode> & node)2193 ListItemGroupPara ListPattern::GetListItemGroupParameter(const RefPtr<FrameNode>& node)
2194 {
2195 ListItemGroupPara listItemGroupPara = { -1, -1, -1, -1 };
2196 auto curFrameParent = node->GetParent();
2197 auto curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2198 while (curFrameParent && (!curFrameParentNode)) {
2199 curFrameParent = curFrameParent->GetParent();
2200 curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2201 }
2202 CHECK_NULL_RETURN(curFrameParentNode, listItemGroupPara);
2203 if (curFrameParent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2204 auto itemGroupPattern = curFrameParentNode->GetPattern<ListItemGroupPattern>();
2205 CHECK_NULL_RETURN(itemGroupPattern, listItemGroupPara);
2206 listItemGroupPara.displayEndIndex = itemGroupPattern->GetDisplayEndIndexInGroup();
2207 listItemGroupPara.displayStartIndex = itemGroupPattern->GetDisplayStartIndexInGroup();
2208 listItemGroupPara.lanes = itemGroupPattern->GetLanesInGroup();
2209 listItemGroupPara.itemEndIndex = itemGroupPattern->GetEndIndexInGroup();
2210 }
2211 return listItemGroupPara;
2212 }
2213
IsListItemGroup(int32_t listIndex,RefPtr<FrameNode> & node)2214 bool ListPattern::IsListItemGroup(int32_t listIndex, RefPtr<FrameNode>& node)
2215 {
2216 auto listFrame = GetHost();
2217 CHECK_NULL_RETURN(listFrame, false);
2218 auto listFocus = listFrame->GetFocusHub();
2219 CHECK_NULL_RETURN(listFocus, false);
2220 for (const auto& childFocus : listFocus->GetChildren()) {
2221 if (!childFocus->IsFocusable()) {
2222 continue;
2223 }
2224 if (auto childFrame = childFocus->GetFrameNode()) {
2225 if (auto childPattern = AceType::DynamicCast<ListItemPattern>(childFrame->GetPattern())) {
2226 auto curIndex = childPattern->GetIndexInList();
2227 auto curIndexInGroup = childPattern->GetIndexInListItemGroup();
2228 if (curIndex == listIndex) {
2229 node = childFrame;
2230 return curIndexInGroup > -1;
2231 }
2232 }
2233 }
2234 }
2235 return false;
2236 }
2237
RefreshLanesItemRange()2238 void ListPattern::RefreshLanesItemRange()
2239 {
2240 auto host = GetHost();
2241 CHECK_NULL_VOID(host);
2242 auto updatePos = host->GetChildrenUpdated();
2243 if (updatePos == -1) {
2244 return;
2245 }
2246 host->ChildrenUpdatedFrom(-1);
2247 if (updatePos == 0) {
2248 lanesItemRange_.clear();
2249 return;
2250 }
2251 for (auto it = lanesItemRange_.begin(); it != lanesItemRange_.end();) {
2252 if (it->second < updatePos) {
2253 it++;
2254 } else if (it->first >= updatePos) {
2255 it = lanesItemRange_.erase(it);
2256 } else {
2257 it->second = updatePos - 1;
2258 it++;
2259 }
2260 }
2261 }
2262
ProvideRestoreInfo()2263 std::string ListPattern::ProvideRestoreInfo()
2264 {
2265 return std::to_string(startIndex_);
2266 }
2267
CloseAllSwipeActions(OnFinishFunc && onFinishCallback)2268 void ListPattern::CloseAllSwipeActions(OnFinishFunc&& onFinishCallback)
2269 {
2270 auto item = swiperItem_.Upgrade();
2271 if (item) {
2272 return item->CloseSwipeAction(std::move(onFinishCallback));
2273 }
2274 }
2275
OnRestoreInfo(const std::string & restoreInfo)2276 void ListPattern::OnRestoreInfo(const std::string& restoreInfo)
2277 {
2278 jumpIndex_ = StringUtils::StringToInt(restoreInfo);
2279 }
2280
DumpAdvanceInfo()2281 void ListPattern::DumpAdvanceInfo()
2282 {
2283 ScrollablePattern::DumpAdvanceInfo();
2284 DumpLog::GetInstance().AddDesc("maxListItemIndex:" + std::to_string(maxListItemIndex_));
2285 DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(startIndex_));
2286 DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(endIndex_));
2287 DumpLog::GetInstance().AddDesc("centerIndex:" + std::to_string(centerIndex_));
2288 DumpLog::GetInstance().AddDesc("startMainPos:" + std::to_string(startMainPos_));
2289 DumpLog::GetInstance().AddDesc("endMainPos:" + std::to_string(endMainPos_));
2290 DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(currentOffset_));
2291 DumpLog::GetInstance().AddDesc("contentMainSize:" + std::to_string(contentMainSize_));
2292 DumpLog::GetInstance().AddDesc("contentStartOffset:" + std::to_string(contentStartOffset_));
2293 DumpLog::GetInstance().AddDesc("contentEndOffset:" + std::to_string(contentEndOffset_));
2294 DumpLog::GetInstance().AddDesc("currentDelta:" + std::to_string(currentDelta_));
2295 crossMatchChild_ ? DumpLog::GetInstance().AddDesc("crossMatchChild:true")
2296 : DumpLog::GetInstance().AddDesc("crossMatchChild:false");
2297 smooth_ ? DumpLog::GetInstance().AddDesc("smooth:true") : DumpLog::GetInstance().AddDesc("smooth:false");
2298 if (jumpIndex_.has_value()) {
2299 DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(jumpIndex_.value()));
2300 } else {
2301 DumpLog::GetInstance().AddDesc("jumpIndex:null");
2302 }
2303 if (jumpIndexInGroup_.has_value()) {
2304 DumpLog::GetInstance().AddDesc("jumpIndexInGroup:" + std::to_string(jumpIndexInGroup_.value()));
2305 } else {
2306 DumpLog::GetInstance().AddDesc("jumpIndexInGroup:null");
2307 }
2308 if (targetIndex_.has_value()) {
2309 DumpLog::GetInstance().AddDesc("targetIndex:" + std::to_string(targetIndex_.value()));
2310 } else {
2311 DumpLog::GetInstance().AddDesc("targetIndex:null");
2312 }
2313 if (predictSnapOffset_.has_value()) {
2314 DumpLog::GetInstance().AddDesc("predictSnapOffset:" + std::to_string(predictSnapOffset_.value()));
2315 } else {
2316 DumpLog::GetInstance().AddDesc("predictSnapOffset:null");
2317 }
2318 if (predictSnapEndPos_.has_value()) {
2319 DumpLog::GetInstance().AddDesc("predictSnapEndPos:" + std::to_string(predictSnapEndPos_.value()));
2320 } else {
2321 DumpLog::GetInstance().AddDesc("predictSnapEndPos:null");
2322 }
2323 paintStateFlag_ ? DumpLog::GetInstance().AddDesc("paintStateFlag:true")
2324 : DumpLog::GetInstance().AddDesc("paintStateFlag:false");
2325 isFramePaintStateValid_ ? DumpLog::GetInstance().AddDesc("isFramePaintStateValid:true")
2326 : DumpLog::GetInstance().AddDesc("isFramePaintStateValid:false");
2327 for (auto item : itemPosition_) {
2328 DumpLog::GetInstance().AddDesc("------------------------------------------");
2329 DumpLog::GetInstance().AddDesc("itemPosition.first:" + std::to_string(item.first));
2330 DumpLog::GetInstance().AddDesc("startPos:" + std::to_string(item.second.startPos));
2331 DumpLog::GetInstance().AddDesc("endPos:" + std::to_string(item.second.endPos));
2332 DumpLog::GetInstance().AddDesc("isGroup:" + std::to_string(item.second.isGroup));
2333 }
2334 DumpLog::GetInstance().AddDesc("------------------------------------------");
2335 scrollStop_ ? DumpLog::GetInstance().AddDesc("scrollStop:true")
2336 : DumpLog::GetInstance().AddDesc("scrollStop:false");
2337 for (auto item : lanesItemRange_) {
2338 DumpLog::GetInstance().AddDesc("------------------------------------------");
2339 DumpLog::GetInstance().AddDesc("lanesItemRange.first:" + std::to_string(item.first));
2340 DumpLog::GetInstance().AddDesc("lanesItemRange.second:" + std::to_string(item.second));
2341 }
2342 DumpLog::GetInstance().AddDesc("------------------------------------------");
2343 DumpLog::GetInstance().AddDesc("lanes:" + std::to_string(lanes_));
2344 DumpLog::GetInstance().AddDesc("laneGutter:" + std::to_string(laneGutter_));
2345 dragFromSpring_ ? DumpLog::GetInstance().AddDesc("dragFromSpring:true")
2346 : DumpLog::GetInstance().AddDesc("dragFromSpring:false");
2347 isScrollEnd_ ? DumpLog::GetInstance().AddDesc("isScrollEnd:true")
2348 : DumpLog::GetInstance().AddDesc("isScrollEnd:false");
2349 IsAtTop() ? DumpLog::GetInstance().AddDesc("IsAtTop:true") : DumpLog::GetInstance().AddDesc("IsAtTop:false");
2350 IsAtBottom() ? DumpLog::GetInstance().AddDesc("IsAtBottom:true")
2351 : DumpLog::GetInstance().AddDesc("IsAtBottom:false");
2352 }
2353
GetDefaultScrollBarDisplayMode() const2354 DisplayMode ListPattern::GetDefaultScrollBarDisplayMode() const
2355 {
2356 auto defaultDisplayMode = DisplayMode::OFF;
2357 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
2358 defaultDisplayMode = DisplayMode::AUTO;
2359 }
2360 return defaultDisplayMode;
2361 }
2362
GetVisibleSelectedItems()2363 std::vector<RefPtr<FrameNode>> ListPattern::GetVisibleSelectedItems()
2364 {
2365 std::vector<RefPtr<FrameNode>> children;
2366 auto host = GetHost();
2367 CHECK_NULL_RETURN(host, children);
2368 for (int32_t index = startIndex_; index <= endIndex_; ++index) {
2369 auto item = host->GetChildByIndex(index);
2370 if (!AceType::InstanceOf<FrameNode>(item)) {
2371 continue;
2372 }
2373 auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
2374 auto itemPattern = itemFrameNode->GetPattern<ListItemPattern>();
2375 if (!itemPattern) {
2376 continue;
2377 }
2378 if (!itemPattern->IsSelected()) {
2379 continue;
2380 }
2381 children.emplace_back(itemFrameNode);
2382 }
2383 return children;
2384 }
2385
GetOrCreateListChildrenMainSize()2386 RefPtr<ListChildrenMainSize> ListPattern::GetOrCreateListChildrenMainSize()
2387 {
2388 if (childrenSize_) {
2389 return childrenSize_;
2390 }
2391 childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>();
2392 auto callback = [weakPattern = WeakClaim(this)](std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag) {
2393 auto pattern = weakPattern.Upgrade();
2394 CHECK_NULL_VOID(pattern);
2395 auto context = PipelineContext::GetCurrentContext();
2396 CHECK_NULL_VOID(context);
2397 context->AddBuildFinishCallBack([weakPattern, change, flag]() {
2398 auto pattern = weakPattern.Upgrade();
2399 CHECK_NULL_VOID(pattern);
2400 pattern->OnChildrenSizeChanged(change, flag);
2401 });
2402 context->RequestFrame();
2403 };
2404 childrenSize_->SetOnDataChange(callback);
2405 return childrenSize_;
2406 }
2407
OnChildrenSizeChanged(std::tuple<int32_t,int32_t,int32_t> change,ListChangeFlag flag)2408 void ListPattern::OnChildrenSizeChanged(std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag)
2409 {
2410 if (!posMap_) {
2411 posMap_ = MakeRefPtr<ListPositionMap>();
2412 }
2413 posMap_->MarkDirty(flag);
2414 MarkDirtyNodeSelf();
2415 }
2416
SetListChildrenMainSize(float defaultSize,const std::vector<float> & mainSize)2417 void ListPattern::SetListChildrenMainSize(float defaultSize, const std::vector<float>& mainSize)
2418 {
2419 childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>(mainSize, defaultSize);
2420 OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
2421 }
2422
ResetChildrenSize()2423 void ListPattern::ResetChildrenSize()
2424 {
2425 if (childrenSize_) {
2426 childrenSize_ = nullptr;
2427 MarkDirtyNodeSelf();
2428 OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
2429 }
2430 }
2431
NotifyDataChange(int32_t index,int32_t count)2432 void ListPattern::NotifyDataChange(int32_t index, int32_t count)
2433 {
2434 if (!maintainVisibleContentPosition_ || itemPosition_.empty()) {
2435 return;
2436 }
2437 auto startIndex = itemPosition_.begin()->first;
2438 if (count == 0 || (count > 0 && index > startIndex) || (count < 0 && index >= startIndex)) {
2439 return;
2440 }
2441 count = std::max(count, index - startIndex);
2442 int32_t mod = 0;
2443 if (count < 0 && lanes_ > 1 && !(itemPosition_.begin()->second.isGroup)) {
2444 mod = -count % lanes_;
2445 }
2446 auto prevPosMap = std::move(itemPosition_);
2447 for (auto &pos : prevPosMap) {
2448 if (mod > 0) {
2449 mod--;
2450 } else {
2451 itemPosition_[pos.first + count] = pos.second;
2452 }
2453 }
2454 needReEstimateOffset_ = true;
2455 }
2456 } // namespace OHOS::Ace::NG
2457