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