1 /*
2 * Copyright (c) 2022-2025 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/utils/system_properties.h"
21 #include "base/memory/referenced.h"
22 #include "core/components/common/layout/constants.h"
23 #include "core/components/list/list_theme.h"
24 #include "core/components/scroll/scroll_bar_theme.h"
25 #include "core/components_ng/base/inspector_filter.h"
26 #include "core/components_ng/pattern/list/list_height_offset_calculator.h"
27 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
28 #include "core/components_ng/pattern/list/list_item_pattern.h"
29 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
30 #include "core/components_ng/pattern/list/list_layout_algorithm.h"
31 #include "core/components_ng/pattern/list/list_layout_property.h"
32 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
33 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
34 #include "core/components_ng/pattern/scrollable/scrollable.h"
35 #include "core/components_ng/pattern/scrollable/scrollable_properties.h"
36 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
37 #include "core/components_ng/property/measure_utils.h"
38 #include "core/components_v2/inspector/inspector_constants.h"
39 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
40 #include "core/components_ng/manager/scroll_adjust/scroll_adjust_manager.h"
41
42 namespace OHOS::Ace::NG {
43 namespace {
44 constexpr Dimension CHAIN_INTERVAL_DEFAULT = 20.0_vp;
45 constexpr double CHAIN_SPRING_MASS = 1.0;
46 constexpr double CHAIN_SPRING_DAMPING = 30.0;
47 constexpr double CHAIN_SPRING_STIFFNESS = 228;
48 constexpr float DEFAULT_MIN_SPACE_SCALE = 0.75f;
49 constexpr float DEFAULT_MAX_SPACE_SCALE = 2.0f;
50 constexpr int DEFAULT_HEADER_VALUE = 2;
51 constexpr int DEFAULT_FOOTER_VALUE = 3;
52 #ifdef SUPPORT_DIGITAL_CROWN
53 constexpr const char* HAPTIC_STRENGTH1 = "watchhaptic.feedback.crown.strength3";
54 #endif
55 } // namespace
56
OnModifyDone()57 void ListPattern::OnModifyDone()
58 {
59 Pattern::OnModifyDone();
60 auto host = GetHost();
61 CHECK_NULL_VOID(host);
62 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
63 CHECK_NULL_VOID(listLayoutProperty);
64 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
65 if (axis != GetAxis()) {
66 needReEstimateOffset_ = true;
67 SetAxis(axis);
68 ChangeAxis(GetHost());
69 }
70 if (!GetScrollableEvent()) {
71 AddScrollEvent();
72 auto scrollableEvent = GetScrollableEvent();
73 CHECK_NULL_VOID(scrollableEvent);
74 scrollable_ = scrollableEvent->GetScrollable();
75 #ifdef SUPPORT_DIGITAL_CROWN
76 SetDigitalCrownEvent();
77 #endif
78 }
79
80 SetEdgeEffect();
81
82 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
83 CHECK_NULL_VOID(paintProperty);
84 if (paintProperty->GetScrollBarProperty()) {
85 SetScrollBar(paintProperty->GetScrollBarProperty());
86 }
87
88 SetChainAnimation();
89 if (multiSelectable_ && !isMouseEventInit_) {
90 InitMouseEvent();
91 }
92 if (!multiSelectable_ && isMouseEventInit_) {
93 UninitMouseEvent();
94 }
95 auto focusHub = host->GetFocusHub();
96 CHECK_NULL_VOID(focusHub);
97 focusHub->SetFocusDependence(FocusDependence::AUTO);
98 InitOnKeyEvent(focusHub);
99 Register2DragDropManager();
100 SetAccessibilityAction();
101 auto fadingEdge = GetFadingEdge(paintProperty);
102 auto overlayNode = host->GetOverlayNode();
103 if (!overlayNode && fadingEdge) {
104 CreateAnalyzerOverlay(host);
105 }
106 }
107
GetFadingEdge(RefPtr<ScrollablePaintProperty> & paintProperty)108 bool ListPattern::GetFadingEdge(RefPtr<ScrollablePaintProperty>& paintProperty)
109 {
110 auto defaultFadingEdge = false;
111 if (!paintProperty->HasDefaultFadingEdge()) {
112 auto host = GetHost();
113 CHECK_NULL_RETURN(host, false);
114 auto context = host->GetContextRefPtr();
115 CHECK_NULL_RETURN(context, false);
116 auto listTheme = context->GetTheme<ListTheme>();
117 CHECK_NULL_RETURN(listTheme, false);
118 defaultFadingEdge = GetAxis() == Axis::VERTICAL ? listTheme->GetFadingEdge()
119 : false;
120 paintProperty->UpdateDefaultFadingEdge(defaultFadingEdge);
121 } else {
122 defaultFadingEdge = paintProperty->GetDefaultFadingEdge().value_or(false);
123 }
124 auto fadingEdge = paintProperty->GetFadingEdge().value_or(defaultFadingEdge);
125 return fadingEdge;
126 }
127
ChangeAxis(RefPtr<UINode> node)128 void ListPattern::ChangeAxis(RefPtr<UINode> node)
129 {
130 CHECK_NULL_VOID(node);
131 auto children = node->GetChildren();
132 for (const auto& child : children) {
133 if (AceType::InstanceOf<FrameNode>(child)) {
134 auto frameNode = AceType::DynamicCast<FrameNode>(child);
135 CHECK_NULL_VOID(frameNode);
136 auto listItemPattern = frameNode->GetPattern<ListItemPattern>();
137 if (listItemPattern) {
138 listItemPattern->ChangeAxis(GetAxis());
139 continue;
140 }
141 auto listItemGroupPattern = frameNode->GetPattern<ListItemGroupPattern>();
142 if (listItemGroupPattern) {
143 listItemGroupPattern->ResetLayoutedInfo();
144 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
145 ChangeAxis(child);
146 }
147 } else {
148 ChangeAxis(child);
149 }
150 }
151 }
152
HandleTargetIndex(bool isJump)153 bool ListPattern::HandleTargetIndex(bool isJump)
154 {
155 if (isJump) {
156 MarkDirtyNodeSelf();
157 return true;
158 }
159 auto iter = itemPosition_.find(targetIndex_.value());
160 if (iter == itemPosition_.end()) {
161 ResetExtraOffset();
162 } else if (!isLayoutListForFocus_) {
163 // isLayoutListForFocus_ is true means only do Layout and do NOT call AnimateToTarget.
164 AnimateToTarget(targetIndex_.value(), targetIndexInGroup_, scrollAlign_);
165 }
166
167 // AniamteToTarget does not need to update endIndex and startIndex in the first frame.
168 targetIndex_.reset();
169 targetIndexInGroup_.reset();
170 isLayoutListForFocus_.reset();
171 return false;
172 }
173
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)174 bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
175 {
176 if (config.skipMeasure && config.skipLayout) {
177 return false;
178 }
179 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
180 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
181 auto listLayoutAlgorithm = DynamicCast<ListLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
182 CHECK_NULL_RETURN(listLayoutAlgorithm, false);
183 prevMeasureBreak_ = listLayoutAlgorithm->MeasureInNextFrame();
184 if (!prevMeasureBreak_) {
185 jumpIndexInGroup_.reset();
186 jumpIndex_.reset();
187 }
188 itemPosition_ = listLayoutAlgorithm->GetItemPosition();
189 cachedItemPosition_ = listLayoutAlgorithm->GetCachedItemPosition();
190 laneIdx4Divider_ = listLayoutAlgorithm->GetLaneIdx4Divider();
191 maxListItemIndex_ = listLayoutAlgorithm->GetMaxListItemIndex();
192 spaceWidth_ = listLayoutAlgorithm->GetSpaceWidth();
193 auto predictSnapOffset = listLayoutAlgorithm->GetPredictSnapOffset();
194 auto predictSnapEndPos = listLayoutAlgorithm->GetPredictSnapEndPosition();
195 bool isJump = listLayoutAlgorithm->NeedEstimateOffset();
196 auto lanesLayoutAlgorithm = DynamicCast<ListLanesLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
197 isStackFromEnd_ = listLayoutAlgorithm->GetStackFromEnd();
198 if (lanesLayoutAlgorithm) {
199 lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
200 if (lanesLayoutAlgorithm->GetLanes() != lanes_) {
201 needReEstimateOffset_ = true;
202 auto item = swiperItem_.Upgrade();
203 if (item) {
204 item->ResetSwipeStatus();
205 }
206 }
207 lanes_ = lanesLayoutAlgorithm->GetLanes();
208 laneGutter_ = lanesLayoutAlgorithm->GetLaneGutter();
209 } else {
210 lanes_ = listLayoutAlgorithm->GetLanes();
211 }
212 float relativeOffset = UpdateTotalOffset(listLayoutAlgorithm, isJump);
213 bool isNeedUpdateIndex = targetIndex_ ? HandleTargetIndex(isJump) : true;
214 if (predictSnapOffset.has_value()) {
215 if (scrollable_ && !(NearZero(predictSnapOffset.value()) && NearZero(scrollSnapVelocity_)) &&
216 (!AnimateRunning() || lastSnapTargetIndex_.has_value())) {
217 StartListSnapAnimation(predictSnapOffset.value(), scrollSnapVelocity_);
218 if (snapTrigOnScrollStart_) {
219 FireOnScrollStart();
220 } else {
221 scrollStop_ = false;
222 }
223 } else if (!snapTrigOnScrollStart_) {
224 OnAnimateStop();
225 }
226 scrollSnapVelocity_ = 0.0f;
227 predictSnapOffset_.reset();
228 ResetLastSnapTargetIndex();
229 snapTrigOnScrollStart_ = false;
230 }
231 if (predictSnapEndPos.has_value() && predictSnapEndPos_.has_value() &&
232 !NearEqual(predictSnapEndPos.value(), predictSnapEndPos_.value())) {
233 if (scrollable_) {
234 scrollable_->UpdateScrollSnapEndWithOffset(predictSnapEndPos.value() - predictSnapEndPos_.value());
235 }
236 }
237 predictSnapEndPos_ = predictSnapEndPos;
238
239 if (isScrollEnd_) {
240 auto host = GetHost();
241 CHECK_NULL_RETURN(host, false);
242 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
243 // AccessibilityEventType::SCROLL_END
244 isScrollEnd_ = false;
245 }
246 currentDelta_ = 0.0f;
247 isNeedCheckOffset_ = false;
248 prevStartOffset_ = startMainPos_;
249 prevEndOffset_ = endMainPos_ - contentMainSize_ + contentEndOffset_;
250 contentMainSize_ = listLayoutAlgorithm->GetContentMainSize();
251 contentStartOffset_ = listLayoutAlgorithm->GetContentStartOffset();
252 contentEndOffset_ = listLayoutAlgorithm->GetContentEndOffset();
253 startMainPos_ = listLayoutAlgorithm->GetStartPosition();
254 endMainPos_ = listLayoutAlgorithm->GetEndPosition();
255 crossMatchChild_ = listLayoutAlgorithm->IsCrossMatchChild();
256 if (!prevMeasureBreak_) {
257 auto endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
258 CheckScrollable();
259 if (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty))) {
260 OnMidIndexChanged();
261 }
262 bool indexChanged = false;
263 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
264 if (isNeedUpdateIndex) {
265 indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
266 (endIndex_ != listLayoutAlgorithm->GetEndIndex()) ||
267 (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty)));
268 }
269 } else {
270 indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
271 (endIndex_ != listLayoutAlgorithm->GetEndIndex());
272 }
273 startIndexChanged_ = startIndex_ != listLayoutAlgorithm->GetStartIndex();
274 endIndexChanged_ = endIndex_ != listLayoutAlgorithm->GetEndIndex();
275 if (indexChanged) {
276 startIndex_ = listLayoutAlgorithm->GetStartIndex();
277 endIndex_ = listLayoutAlgorithm->GetEndIndex();
278 centerIndex_ = listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty));
279 }
280 ProcessEvent(indexChanged, relativeOffset, isJump);
281 HandleScrollBarOutBoundary();
282 UpdateScrollBarOffset();
283 if (config.frameSizeChange) {
284 if (GetScrollBar() != nullptr) {
285 GetScrollBar()->ScheduleDisappearDelayTask();
286 }
287 }
288 bool sizeDiminished =
289 !chainAnimation_ && IsOutOfBoundary(false) && (endOffset + relativeOffset - prevEndOffset_ < -0.1f);
290 if (!GetCanStayOverScroll()) {
291 CheckRestartSpring(sizeDiminished);
292 }
293 isInitialized_ = true;
294 } else {
295 ACE_SCOPED_TRACE("List MeasureInNextFrame");
296 auto host = GetHost();
297 CHECK_NULL_RETURN(host, false);
298 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
299 }
300 DrivenRender(dirty);
301
302 ChangeAnimateOverScroll();
303 SetScrollSource(SCROLL_FROM_NONE);
304 MarkSelectedItems();
305 UpdateListDirectionInCardStyle();
306 snapTrigByScrollBar_ = false;
307 ChangeCanStayOverScroll();
308 return true;
309 }
310
UpdateListDirectionInCardStyle()311 void ListPattern::UpdateListDirectionInCardStyle()
312 {
313 if (isNeedToUpdateListDirection_) {
314 auto layoutProperty = GetLayoutProperty<ListLayoutProperty>();
315 layoutProperty->UpdateListDirection(Axis::VERTICAL);
316 isNeedToUpdateListDirection_ = false;
317 }
318 }
319
GetInitialScrollAlign() const320 ScrollAlign ListPattern::GetInitialScrollAlign() const
321 {
322 auto snapAlign = GetScrollSnapAlign();
323 if (snapAlign == ScrollSnapAlign::CENTER) {
324 return ScrollAlign::CENTER;
325 } else if (isStackFromEnd_ && snapAlign == ScrollSnapAlign::START) {
326 return ScrollAlign::END;
327 }
328 return ScrollAlign::START;
329 }
330
CalculateTargetPos(float startPos,float endPos)331 float ListPattern::CalculateTargetPos(float startPos, float endPos)
332 {
333 float topOffset = startPos;
334 float bottomOffset = endPos - contentMainSize_;
335 if (!IsScrollSnapAlignCenter()) {
336 topOffset -= contentStartOffset_;
337 bottomOffset += contentEndOffset_;
338 }
339 if (GreatOrEqual(topOffset, 0.0f) && LessOrEqual(bottomOffset, 0.0f)) {
340 return 0.0f;
341 }
342 if ((NearEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) ||
343 (LessNotEqual(topOffset, 0.0f) && NearEqual(bottomOffset, 0.0f))) {
344 return 0.0f;
345 }
346 if (LessNotEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) {
347 if (GreatOrEqual(std::abs(topOffset), std::abs(bottomOffset))) {
348 return bottomOffset;
349 } else {
350 return topOffset;
351 }
352 }
353 if (GreatNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
354 return bottomOffset;
355 } else if (LessNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
356 return topOffset;
357 } else {
358 if (LessNotEqual(topOffset, 0.0f)) {
359 return topOffset;
360 } else {
361 return bottomOffset;
362 }
363 }
364 return 0.0f;
365 }
366
CreateNodePaintMethod()367 RefPtr<NodePaintMethod> ListPattern::CreateNodePaintMethod()
368 {
369 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
370 V2::ItemDivider divider;
371 if (!chainAnimation_ && listLayoutProperty->HasDivider()) {
372 divider = listLayoutProperty->GetDivider().value();
373 }
374 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
375 auto layoutDirection = listLayoutProperty->GetNonAutoLayoutDirection();
376 auto drawVertical = (axis == Axis::HORIZONTAL);
377 auto drawDirection = (layoutDirection == TextDirection::RTL);
378 auto paint = MakeRefPtr<ListPaintMethod>(divider, drawVertical, drawDirection, lanes_, spaceWidth_);
379 if (drawDirection) {
380 paint->SetDirection(true);
381 }
382 paint->SetScrollBar(GetScrollBar());
383 paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
384 paint->SetTotalItemCount(maxListItemIndex_ + 1);
385 auto scrollEffect = GetScrollEdgeEffect();
386 if (scrollEffect && scrollEffect->IsFadeEffect()) {
387 paint->SetEdgeEffect(scrollEffect);
388 }
389 auto host = GetHost();
390 CHECK_NULL_RETURN(host, paint);
391 const auto& geometryNode = host->GetGeometryNode();
392 auto renderContext = host->GetRenderContext();
393 auto frameRect = renderContext->GetPaintRectWithoutTransform();
394 if (!listContentModifier_) {
395 CHECK_NULL_RETURN(renderContext, paint);
396 auto size = frameRect.GetSize();
397 auto& padding = geometryNode->GetPadding();
398 if (padding) {
399 size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
400 }
401 OffsetF offset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
402 listContentModifier_ = AceType::MakeRefPtr<ListContentModifier>(offset, size);
403 }
404 listContentModifier_->SetIsNeedDividerAnimation(isNeedDividerAnimation_);
405 paint->SetLaneGutter(laneGutter_);
406 bool showCached = listLayoutProperty->GetShowCachedItemsValue(false);
407 bool clip = !renderContext || renderContext->GetClipEdge().value_or(true);
408 paint->SetItemsPosition(itemPosition_, cachedItemPosition_, pressedItem_, showCached, clip);
409 paint->SetLaneIdx(laneIdx4Divider_);
410 paint->SetContentModifier(listContentModifier_);
411 paint->SetAdjustOffset(geometryNode->GetParentAdjust().GetOffset().GetY());
412 paint->UpdateBoundsRect(frameRect, clip);
413 UpdateFadingEdge(paint);
414 return paint;
415 }
416
UpdateStartListItemIndex()417 bool ListPattern::UpdateStartListItemIndex()
418 {
419 auto host = GetHost();
420 CHECK_NULL_RETURN(host, false);
421 CHECK_EQUAL_RETURN(host->GetChildTrueTotalCount(), 0, false);
422 auto startWrapper = host->GetOrCreateChildByIndex(startIndex_);
423 int32_t startArea = -1;
424 int32_t startItemIndexInGroup = -1;
425 bool startFlagChanged = (startInfo_.index != startIndex_);
426 bool startIsGroup = startWrapper && startWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
427 if (startIsGroup) {
428 auto startPattern = startWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
429 VisibleContentInfo startGroupInfo = startPattern->GetStartListItemIndex();
430 startFlagChanged = startFlagChanged || (startInfo_.area != startGroupInfo.area) ||
431 (startInfo_.indexInGroup != startGroupInfo.indexInGroup);
432 startArea = startGroupInfo.area;
433 startItemIndexInGroup = startGroupInfo.indexInGroup;
434 if (startFlagChanged) {
435 VisibleContentInfo endGroupInfo = startPattern->GetEndListItemIndex();
436 int32_t endItemIndexInGroup = endGroupInfo.indexInGroup;
437 startWrapper->GetHostNode()->OnAccessibilityEvent(
438 AccessibilityEventType::SCROLLING_EVENT, startItemIndexInGroup, endItemIndexInGroup);
439 }
440 }
441 startInfo_ = { startIndex_, startArea, startItemIndexInGroup };
442 return startFlagChanged;
443 }
444
UpdateEndListItemIndex()445 bool ListPattern::UpdateEndListItemIndex()
446 {
447 auto host = GetHost();
448 CHECK_NULL_RETURN(host, false);
449 CHECK_EQUAL_RETURN(host->GetChildTrueTotalCount(), 0, false);
450 auto endWrapper = host->GetOrCreateChildByIndex(endIndex_);
451 int32_t endArea = -1;
452 int32_t endItemIndexInGroup = -1;
453 bool endFlagChanged = (endInfo_.index != endIndex_);
454 bool endIsGroup = endWrapper && endWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
455 if (endIsGroup) {
456 auto endPattern = endWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>();
457 VisibleContentInfo endGroupInfo = endPattern->GetEndListItemIndex();
458 endFlagChanged = endFlagChanged || (endInfo_.area != endGroupInfo.area) ||
459 (endInfo_.indexInGroup != endGroupInfo.indexInGroup);
460 endArea = endGroupInfo.area;
461 endItemIndexInGroup = endGroupInfo.indexInGroup;
462 if (endFlagChanged) {
463 VisibleContentInfo startGroupInfo = endPattern->GetStartListItemIndex();
464 int32_t startItemIndexInGroup = startGroupInfo.indexInGroup;
465 endWrapper->GetHostNode()->OnAccessibilityEvent(
466 AccessibilityEventType::SCROLLING_EVENT, startItemIndexInGroup, endItemIndexInGroup);
467 }
468 }
469 endInfo_ = { endIndex_, endArea, endItemIndexInGroup };
470 return endFlagChanged;
471 }
472
ProcessEvent(bool indexChanged,float finalOffset,bool isJump)473 void ListPattern::ProcessEvent(bool indexChanged, float finalOffset, bool isJump)
474 {
475 auto host = GetHost();
476 CHECK_NULL_VOID(host);
477 auto listEventHub = host->GetOrCreateEventHub<ListEventHub>();
478 CHECK_NULL_VOID(listEventHub);
479 paintStateFlag_ = !NearZero(finalOffset) && !isJump;
480 isFramePaintStateValid_ = true;
481 auto onScroll = listEventHub->GetOnScroll();
482 PrintOffsetLog(AceLogTag::ACE_LIST, host->GetId(), finalOffset);
483 if (onScroll) {
484 FireOnScrollWithVersionCheck(finalOffset, onScroll);
485 }
486 FireObserverOnDidScroll(finalOffset);
487 FireObserverOnScrollerAreaChange(finalOffset);
488 auto onDidScroll = listEventHub->GetOnDidScroll();
489 if (onDidScroll) {
490 FireOnScroll(finalOffset, onDidScroll);
491 }
492 auto onJSFrameNodeDidScroll = listEventHub->GetJSFrameNodeOnDidScroll();
493 if (onJSFrameNodeDidScroll) {
494 FireOnScroll(finalOffset, onJSFrameNodeDidScroll);
495 }
496 auto onScrollIndex = listEventHub->GetOnScrollIndex();
497 auto onJSFrameNodeScrollIndex = listEventHub->GetJSFrameNodeOnListScrollIndex();
498 FireOnScrollIndex(indexChanged, onScrollIndex);
499 FireOnScrollIndex(indexChanged, onJSFrameNodeScrollIndex);
500 if (startIndexChanged_ || endIndexChanged_) {
501 host->OnAccessibilityEvent(AccessibilityEventType::SCROLLING_EVENT, startIndex_, endIndex_);
502 }
503 OnScrollVisibleContentChange(listEventHub, indexChanged);
504 auto onReachStart = listEventHub->GetOnReachStart();
505 auto onJSFrameNodeReachStart = listEventHub->GetJSFrameNodeOnReachStart();
506 FireOnReachStart(onReachStart, onJSFrameNodeReachStart);
507 auto onReachEnd = listEventHub->GetOnReachEnd();
508 auto onJSFrameNodeReachEnd = listEventHub->GetJSFrameNodeOnReachEnd();
509 FireOnReachEnd(onReachEnd, onJSFrameNodeReachEnd);
510 OnScrollStop(listEventHub->GetOnScrollStop(), listEventHub->GetJSFrameNodeOnScrollStop());
511 ProcessFocusEvent(indexChanged);
512 }
513
FireOnScrollWithVersionCheck(float finalOffset,OnScrollEvent & onScroll)514 void ListPattern::FireOnScrollWithVersionCheck(float finalOffset, OnScrollEvent& onScroll)
515 {
516 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
517 FireOnScroll(finalOffset, onScroll);
518 } else {
519 if (!NearZero(finalOffset)) {
520 auto offsetPX = Dimension(finalOffset);
521 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
522 auto source = GetScrollSource();
523 if (source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR || source == SCROLL_FROM_ANIMATION_CONTROLLER) {
524 source = SCROLL_FROM_NONE;
525 }
526 onScroll(offsetVP, GetScrollState(source));
527 }
528 }
529 }
530
FireOnReachStart(const OnReachEvent & onReachStart,const OnReachEvent & onJSFrameNodeReachStart)531 void ListPattern::FireOnReachStart(const OnReachEvent& onReachStart, const OnReachEvent& onJSFrameNodeReachStart)
532 {
533 auto host = GetHost();
534 CHECK_NULL_VOID(host);
535 if (startIndex_ == 0) {
536 bool scrollUpToStart =
537 GreatNotEqual(prevStartOffset_, contentStartOffset_) && LessOrEqual(startMainPos_, contentStartOffset_);
538 bool scrollDownToStart =
539 (startIndexChanged_ || LessNotEqual(prevStartOffset_, contentStartOffset_) || !isInitialized_) &&
540 GreatOrEqual(startMainPos_, contentStartOffset_);
541 if (scrollUpToStart || scrollDownToStart) {
542 FireObserverOnReachStart();
543 ReportOnItemListEvent("onReachStart");
544 CHECK_NULL_VOID(onReachStart || onJSFrameNodeReachStart);
545 ACE_SCOPED_TRACE("OnReachStart, scrollUpToStart:%u, scrollDownToStart:%u, id:%d, tag:List",
546 scrollUpToStart, scrollDownToStart, static_cast<int32_t>(host->GetAccessibilityId()));
547 if (onReachStart) {
548 onReachStart();
549 }
550 if (onJSFrameNodeReachStart) {
551 onJSFrameNodeReachStart();
552 }
553 AddEventsFiredInfo(ScrollableEventType::ON_REACH_START);
554 }
555 }
556 }
557
FireOnReachEnd(const OnReachEvent & onReachEnd,const OnReachEvent & onJSFrameNodeReachEnd)558 void ListPattern::FireOnReachEnd(const OnReachEvent& onReachEnd, const OnReachEvent& onJSFrameNodeReachEnd)
559 {
560 auto host = GetHost();
561 CHECK_NULL_VOID(host);
562 if (endIndex_ == GetMaxIndexByRepeat()) {
563 float endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
564 // deltaOffset passes through multiple items also needs to fire reachEnd
565 bool scrollUpToEnd =
566 (endIndexChanged_ || (Positive(prevEndOffset_) || !isInitialized_)) && NonPositive(endOffset);
567 bool scrollDownToEnd = Negative(prevEndOffset_) && NonNegative(endOffset);
568 auto scrollSource = GetScrollSource();
569 if (scrollUpToEnd || (scrollDownToEnd && scrollSource != SCROLL_FROM_NONE)) {
570 FireObserverOnReachEnd();
571 ReportOnItemListEvent("onReachEnd");
572 CHECK_NULL_VOID(onReachEnd || onJSFrameNodeReachEnd);
573 ACE_SCOPED_TRACE("OnReachEnd, scrollUpToEnd:%u, scrollDownToEnd:%u, scrollSource:%d, id:%d, tag:List",
574 scrollUpToEnd, scrollDownToEnd, scrollSource, static_cast<int32_t>(host->GetAccessibilityId()));
575 if (onReachEnd) {
576 onReachEnd();
577 }
578 if (onJSFrameNodeReachEnd) {
579 onJSFrameNodeReachEnd();
580 }
581 AddEventsFiredInfo(ScrollableEventType::ON_REACH_END);
582 }
583 }
584 }
585
FireOnScrollIndex(bool indexChanged,const OnScrollIndexEvent & onScrollIndex)586 void ListPattern::FireOnScrollIndex(bool indexChanged, const OnScrollIndexEvent& onScrollIndex)
587 {
588 CHECK_NULL_VOID(indexChanged && onScrollIndex);
589 int32_t startIndex = startIndex_ == -1 ? 0 : startIndex_;
590 int32_t endIndex = endIndex_ == -1 ? 0 : endIndex_;
591 if (SystemProperties::IsWhiteBlockEnabled()) {
592 endIndex = ScrollAdjustmanager::GetInstance().AdjustEndIndex(endIndex);
593 }
594 onScrollIndex(startIndex, endIndex, centerIndex_);
595 ReportOnItemListScrollEvent("onScrollIndex", startIndex, endIndex);
596 }
597
DrivenRender(const RefPtr<LayoutWrapper> & layoutWrapper)598 void ListPattern::DrivenRender(const RefPtr<LayoutWrapper>& layoutWrapper)
599 {
600 auto host = GetHost();
601 CHECK_NULL_VOID(host);
602 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
603 auto listPaintProperty = host->GetPaintProperty<ScrollablePaintProperty>();
604 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
605 auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
606 bool barNeedPaint = GetScrollBar() ? GetScrollBar()->NeedPaint() : false;
607 auto chainAnimation = listLayoutProperty->GetChainAnimation().value_or(false);
608 bool drivenRender = !(axis != Axis::VERTICAL || stickyStyle != V2::StickyStyle::NONE || barNeedPaint ||
609 chainAnimation || !isScrollable_);
610
611 auto renderContext = host->GetRenderContext();
612 CHECK_NULL_VOID(renderContext);
613 renderContext->MarkDrivenRender(drivenRender);
614 if (drivenRender && isFramePaintStateValid_) {
615 // Mark items
616 int32_t indexStep = 0;
617 int32_t startIndex = itemPosition_.empty() ? 0 : itemPosition_.begin()->first;
618 for (auto& pos : itemPosition_) {
619 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first + itemStartIndex_);
620 CHECK_NULL_VOID(wrapper);
621 auto itemHost = wrapper->GetHostNode();
622 CHECK_NULL_VOID(itemHost);
623 auto itemRenderContext = itemHost->GetRenderContext();
624 CHECK_NULL_VOID(itemRenderContext);
625 itemRenderContext->MarkDrivenRenderItemIndex(startIndex + indexStep);
626 indexStep++;
627 }
628 renderContext->MarkDrivenRenderFramePaintState(paintStateFlag_);
629 isFramePaintStateValid_ = false;
630 }
631 }
632
CheckScrollable()633 void ListPattern::CheckScrollable()
634 {
635 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
636 CHECK_NULL_VOID(listProperty);
637 auto lastScrollable = isScrollable_;
638 if (itemPosition_.empty()) {
639 isScrollable_ = false;
640 } else {
641 if ((itemPosition_.begin()->first == 0) && (itemPosition_.rbegin()->first == maxListItemIndex_) &&
642 !IsScrollSnapAlignCenter()) {
643 isScrollable_ = GetAlwaysEnabled() || GreatNotEqual(endMainPos_ - startMainPos_,
644 contentMainSize_ - contentStartOffset_ - contentEndOffset_);
645 } else {
646 isScrollable_ = true;
647 }
648 }
649 if (chainAnimation_ && lastScrollable && !isScrollable_) {
650 ACE_SCOPED_TRACE("Scrollable List changes to un-scrollable, reset chainAnimation");
651 chainAnimation_.Reset();
652 }
653 if (!chainAnimation_ && !lastScrollable && isScrollable_) {
654 SetChainAnimation();
655 }
656 SetScrollEnabled(isScrollable_);
657
658 if (!listProperty->GetScrollEnabled().value_or(isScrollable_)) {
659 SetScrollEnabled(false);
660 }
661 }
662
CreateLayoutAlgorithm()663 RefPtr<LayoutAlgorithm> ListPattern::CreateLayoutAlgorithm()
664 {
665 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
666 CHECK_NULL_RETURN(listLayoutProperty, nullptr);
667 RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm;
668 if (listLayoutProperty->HasLanes() || listLayoutProperty->HasLaneMinLength() ||
669 listLayoutProperty->HasLaneMaxLength()) {
670 auto lanesLayoutAlgorithm = MakeRefPtr<ListLanesLayoutAlgorithm>();
671 RefreshLanesItemRange();
672 lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
673 lanesLayoutAlgorithm->SetLanes(lanes_);
674 listLayoutAlgorithm.Swap(lanesLayoutAlgorithm);
675 } else {
676 listLayoutAlgorithm.Swap(MakeRefPtr<ListLayoutAlgorithm>());
677 }
678 if (!posMap_) {
679 posMap_ = MakeRefPtr<ListPositionMap>();
680 }
681
682 SetLayoutAlgorithmParams(listLayoutAlgorithm, listLayoutProperty);
683
684 auto pipeline = GetContext();
685 if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
686 listLayoutAlgorithm->SetIsRoundingMode();
687 }
688 return listLayoutAlgorithm;
689 }
690
SetLayoutAlgorithmParams(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,const RefPtr<ListLayoutProperty> & listLayoutProperty)691 void ListPattern::SetLayoutAlgorithmParams(
692 const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, const RefPtr<ListLayoutProperty>& listLayoutProperty)
693 {
694 CHECK_NULL_VOID(listLayoutAlgorithm);
695 CHECK_NULL_VOID(listLayoutProperty);
696 if (childrenSize_) {
697 listLayoutAlgorithm->SetListChildrenMainSize(childrenSize_);
698 }
699 listLayoutAlgorithm->SetListPositionMap(posMap_);
700 SetLayoutAlgorithmJumpAlign(listLayoutAlgorithm, listLayoutProperty);
701 if (targetIndex_) {
702 listLayoutAlgorithm->SetTargetIndex(targetIndex_.value());
703 listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
704 }
705 if (jumpIndexInGroup_) {
706 listLayoutAlgorithm->SetIndexInGroup(jumpIndexInGroup_.value());
707 }
708 if (targetIndexInGroup_) {
709 listLayoutAlgorithm->SetTargetIndexInGroup(targetIndexInGroup_.value());
710 }
711 SetLayoutAlgorithmSnapParam(listLayoutAlgorithm);
712 listLayoutAlgorithm->SetCurrentDelta(currentDelta_);
713 listLayoutAlgorithm->SetIsNeedCheckOffset(isNeedCheckOffset_);
714 listLayoutAlgorithm->SetItemsPosition(itemPosition_);
715 listLayoutAlgorithm->SetPrevContentMainSize(contentMainSize_);
716 listLayoutAlgorithm->SetPrevContentStartOffset(contentStartOffset_);
717 listLayoutAlgorithm->SetPrevContentEndOffset(contentEndOffset_);
718 if (IsOutOfBoundary(false) && GetScrollSource() != SCROLL_FROM_AXIS) {
719 listLayoutAlgorithm->SetOverScrollFeature();
720 }
721 listLayoutAlgorithm->SetIsSpringEffect(IsScrollableSpringEffect());
722 listLayoutAlgorithm->SetCanOverScrollStart(JudgeCanOverScrollStart());
723 listLayoutAlgorithm->SetCanOverScrollEnd(JudgeCanOverScrollEnd());
724 if (chainAnimation_ && GetEffectEdge() == EffectEdge::ALL) {
725 SetChainAnimationLayoutAlgorithm(listLayoutAlgorithm, listLayoutProperty);
726 SetChainAnimationToPosMap();
727 }
728 listLayoutAlgorithm->SetPrevMeasureBreak(prevMeasureBreak_);
729 listLayoutAlgorithm->SetDraggingIndex(draggingIndex_);
730 }
731
JudgeCanOverScrollStart()732 bool ListPattern::JudgeCanOverScrollStart()
733 {
734 auto source = GetScrollSource();
735 if (!CanOverScrollStart(source)) {
736 return false;
737 }
738 if (IsAtTop()) {
739 return true;
740 }
741
742 return source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING;
743 }
744
JudgeCanOverScrollEnd()745 bool ListPattern::JudgeCanOverScrollEnd()
746 {
747 auto source = GetScrollSource();
748 if (!CanOverScrollEnd(source)) {
749 return false;
750 }
751 if (IsAtBottom(true)) {
752 return true;
753 }
754 return source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING;
755 }
756
SetChainAnimationToPosMap()757 void ListPattern::SetChainAnimationToPosMap()
758 {
759 CHECK_NULL_VOID(posMap_);
760 posMap_->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
761 auto list = weak.Upgrade();
762 CHECK_NULL_RETURN(list, 0.0f);
763 return list->GetChainDelta(index);
764 });
765 }
766
SetChainAnimationLayoutAlgorithm(RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm,const RefPtr<ListLayoutProperty> & listLayoutProperty)767 void ListPattern::SetChainAnimationLayoutAlgorithm(
768 RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm, const RefPtr<ListLayoutProperty>& listLayoutProperty)
769 {
770 CHECK_NULL_VOID(listLayoutAlgorithm);
771 CHECK_NULL_VOID(listLayoutProperty);
772 listLayoutAlgorithm->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
773 auto list = weak.Upgrade();
774 CHECK_NULL_RETURN(list, 0.0f);
775 return list->GetChainDelta(index);
776 });
777 if (chainAnimation_ && chainAnimation_->HasSpaceDelta() && !itemPosition_.empty()) {
778 auto res = GetOutBoundaryOffset(currentDelta_, false);
779 if ((NearZero(res.start) || !CanOverScrollStart(GetScrollSource())) &&
780 (NearZero(res.end) || !CanOverScrollEnd(GetScrollSource()))) {
781 chainAnimation_->ResetSpaceDelta();
782 }
783 }
784 if (!listLayoutProperty->GetSpace().has_value() && chainAnimation_) {
785 float chainInterval = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
786 auto pipeline = GetContext();
787 if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
788 chainInterval = Round(chainInterval);
789 }
790 listLayoutAlgorithm->SetChainInterval(chainInterval);
791 }
792 }
793
SetLayoutAlgorithmJumpAlign(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,const RefPtr<ListLayoutProperty> & listLayoutProperty)794 void ListPattern::SetLayoutAlgorithmJumpAlign(
795 const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, const RefPtr<ListLayoutProperty>& listLayoutProperty)
796 {
797 CHECK_NULL_VOID(listLayoutAlgorithm);
798 CHECK_NULL_VOID(listLayoutProperty);
799 bool needUseInitialIndex = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FOURTEEN) ?
800 !isInitialized_ && !jumpIndex_ : !isInitialized_;
801 if (needUseInitialIndex) {
802 isStackFromEnd_ = listLayoutProperty->GetStackFromEnd().value_or(false);
803 int32_t defaultInitialIndex = isStackFromEnd_ ? ListLayoutAlgorithm::LAST_ITEM : 0;
804 jumpIndex_ = listLayoutProperty->GetInitialIndex().value_or(defaultInitialIndex);
805 if (NeedScrollSnapAlignEffect()) {
806 scrollAlign_ = GetInitialScrollAlign();
807 } else if (isStackFromEnd_) {
808 scrollAlign_ = ScrollAlign::END;
809 }
810 }
811 if (jumpIndex_) {
812 listLayoutAlgorithm->SetIndex(jumpIndex_.value());
813 listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
814 }
815 }
816
SetLayoutAlgorithmSnapParam(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm)817 void ListPattern::SetLayoutAlgorithmSnapParam(const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm)
818 {
819 CHECK_NULL_VOID(listLayoutAlgorithm);
820 if (predictSnapOffset_.has_value()) {
821 listLayoutAlgorithm->SetPredictSnapOffset(predictSnapOffset_.value());
822 listLayoutAlgorithm->SetScrollSnapVelocity(scrollSnapVelocity_);
823 }
824 listLayoutAlgorithm->SetTotalOffset(GetTotalOffset());
825 if (scrollable_) {
826 auto snapOffset = scrollable_->GetPredictSnapOffset();
827 if (snapOffset) {
828 predictSnapEndPos_ = GetTotalOffset() - snapOffset.value() + currentDelta_;
829 } else {
830 predictSnapEndPos_.reset();
831 }
832 }
833 if (predictSnapEndPos_.has_value()) {
834 listLayoutAlgorithm->SetPredictSnapEndPosition(predictSnapEndPos_.value());
835 }
836 }
837
IsScrollSnapAlignCenter() const838 bool ListPattern::IsScrollSnapAlignCenter() const
839 {
840 auto snapAlign = GetScrollSnapAlign();
841 return snapAlign == ScrollSnapAlign::CENTER;
842 }
843
NeedScrollSnapAlignEffect() const844 bool ListPattern::NeedScrollSnapAlignEffect() const
845 {
846 auto snapAlign = GetScrollSnapAlign();
847 return snapAlign != ScrollSnapAlign::NONE;
848 }
849
GetScrollSnapAlign() const850 ScrollSnapAlign ListPattern::GetScrollSnapAlign() const
851 {
852 auto host = GetHost();
853 CHECK_NULL_RETURN(host, ScrollSnapAlign::NONE);
854 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
855 CHECK_NULL_RETURN(listProperty, ScrollSnapAlign::NONE);
856 return listProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE);
857 }
858
IsAtTop() const859 bool ListPattern::IsAtTop() const
860 {
861 bool groupAtStart = true;
862 bool groupAtEnd = true;
863 GetListItemGroupEdge(groupAtStart, groupAtEnd);
864 int32_t startIndex = startIndex_;
865 float startMainPos = startMainPos_;
866 float endMainPos = endMainPos_;
867 auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
868 if (GreatNotEqual(contentMainSize, endMainPos - startMainPos) && isStackFromEnd_) {
869 startMainPos = endMainPos - contentMainSize;
870 }
871 return (startIndex == 0 && groupAtStart) &&
872 NonNegative(startMainPos - currentDelta_ + GetChainDelta(0) - contentStartOffset_);
873 }
874
IsAtBottom(bool considerRepeat) const875 bool ListPattern::IsAtBottom(bool considerRepeat) const
876 {
877 bool groupAtStart = true;
878 bool groupAtEnd = true;
879 GetListItemGroupEdge(groupAtStart, groupAtEnd);
880 int32_t endIndex = endIndex_;
881 float endMainPos = endMainPos_;
882 float startMainPos = startMainPos_;
883 auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
884 if (startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos) && !isStackFromEnd_) {
885 endMainPos = startMainPos + contentMainSize;
886 }
887 auto maxListItemIndex = considerRepeat ? GetMaxIndexByRepeat() : maxListItemIndex_;
888 return (endIndex == maxListItemIndex && groupAtEnd) &&
889 LessOrEqual(endMainPos - currentDelta_ + GetChainDelta(endIndex), contentMainSize_ - contentEndOffset_);
890 }
891
GetListItemGroupEdge(bool & groupAtStart,bool & groupAtEnd) const892 void ListPattern::GetListItemGroupEdge(bool& groupAtStart, bool& groupAtEnd) const
893 {
894 if (itemPosition_.empty()) {
895 return;
896 }
897 if (startIndex_ == 0 && itemPosition_.begin()->second.isGroup) {
898 auto& groupInfo = itemPosition_.begin()->second.groupInfo;
899 groupAtStart = groupInfo && groupInfo.value().atStart;
900 }
901 if (endIndex_ == maxListItemIndex_ && itemPosition_.rbegin()->second.isGroup) {
902 auto& groupInfo = itemPosition_.rbegin()->second.groupInfo;
903 groupAtEnd = groupInfo && groupInfo.value().atEnd;
904 }
905 }
906
GetOffsetWithLimit(float offset) const907 float ListPattern::GetOffsetWithLimit(float offset) const
908 {
909 float currentOffset = GetTotalOffset() + contentStartOffset_;
910 if (Positive(offset)) {
911 return std::min(currentOffset, offset);
912 } else if (Negative(offset)) {
913 auto remainHeight = GetTotalHeight() - currentOffset;
914 return std::max(offset, -remainHeight);
915 }
916 return 0;
917 }
918
GetOverScrollOffset(double delta) const919 OverScrollOffset ListPattern::GetOverScrollOffset(double delta) const
920 {
921 OverScrollOffset offset = { 0, 0 };
922 bool groupAtStart = true;
923 bool groupAtEnd = true;
924 GetListItemGroupEdge(groupAtStart, groupAtEnd);
925
926 int32_t startIndex = startIndex_;
927 float startMainPos = startMainPos_;
928 int32_t endIndex = endIndex_;
929 float endMainPos = endMainPos_;
930 if (startIndex == 0 && groupAtStart) {
931 offset.start = GetStartOverScrollOffset(delta, startMainPos);
932 }
933 if (endIndex == GetMaxIndexByRepeat() && groupAtEnd) {
934 offset.end = GetEndOverScrollOffset(delta, endMainPos, startMainPos);
935 }
936 return offset;
937 }
938
GetStartOverScrollOffset(float offset,float startMainPos) const939 float ListPattern::GetStartOverScrollOffset(float offset, float startMainPos) const
940 {
941 float startOffset = 0.0f;
942 float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(0, -offset) : 0.f;
943 auto startPos = startMainPos + ChainDelta - currentDelta_;
944 auto contentStartPos = contentStartOffset_;
945 auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
946 if (GreatNotEqual(contentMainSize, endMainPos_ - startMainPos_) && isStackFromEnd_) {
947 contentStartPos = contentMainSize - endMainPos_ + startMainPos_;
948 }
949 auto newStartPos = startPos + offset;
950 if (startPos > contentStartPos && newStartPos > contentStartPos) {
951 startOffset = offset;
952 }
953 if (startPos > contentStartPos && newStartPos <= contentStartPos) {
954 startOffset = contentStartPos - startPos;
955 }
956 if (startPos <= contentStartPos && newStartPos > contentStartPos) {
957 startOffset = newStartPos - contentStartPos;
958 }
959 return startOffset;
960 }
961
GetEndOverScrollOffset(float offset,float endMainPos,float startMainPos) const962 float ListPattern::GetEndOverScrollOffset(float offset, float endMainPos, float startMainPos) const
963 {
964 float endOffset = 0.0f;
965 float ChainDelta = chainAnimation_ ? chainAnimation_->GetValuePredict(maxListItemIndex_, -offset) : 0.f;
966 auto endPos = endMainPos + ChainDelta - currentDelta_;
967 auto contentEndPos = contentMainSize_ - contentEndOffset_;
968 auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
969 if (GreatNotEqual(contentMainSize, endMainPos - startMainPos) && !isStackFromEnd_) {
970 endPos = startMainPos + contentMainSize;
971 }
972 auto newEndPos = endPos + offset;
973 if (endPos < contentEndPos && newEndPos < contentEndPos) {
974 endOffset = offset;
975 }
976 if (endPos < contentEndPos && newEndPos >= contentEndPos) {
977 endOffset = contentEndPos - endPos;
978 }
979 if (endPos >= contentEndPos && newEndPos < contentEndPos) {
980 endOffset = newEndPos - contentEndPos;
981 }
982 return endOffset;
983 }
984
GetOutBoundaryOffset(float delta,bool useChainDelta) const985 OverScrollOffset ListPattern::GetOutBoundaryOffset(float delta, bool useChainDelta) const
986 {
987 OverScrollOffset offset = { 0, 0 };
988 bool groupAtStart = true;
989 bool groupAtEnd = true;
990 GetListItemGroupEdge(groupAtStart, groupAtEnd);
991
992 int32_t startIndex = startIndex_;
993 float startMainPos = startMainPos_;
994 int32_t endIndex = endIndex_;
995 float endMainPos = endMainPos_;
996 if (startIndex == 0 && groupAtStart) {
997 auto startChainDelta = useChainDelta ? GetChainDelta(0) : 0.0f;
998 auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
999 if (endIndex_ == maxListItemIndex_ && GreatNotEqual(contentMainSize, endMainPos - startMainPos) &&
1000 isStackFromEnd_) {
1001 startMainPos = endMainPos - contentMainSize;
1002 }
1003 offset.start = startMainPos - delta + startChainDelta - contentStartOffset_;
1004 offset.start = std::max(offset.start, 0.0);
1005 }
1006 if (endIndex >= maxListItemIndex_ && groupAtEnd) {
1007 auto endChainDelta = useChainDelta ? GetChainDelta(endIndex) : 0.0f;
1008 endMainPos = endMainPos + endChainDelta;
1009 auto contentMainSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_;
1010 if (startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos)) {
1011 endMainPos = startMainPos + contentMainSize;
1012 }
1013 offset.end = contentMainSize_ - contentEndOffset_ - (endMainPos - delta);
1014 offset.end = std::max(offset.end, 0.0);
1015 }
1016 return offset;
1017 }
1018
UpdateCurrentOffset(float offset,int32_t source)1019 bool ListPattern::UpdateCurrentOffset(float offset, int32_t source)
1020 {
1021 // check edgeEffect is not springEffect
1022 if (!jumpIndex_.has_value() && !targetIndex_.has_value() && !HandleEdgeEffect(offset, source, GetContentSize())) {
1023 if (IsOutOfBoundary(false)) {
1024 MarkDirtyNodeSelf();
1025 }
1026 return false;
1027 }
1028
1029 SetScrollSource(source);
1030 FireAndCleanScrollingListener();
1031 auto lastDelta = currentDelta_;
1032 currentDelta_ = currentDelta_ - offset;
1033 if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING || source == SCROLL_FROM_STATUSBAR) {
1034 isNeedCheckOffset_ = true;
1035 }
1036 if (!NearZero(offset)) {
1037 MarkDirtyNodeSelf();
1038 }
1039 if (itemPosition_.empty() || !IsOutOfBoundary() || !isScrollable_) {
1040 auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
1041 userOffset = FireObserverOnWillScroll(userOffset);
1042 currentDelta_ = lastDelta + userOffset;
1043 return true;
1044 }
1045
1046 if (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_CROWN) {
1047 auto res = GetOutBoundaryOffset(currentDelta_);
1048 // over scroll in drag update from normal to over scroll.
1049 float overScroll = std::max(res.start, res.end);
1050 // adjust offset.
1051 auto friction = GetScrollUpdateFriction(overScroll);
1052 offset = offset * friction;
1053 currentDelta_ = lastDelta - offset;
1054 }
1055
1056 auto userOffset = FireOnWillScroll(currentDelta_ - lastDelta);
1057 userOffset = FireObserverOnWillScroll(userOffset);
1058 currentDelta_ = lastDelta + userOffset;
1059 MarkScrollBarProxyDirty();
1060 return true;
1061 }
1062
MarkDirtyNodeSelf()1063 void ListPattern::MarkDirtyNodeSelf()
1064 {
1065 auto host = GetHost();
1066 CHECK_NULL_VOID(host);
1067 if (!crossMatchChild_) {
1068 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1069 } else {
1070 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
1071 }
1072 }
1073
OnScrollEndCallback()1074 void ListPattern::OnScrollEndCallback()
1075 {
1076 if (AnimateStoped()) {
1077 scrollStop_ = true;
1078 MarkDirtyNodeSelf();
1079 }
1080 }
1081
GetContentSize() const1082 SizeF ListPattern::GetContentSize() const
1083 {
1084 auto host = GetHost();
1085 CHECK_NULL_RETURN(host, SizeF());
1086 auto geometryNode = host->GetGeometryNode();
1087 CHECK_NULL_RETURN(geometryNode, SizeF());
1088 auto renderContext = host->GetRenderContext();
1089 CHECK_NULL_RETURN(renderContext, SizeF());
1090 auto size = renderContext->GetPaintRectWithoutTransform().GetSize();
1091 auto& padding = geometryNode->GetPadding();
1092 if (padding) {
1093 size.MinusPadding(*padding->left, *padding->right, *padding->top, *padding->bottom);
1094 }
1095 return size;
1096 }
1097
IsOutOfBoundary(bool useCurrentDelta)1098 bool ListPattern::IsOutOfBoundary(bool useCurrentDelta)
1099 {
1100 if (itemPosition_.empty()) {
1101 return false;
1102 }
1103 auto currentDelta = useCurrentDelta ? currentDelta_ : 0.0f;
1104 auto res = GetOutBoundaryOffset(currentDelta);
1105 // over scroll in drag update from normal to over scroll.
1106 return Positive(res.start) || Positive(res.end);
1107 }
1108
OnScrollCallback(float offset,int32_t source)1109 bool ListPattern::OnScrollCallback(float offset, int32_t source)
1110 {
1111 if (source == SCROLL_FROM_START) {
1112 auto item = swiperItem_.Upgrade();
1113 if (item) {
1114 item->ResetSwipeStatus();
1115 }
1116 FireOnScrollStart();
1117 return true;
1118 }
1119 ProcessDragUpdate(offset, source);
1120 return UpdateCurrentOffset(offset, source);
1121 }
1122
StartSnapAnimation(SnapAnimationOptions snapAnimationOptions)1123 bool ListPattern::StartSnapAnimation(SnapAnimationOptions snapAnimationOptions)
1124 {
1125 auto snapDirection = snapAnimationOptions.snapDirection;
1126 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
1127 CHECK_NULL_RETURN(listProperty, false);
1128 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(ScrollSnapAlign::NONE);
1129 CHECK_NULL_RETURN(scrollSnapAlign != ScrollSnapAlign::NONE, false);
1130 if (snapDirection != SnapDirection::NONE) {
1131 return ScrollToSnapIndex(snapDirection, scrollSnapAlign);
1132 }
1133 if (snapAnimationOptions.fromScrollBar &&
1134 ((GreatNotEqual(snapAnimationOptions.animationVelocity, 0.0f) && IsAtTop()) ||
1135 (LessNotEqual(snapAnimationOptions.animationVelocity, 0.0f) && IsAtBottom()))) {
1136 return false;
1137 }
1138 if (!IsScrolling()) {
1139 snapTrigOnScrollStart_ = true;
1140 }
1141 predictSnapOffset_ = snapAnimationOptions.snapDelta;
1142 scrollSnapVelocity_ = snapAnimationOptions.animationVelocity;
1143 snapTrigByScrollBar_ = snapAnimationOptions.fromScrollBar;
1144 predictSnapEndPos_.reset();
1145 MarkDirtyNodeSelf();
1146 return true;
1147 }
1148
ScrollToSnapIndex(SnapDirection snapDirection,ScrollSnapAlign scrollSnapAlign)1149 bool ListPattern::ScrollToSnapIndex(SnapDirection snapDirection, ScrollSnapAlign scrollSnapAlign)
1150 {
1151 ScrollAlign align = ScrollAlign::NONE;
1152 auto anchorIndex = startIndex_;
1153 switch (scrollSnapAlign) {
1154 case ScrollSnapAlign::NONE:
1155 return false;
1156 case ScrollSnapAlign::START:
1157 align = ScrollAlign::START;
1158 anchorIndex = GetStartIndexExcludeStartOffset();
1159 break;
1160 case ScrollSnapAlign::CENTER:
1161 align = ScrollAlign::CENTER;
1162 anchorIndex = centerIndex_;
1163 break;
1164 case ScrollSnapAlign::END:
1165 align = ScrollAlign::END;
1166 anchorIndex = GetEndIndexExcludeEndOffset();
1167 break;
1168 }
1169 if (snapDirection == SnapDirection::FORWARD) {
1170 if (!lastSnapTargetIndex_.has_value()) {
1171 if (align == ScrollAlign::START) {
1172 auto isAligned = GreatOrEqual(itemPosition_[anchorIndex].startPos, contentStartOffset_);
1173 lastSnapTargetIndex_ = isAligned ? anchorIndex - 1 : anchorIndex;
1174 } else if (align == ScrollAlign::END) {
1175 lastSnapTargetIndex_ = anchorIndex - 1;
1176 } else {
1177 auto item = itemPosition_[anchorIndex];
1178 auto itemCenterPos = (item.startPos + item.endPos) / 2;
1179 lastSnapTargetIndex_ =
1180 GreatOrEqual(itemCenterPos, contentMainSize_ / 2) ? anchorIndex - 1 : anchorIndex;
1181 }
1182 } else {
1183 lastSnapTargetIndex_ = lastSnapTargetIndex_.value() - 1;
1184 }
1185 } else if (snapDirection == SnapDirection::BACKWARD) {
1186 if (!lastSnapTargetIndex_.has_value()) {
1187 if (align == ScrollAlign::START) {
1188 lastSnapTargetIndex_ = anchorIndex + 1;
1189 } else if (align == ScrollAlign::END) {
1190 auto isAligned = LessOrEqual(itemPosition_[anchorIndex].endPos, contentMainSize_ - contentEndOffset_);
1191 lastSnapTargetIndex_ = isAligned ? anchorIndex + 1 : anchorIndex;
1192 } else {
1193 auto item = itemPosition_[anchorIndex];
1194 auto itemCenterPos = (item.startPos + item.endPos) / 2;
1195 lastSnapTargetIndex_ = LessOrEqual(itemCenterPos, contentMainSize_ / 2) ? anchorIndex + 1 : anchorIndex;
1196 }
1197 } else {
1198 lastSnapTargetIndex_ = lastSnapTargetIndex_.value() + 1;
1199 }
1200 }
1201 lastSnapTargetIndex_ = std::max(lastSnapTargetIndex_.value(), 0);
1202 lastSnapTargetIndex_ = std::min(lastSnapTargetIndex_.value(), maxListItemIndex_);
1203 ScrollToIndex(lastSnapTargetIndex_.value(), true, align);
1204 return true;
1205 }
1206
GetEndIndexExcludeEndOffset()1207 int32_t ListPattern::GetEndIndexExcludeEndOffset()
1208 {
1209 auto endPos = contentMainSize_ - contentEndOffset_;
1210 auto iter = itemPosition_.rbegin();
1211 while (iter != itemPosition_.rend() && GreatOrEqual(iter->second.startPos, endPos)) {
1212 iter++;
1213 }
1214 return iter->first;
1215 }
1216
GetStartIndexExcludeStartOffset()1217 int32_t ListPattern::GetStartIndexExcludeStartOffset()
1218 {
1219 auto iter = itemPosition_.begin();
1220 while (iter != itemPosition_.end() && LessOrEqual(iter->second.endPos, contentStartOffset_)) {
1221 iter++;
1222 }
1223 return iter->first;
1224 }
1225
StartListSnapAnimation(float scrollSnapDelta,float scrollSnapVelocity)1226 void ListPattern::StartListSnapAnimation(float scrollSnapDelta, float scrollSnapVelocity)
1227 {
1228 CHECK_NULL_VOID(scrollable_);
1229 scrollable_->StartListSnapAnimation(scrollSnapDelta, scrollSnapVelocity, snapTrigByScrollBar_);
1230 }
1231
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)1232 void ListPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
1233 {
1234 scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
1235 auto list = weak.Upgrade();
1236 CHECK_NULL_RETURN(list, 0.0);
1237 return list->startMainPos_ - list->currentDelta_;
1238 });
1239 scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1240 auto list = weak.Upgrade();
1241 auto endPos = list->endMainPos_;
1242 auto startPos = list->startMainPos_;
1243 float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
1244 if (list->isStackFromEnd_) {
1245 return leading;
1246 }
1247 return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
1248 });
1249 scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
1250 auto list = weak.Upgrade();
1251 CHECK_NULL_RETURN(list, 0.0);
1252 auto startMainPos = list->startMainPos_;
1253 auto endMainPos = list->endMainPos_;
1254 auto contentMainSize = list->contentMainSize_ - list->contentEndOffset_ - list->contentStartOffset_;
1255 if (list->startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos) &&
1256 list->isStackFromEnd_) {
1257 return contentMainSize - endMainPos + startMainPos;
1258 }
1259 return list->contentStartOffset_;
1260 });
1261 scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1262 auto list = weak.Upgrade();
1263 auto endPos = list->endMainPos_;
1264 auto startPos = list->startMainPos_;
1265 float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
1266 if (list->isStackFromEnd_) {
1267 return leading;
1268 }
1269 return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
1270 });
1271 scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
1272 auto list = weak.Upgrade();
1273 CHECK_NULL_RETURN(list, 0.0);
1274 auto startMainPos = list->startMainPos_;
1275 auto endMainPos = list->endMainPos_;
1276 auto contentMainSize = list->contentMainSize_ - list->contentEndOffset_ - list->contentStartOffset_;
1277 if (list->startIndex_ == 0 && GreatNotEqual(contentMainSize, endMainPos - startMainPos) &&
1278 list->isStackFromEnd_) {
1279 return contentMainSize - endMainPos + startMainPos;
1280 }
1281 return list->contentStartOffset_;
1282 });
1283 }
1284
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)1285 void ListPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
1286 {
1287 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
1288 auto pattern = wp.Upgrade();
1289 CHECK_NULL_RETURN(pattern, false);
1290 return pattern->OnKeyEvent(event);
1291 };
1292 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
1293 }
1294
OnKeyEvent(const KeyEvent & event)1295 bool ListPattern::OnKeyEvent(const KeyEvent& event)
1296 {
1297 if (event.action != KeyAction::DOWN) {
1298 return false;
1299 }
1300 if (event.code == KeyCode::KEY_PAGE_DOWN) {
1301 ScrollPage(false);
1302 return true;
1303 }
1304 if (event.code == KeyCode::KEY_PAGE_UP) {
1305 ScrollPage(true);
1306 return true;
1307 }
1308 if (FocusHub::IsFocusStepKey(event.code)) {
1309 return ScrollToLastFocusIndex(event);
1310 }
1311 return HandleDirectionKey(event);
1312 }
1313
HandleDirectionKey(const KeyEvent & event)1314 bool ListPattern::HandleDirectionKey(const KeyEvent& event)
1315 {
1316 return false;
1317 }
1318
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)1319 WeakPtr<FocusHub> ListPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
1320 {
1321 auto curFocus = currentFocusNode.Upgrade();
1322 CHECK_NULL_RETURN(curFocus, nullptr);
1323 auto curFrame = curFocus->GetFrameNode();
1324 CHECK_NULL_RETURN(curFrame, nullptr);
1325 auto curPattern = curFrame->GetPattern();
1326 CHECK_NULL_RETURN(curPattern, nullptr);
1327 auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
1328 auto curIndex = -1;
1329 auto curIndexInGroup = -1;
1330 if (!curItemPattern) {
1331 auto parentNode = curFrame->GetParentFrameNode();
1332 CHECK_NULL_RETURN(parentNode, nullptr);
1333 auto parentPattern = AceType::DynamicCast<ListItemGroupPattern>(parentNode->GetPattern());
1334 CHECK_NULL_RETURN(parentPattern, nullptr);
1335 if (parentPattern->GetHeader() == curFrame) {
1336 curIndex = parentPattern->GetIndexInList();
1337 curIndexInGroup = -1;
1338 } else if (parentPattern->GetFooter() == curFrame) {
1339 curIndex = parentPattern->GetIndexInList();
1340 curIndexInGroup = parentPattern->GetTotalItemCount();
1341 } else {
1342 return nullptr;
1343 }
1344 } else {
1345 curIndex = curItemPattern->GetIndexInList();
1346 curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
1347 }
1348
1349 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
1350 CHECK_NULL_RETURN(listProperty, nullptr);
1351 auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
1352 auto curListItemGroupPara = GetListItemGroupParameter(curFrame);
1353 if (curIndex < 0 || curIndex > maxListItemIndex_) {
1354 return nullptr;
1355 }
1356
1357 auto moveStep = 0;
1358 auto nextIndex = curIndex;
1359 auto nextIndexInGroup = curIndexInGroup;
1360 if (lanes_ <= 1) {
1361 if ((isVertical && step == FocusStep::UP_END) || (!isVertical && step == FocusStep::LEFT_END)) {
1362 moveStep = 1;
1363 nextIndex = 0;
1364 nextIndexInGroup = -1;
1365 } else if ((isVertical && step == FocusStep::DOWN_END) || (!isVertical && step == FocusStep::RIGHT_END)) {
1366 moveStep = -1;
1367 nextIndex = maxListItemIndex_;
1368 nextIndexInGroup = -1;
1369 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT) ||
1370 (step == FocusStep::TAB)) {
1371 moveStep = 1;
1372 if ((curIndexInGroup == -1 && !curListItemGroupPara.hasHeader) ||
1373 (curIndexInGroup == curListItemGroupPara.itemEndIndex && !curListItemGroupPara.hasFooter) ||
1374 (curIndexInGroup > curListItemGroupPara.itemEndIndex)) {
1375 nextIndex = curIndex + moveStep;
1376 nextIndexInGroup = -1;
1377 } else {
1378 nextIndexInGroup = curIndexInGroup + moveStep;
1379 }
1380 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT) ||
1381 (step == FocusStep::SHIFT_TAB)) {
1382 moveStep = -1;
1383 if (curIndexInGroup == 0 && curListItemGroupPara.hasHeader) {
1384 nextIndexInGroup = -1;
1385 } else if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1386 nextIndex = curIndex + moveStep;
1387 nextIndexInGroup = -1;
1388 } else {
1389 nextIndexInGroup = curIndexInGroup + moveStep;
1390 }
1391 }
1392 } else {
1393 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
1394 moveStep = 1;
1395 nextIndex = 0;
1396 nextIndexInGroup = -1;
1397 } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
1398 moveStep = -1;
1399 nextIndex = maxListItemIndex_;
1400 nextIndexInGroup = -1;
1401 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
1402 if (curIndexInGroup == -1) {
1403 moveStep = lanes_;
1404 nextIndex = GetNextLineFocusIndex(curIndex);
1405 nextIndexInGroup = -1;
1406 } else {
1407 moveStep = curListItemGroupPara.lanes;
1408 nextIndexInGroup = curIndexInGroup + moveStep;
1409 VerifyFocusIndex(nextIndex, nextIndexInGroup, curListItemGroupPara);
1410 }
1411 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
1412 if (curIndexInGroup == -1) {
1413 moveStep = -lanes_;
1414 nextIndex = curIndex + moveStep;
1415 nextIndexInGroup = -1;
1416 } else {
1417 moveStep = -curListItemGroupPara.lanes;
1418 nextIndexInGroup = curIndexInGroup + moveStep;
1419 VerifyFocusIndex(nextIndex, nextIndexInGroup, curListItemGroupPara);
1420 }
1421 } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1422 moveStep = 1;
1423 if (((curIndexInGroup == -1) && ((curIndex - (lanes_ - 1)) % lanes_ != 0)) ||
1424 ((curIndexInGroup != -1) &&
1425 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes == 0))) {
1426 nextIndex = curIndex + moveStep;
1427 nextIndexInGroup = -1;
1428 } else if ((curIndexInGroup != -1) &&
1429 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes != 0)) {
1430 nextIndexInGroup = curIndexInGroup + moveStep;
1431 }
1432 } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1433 moveStep = -1;
1434 if (((curIndexInGroup == -1) && (curIndex % lanes_ != 0)) ||
1435 ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes == 0))) {
1436 nextIndex = curIndex + moveStep;
1437 nextIndexInGroup = -1;
1438 } else if ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes != 0)) {
1439 nextIndexInGroup = curIndexInGroup + moveStep;
1440 }
1441 } else if (step == FocusStep::TAB) {
1442 moveStep = 1;
1443 if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1444 nextIndex = curIndex + moveStep;
1445 nextIndexInGroup = -1;
1446 } else {
1447 nextIndexInGroup = curIndexInGroup + moveStep;
1448 }
1449 } else if (step == FocusStep::SHIFT_TAB) {
1450 moveStep = -1;
1451 if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1452 nextIndex = curIndex + moveStep;
1453 nextIndexInGroup = -1;
1454 } else {
1455 nextIndexInGroup = curIndexInGroup + moveStep;
1456 }
1457 }
1458 }
1459 while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
1460 if ((nextIndex == curIndex) && (curIndexInGroup == nextIndexInGroup)) {
1461 return nullptr;
1462 }
1463 auto nextFocusNode =
1464 ScrollAndFindFocusNode(nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep, step);
1465 if (nextFocusNode.Upgrade()) {
1466 return nextFocusNode;
1467 }
1468 if (nextIndexInGroup > -1) {
1469 nextIndexInGroup += moveStep;
1470 } else {
1471 nextIndex += moveStep;
1472 }
1473 }
1474 return nullptr;
1475 }
1476
VerifyFocusIndex(int32_t & nextIndex,int32_t & nextIndexInGroup,const ListItemGroupPara & param)1477 void ListPattern::VerifyFocusIndex(int32_t& nextIndex, int32_t& nextIndexInGroup, const ListItemGroupPara& param)
1478 {
1479 if (nextIndexInGroup < 0) {
1480 nextIndex--;
1481 nextIndexInGroup = -1;
1482 } else if (nextIndexInGroup > param.itemEndIndex) {
1483 if (param.lanes > 1 &&
1484 nextIndexInGroup <= param.itemEndIndex + param.lanes - param.itemEndIndex % param.lanes - 1) {
1485 nextIndexInGroup = param.itemEndIndex;
1486 } else {
1487 nextIndex++;
1488 nextIndexInGroup = -1;
1489 }
1490 }
1491 }
1492
GetNextLineFocusIndex(int32_t currIndex)1493 int32_t ListPattern::GetNextLineFocusIndex(int32_t currIndex)
1494 {
1495 int32_t nextIndex = currIndex + lanes_;
1496 if (nextIndex <= maxListItemIndex_) {
1497 return nextIndex;
1498 }
1499 auto it1 = itemPosition_.find(currIndex);
1500 if (it1 == itemPosition_.end()) {
1501 return nextIndex;
1502 }
1503 auto it2 = itemPosition_.find(maxListItemIndex_);
1504 if (it2 == itemPosition_.end()) {
1505 return maxListItemIndex_;
1506 }
1507 if (NearEqual(it1->second.startPos, it2->second.startPos)) {
1508 return nextIndex;
1509 }
1510 return maxListItemIndex_;
1511 }
1512
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarGroupIndex)1513 WeakPtr<FocusHub> ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarGroupIndex)
1514 {
1515 auto listFrame = GetHost();
1516 CHECK_NULL_RETURN(listFrame, nullptr);
1517 auto listFocus = listFrame->GetFocusHub();
1518 CHECK_NULL_RETURN(listFocus, nullptr);
1519 WeakPtr<FocusHub> target;
1520 listFocus->AnyChildFocusHub([&target, tarMainIndex, tarGroupIndex](const RefPtr<FocusHub>& childFocus) {
1521 if (!childFocus->IsFocusable()) {
1522 return false;
1523 }
1524 auto childFrame = childFocus->GetFrameNode();
1525 if (!childFrame) {
1526 return false;
1527 }
1528 auto childPattern = childFrame->GetPattern();
1529 if (!childPattern) {
1530 return false;
1531 }
1532 auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
1533 if (!childItemPattern) {
1534 auto parentNode = childFrame->GetParentFrameNode();
1535 CHECK_NULL_RETURN(parentNode, false);
1536 auto parentPattern = AceType::DynamicCast<ListItemGroupPattern>(parentNode->GetPattern());
1537 CHECK_NULL_RETURN(parentPattern, false);
1538 if (parentPattern->GetIndexInList() == tarMainIndex) {
1539 if ((parentPattern->GetHeader() == childFrame && tarGroupIndex == -1) ||
1540 (parentPattern->GetFooter() == childFrame && tarGroupIndex == parentPattern->GetTotalItemCount())) {
1541 target = childFocus;
1542 return true;
1543 }
1544 }
1545 return false;
1546 }
1547 auto curIndex = childItemPattern->GetIndexInList();
1548 auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
1549 if (curIndex == tarMainIndex && curIndexInGroup == tarGroupIndex) {
1550 target = childFocus;
1551 return true;
1552 }
1553 return false;
1554 });
1555 return target;
1556 }
1557
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1558 bool ListPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1559 {
1560 CHECK_NULL_RETURN(focusFrameNode, false);
1561 auto focusPattern = focusFrameNode->GetPattern<ListItemPattern>();
1562 CHECK_NULL_RETURN(focusPattern, false);
1563 auto curIndex = focusPattern->GetIndexInList();
1564 ScrollToIndex(curIndex, smooth_, GetScrollToNodeAlign());
1565 auto pipeline = GetContext();
1566 if (pipeline) {
1567 pipeline->FlushUITasks();
1568 }
1569 return true;
1570 }
1571
GetScrollOffsetAbility()1572 ScrollOffsetAbility ListPattern::GetScrollOffsetAbility()
1573 {
1574 return {
1575 [wp = WeakClaim(this)](float moveOffset) -> bool {
1576 auto pattern = wp.Upgrade();
1577 CHECK_NULL_RETURN(pattern, false);
1578 pattern->ScrollBy(-moveOffset);
1579 return true;
1580 },
1581 GetAxis(),
1582 IsScrollSnapAlignCenter() ? 0 : contentStartOffset_,
1583 IsScrollSnapAlignCenter() ? 0 : contentEndOffset_,
1584 };
1585 }
1586
GetScrollIndexAbility()1587 std::function<bool(int32_t)> ListPattern::GetScrollIndexAbility()
1588 {
1589 return [wp = WeakClaim(this)](int32_t index) -> bool {
1590 auto pattern = wp.Upgrade();
1591 CHECK_NULL_RETURN(pattern, false);
1592 if (index == FocusHub::SCROLL_TO_HEAD) {
1593 // When the focus framework calls to find Head and Tail, it should reset. Otherwise, due to scrolling, the
1594 // newly acquired focus will immediately lose focus and set depend to SELF.
1595 pattern->ResetFocusIndex();
1596 pattern->ResetGroupFocusIndex();
1597 pattern->ScrollToIndex(0, false, ScrollAlign::START);
1598 } else if (index == FocusHub::SCROLL_TO_TAIL) {
1599 pattern->ResetFocusIndex();
1600 pattern->ResetGroupFocusIndex();
1601 pattern->ScrollToIndex(ListLayoutAlgorithm::LAST_ITEM, false, ScrollAlign::END);
1602 } else {
1603 pattern->ScrollToIndex(index, false, ScrollAlign::AUTO);
1604 }
1605 return true;
1606 };
1607 }
1608
ScrollAndFindFocusNode(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step)1609 WeakPtr<FocusHub> ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1610 int32_t curIndexInGroup, int32_t moveStep, FocusStep step)
1611 {
1612 bool isScrollIndex = ScrollListForFocus(nextIndex, curIndex, nextIndexInGroup);
1613 bool needFindNextFocusNode = ScrollListItemGroupForFocus(
1614 nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep, step, isScrollIndex);
1615
1616 return needFindNextFocusNode ? GetChildFocusNodeByIndex(nextIndex, nextIndexInGroup) : nullptr;
1617 }
1618
ScrollListForFocus(int32_t nextIndex,int32_t curIndex,int32_t nextIndexInGroup)1619 bool ListPattern::ScrollListForFocus(int32_t nextIndex, int32_t curIndex, int32_t nextIndexInGroup)
1620 {
1621 auto isScrollIndex = false;
1622 auto pipeline = GetContext();
1623 CHECK_NULL_RETURN(pipeline, isScrollIndex);
1624 if (nextIndex < startIndex_ && nextIndexInGroup == -1) {
1625 isScrollIndex = true;
1626 ScrollToIndex(nextIndex, false, ScrollAlign::START);
1627 pipeline->FlushUITasks();
1628 } else if (nextIndex > endIndex_ && nextIndexInGroup == -1) {
1629 isScrollIndex = true;
1630 ScrollToIndex(nextIndex, false, ScrollAlign::END);
1631 pipeline->FlushUITasks();
1632 } else if (nextIndexInGroup == -1 && HandleDisplayedChildFocus(nextIndex, curIndex)) {
1633 isScrollIndex = true;
1634 pipeline->FlushUITasks();
1635 }
1636 return isScrollIndex;
1637 }
1638
HandleDisplayedChildFocus(int32_t nextIndex,int32_t curIndex)1639 bool ListPattern::HandleDisplayedChildFocus(int32_t nextIndex, int32_t curIndex)
1640 {
1641 auto iter = itemPosition_.find(nextIndex);
1642 if (iter == itemPosition_.end()) {
1643 return false;
1644 }
1645 float targetPos = 0.0f;
1646 float startPos = iter->second.startPos;
1647 float endPos = iter->second.endPos;
1648 ScrollAlign align = ScrollAlign::AUTO;
1649 if (iter->second.isGroup) {
1650 auto host = GetHost();
1651 CHECK_NULL_RETURN(host, false);
1652 auto itemGroupWrapper = host->GetChildByIndex(nextIndex);
1653 CHECK_NULL_RETURN(itemGroupWrapper, false);
1654 auto itemGroup = itemGroupWrapper->GetHostNode();
1655 CHECK_NULL_RETURN(itemGroup, false);
1656 auto groupPattern = itemGroup->GetPattern<ListItemGroupPattern>();
1657 CHECK_NULL_RETURN(groupPattern, false);
1658 int32_t indexInGroup = nextIndex < curIndex ? groupPattern->GetEndIndexInGroup() : 0;
1659 if (!GetListItemGroupAnimatePosWithIndexInGroup(nextIndex, indexInGroup, startPos, align, targetPos)) {
1660 targetPos = startPos;
1661 }
1662 } else {
1663 GetListItemAnimatePos(startPos, endPos, align, targetPos);
1664 }
1665 if (Positive(targetPos)) {
1666 ScrollToIndex(nextIndex, false, ScrollAlign::END);
1667 return iter->second.isGroup;
1668 } else if (Negative(targetPos)) {
1669 ScrollToIndex(nextIndex, false, ScrollAlign::START);
1670 return iter->second.isGroup;
1671 }
1672 return false;
1673 }
1674
CalcAlignForFocusToGroupItem(int32_t moveStep,FocusStep step) const1675 ScrollAlign ListPattern::CalcAlignForFocusToGroupItem(int32_t moveStep, FocusStep step) const
1676 {
1677 // Extract method only for ScrollListItemGroupForFocus.
1678 ScrollAlign scrollAlign = ScrollAlign::END;
1679 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END) || (step == FocusStep::DOWN_END) ||
1680 (step == FocusStep::RIGHT_END)) {
1681 scrollAlign = moveStep < 0 ? ScrollAlign::END : ScrollAlign::START;
1682 } else {
1683 scrollAlign = moveStep < 0 ? ScrollAlign::START : ScrollAlign::END;
1684 }
1685 return scrollAlign;
1686 }
1687
CalcNextIndexInGroup(int32_t nextIndex,int32_t curIndex,int32_t curIndexInGroup,int32_t moveStep,ListItemGroupPara & nextListItemGroupPara) const1688 int32_t ListPattern::CalcNextIndexInGroup(int32_t nextIndex, int32_t curIndex, int32_t curIndexInGroup,
1689 int32_t moveStep, ListItemGroupPara& nextListItemGroupPara) const
1690 {
1691 // Extract method only for ScrollListItemGroupForFocus.
1692 int32_t nextIndexInGroup = -1;
1693 if (nextIndex != curIndex) {
1694 nextIndexInGroup = moveStep < 0 ? nextListItemGroupPara.itemEndIndex : 0;
1695 }
1696 if (moveStep == -1) {
1697 if (curIndexInGroup == -1 && nextListItemGroupPara.hasFooter) {
1698 nextIndexInGroup = nextListItemGroupPara.itemEndIndex + 1;
1699 }
1700 } else if (moveStep == 1) {
1701 if (!nextListItemGroupPara.hasHeader) {
1702 nextIndexInGroup = 0;
1703 } else {
1704 nextIndexInGroup = -1;
1705 }
1706 }
1707 return nextIndexInGroup;
1708 }
1709
ScrollListItemGroupForFocus(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step,bool isScrollIndex)1710 bool ListPattern::ScrollListItemGroupForFocus(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1711 int32_t curIndexInGroup, int32_t moveStep, FocusStep step, bool isScrollIndex)
1712 {
1713 auto pipeline = GetContext();
1714 CHECK_NULL_RETURN(pipeline, true);
1715 RefPtr<FrameNode> nextIndexNode;
1716 auto isNextInGroup = IsListItemGroup(nextIndex, nextIndexNode);
1717 if (!isNextInGroup || !nextIndexNode) {
1718 nextIndexInGroup = -1;
1719 return true;
1720 }
1721 auto nextListItemGroupPara = GetListItemGroupParameter(nextIndexNode);
1722 if (nextIndexInGroup == -1) {
1723 nextIndexInGroup = CalcNextIndexInGroup(nextIndex, curIndex, curIndexInGroup, moveStep, nextListItemGroupPara);
1724 if ((nextIndexInGroup < nextListItemGroupPara.displayStartIndex) ||
1725 (nextIndexInGroup > nextListItemGroupPara.displayEndIndex) || (isScrollIndex)) {
1726 ScrollAlign align = CalcAlignForFocusToGroupItem(moveStep, step);
1727 ScrollToItemInGroup(nextIndex, nextIndexInGroup, false, align);
1728 pipeline->FlushUITasks();
1729 }
1730 } else if (nextIndexInGroup > nextListItemGroupPara.itemEndIndex) {
1731 if (nextListItemGroupPara.hasFooter) {
1732 ScrollToIndex(nextIndex, false, ScrollAlign::END);
1733 pipeline->FlushUITasks();
1734 } else {
1735 nextIndexInGroup = -1;
1736 return false;
1737 }
1738 } else if ((nextIndexInGroup < curIndexInGroup) && (nextIndexInGroup < nextListItemGroupPara.displayStartIndex)) {
1739 ScrollToItemInGroup(nextIndex, nextIndexInGroup, false, ScrollAlign::START);
1740 pipeline->FlushUITasks();
1741 } else if ((nextIndexInGroup > curIndexInGroup) && (nextIndexInGroup > nextListItemGroupPara.displayEndIndex)) {
1742 ScrollToItemInGroup(nextIndex, nextIndexInGroup, false, ScrollAlign::END);
1743 pipeline->FlushUITasks();
1744 }
1745 return true;
1746 }
1747
OnAnimateStop()1748 void ListPattern::OnAnimateStop()
1749 {
1750 if (!GetIsDragging() || GetScrollAbort()) {
1751 scrollStop_ = true;
1752 MarkDirtyNodeSelf();
1753 isScrollEnd_ = true;
1754 }
1755 scrollTarget_.reset();
1756 }
1757
ScrollTo(float position)1758 void ListPattern::ScrollTo(float position)
1759 {
1760 StopAnimate();
1761 jumpIndex_.reset();
1762 targetIndex_.reset();
1763 currentDelta_ = 0.0f;
1764 SetAnimateCanOverScroll(GetCanStayOverScroll());
1765 UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1766 SetIsOverScroll(GetCanStayOverScroll());
1767 MarkDirtyNodeSelf();
1768 isScrollEnd_ = true;
1769 }
1770
ResetScrollToIndexParams()1771 void ListPattern::ResetScrollToIndexParams()
1772 {
1773 targetIndex_.reset();
1774 targetIndexInGroup_.reset();
1775 scrollAlign_ = ScrollAlign::START;
1776 }
1777
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align,std::optional<float> extraOffset)1778 void ListPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align, std::optional<float> extraOffset)
1779 {
1780 SetScrollSource(SCROLL_FROM_JUMP);
1781 // When snap align scrolling with the mouse wheel, do not interrupt the animation.
1782 if (!smooth && !lastSnapTargetIndex_.has_value()) {
1783 ResetScrollToIndexParams();
1784 StopAnimate();
1785 }
1786 if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1787 currentDelta_ = 0.0f;
1788 smooth_ = smooth;
1789 if (smooth_) {
1790 SetExtraOffset(extraOffset);
1791 if (!AnimateToTarget(index, std::nullopt, align)) {
1792 targetIndex_ = index;
1793 scrollAlign_ = align;
1794 }
1795 } else {
1796 if (extraOffset.has_value()) {
1797 currentDelta_ = extraOffset.value();
1798 }
1799 jumpIndex_ = index;
1800 scrollAlign_ = align;
1801 jumpIndexInGroup_.reset();
1802 }
1803 MarkDirtyNodeSelf();
1804 }
1805 isScrollEnd_ = true;
1806 FireAndCleanScrollingListener();
1807 }
1808
CheckTargetValid(int32_t index,int32_t indexInGroup)1809 bool ListPattern::CheckTargetValid(int32_t index, int32_t indexInGroup)
1810 {
1811 auto host = GetHost();
1812 auto totalItemCount = host->GetTotalChildCount();
1813 if ((index < 0) || (index >= totalItemCount)) {
1814 return false;
1815 }
1816 auto groupWrapper = host->GetOrCreateChildByIndex(index);
1817 CHECK_NULL_RETURN(groupWrapper, false);
1818 if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
1819 return false;
1820 }
1821 auto groupNode = groupWrapper->GetHostNode();
1822 CHECK_NULL_RETURN(groupNode, false);
1823 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1824 CHECK_NULL_RETURN(groupPattern, false);
1825 auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
1826 if ((indexInGroup < 0) || (indexInGroup >= groupItemCount)) {
1827 return false;
1828 }
1829 return true;
1830 }
1831
ScrollToItemInGroup(int32_t index,int32_t indexInGroup,bool smooth,ScrollAlign align)1832 void ListPattern::ScrollToItemInGroup(int32_t index, int32_t indexInGroup, bool smooth, ScrollAlign align)
1833 {
1834 SetScrollSource(SCROLL_FROM_JUMP);
1835 if (!smooth) {
1836 StopAnimate();
1837 targetIndex_.reset();
1838 targetIndexInGroup_.reset();
1839 }
1840 if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1841 currentDelta_ = 0.0f;
1842 smooth_ = smooth;
1843 if (smooth_) {
1844 if (!AnimateToTarget(index, indexInGroup, align) && CheckTargetValid(index, indexInGroup)) {
1845 targetIndex_ = index;
1846 currentDelta_ = 0;
1847 targetIndexInGroup_ = indexInGroup;
1848 scrollAlign_ = align;
1849 }
1850 } else {
1851 jumpIndex_ = index;
1852 jumpIndexInGroup_ = indexInGroup;
1853 scrollAlign_ = align;
1854 if (align == ScrollAlign::AUTO && !isInitialized_) {
1855 scrollAlign_ = ScrollAlign::END;
1856 }
1857 }
1858 MarkDirtyNodeSelf();
1859 }
1860 isScrollEnd_ = true;
1861 FireAndCleanScrollingListener();
1862 }
1863
GetListItemAnimatePos(float startPos,float endPos,ScrollAlign align,float & targetPos)1864 bool ListPattern::GetListItemAnimatePos(float startPos, float endPos, ScrollAlign align, float& targetPos)
1865 {
1866 switch (align) {
1867 case ScrollAlign::START:
1868 case ScrollAlign::NONE:
1869 targetPos = startPos;
1870 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1871 targetPos -= contentStartOffset_;
1872 }
1873 break;
1874 case ScrollAlign::CENTER:
1875 targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1876 break;
1877 case ScrollAlign::END:
1878 targetPos = endPos - contentMainSize_;
1879 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1880 targetPos += contentEndOffset_;
1881 }
1882 break;
1883 case ScrollAlign::AUTO:
1884 targetPos = CalculateTargetPos(startPos, endPos);
1885 break;
1886 }
1887 return true;
1888 }
1889
GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index,float startPos,float endPos,ScrollAlign align,float & targetPos)1890 bool ListPattern::GetListItemGroupAnimatePosWithoutIndexInGroup(
1891 int32_t index, float startPos, float endPos, ScrollAlign align, float& targetPos)
1892 {
1893 auto host = GetHost();
1894 CHECK_NULL_RETURN(host, false);
1895 auto groupWrapper = host->GetChildByIndex(index);
1896 CHECK_NULL_RETURN(groupWrapper, false);
1897 auto groupNode = groupWrapper->GetHostNode();
1898 CHECK_NULL_RETURN(groupNode, false);
1899 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1900 CHECK_NULL_RETURN(groupPattern, false);
1901 auto groupLayoutProperty = groupNode->GetLayoutProperty<ListItemGroupLayoutProperty>();
1902 CHECK_NULL_RETURN(groupLayoutProperty, false);
1903 auto visible = groupLayoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
1904
1905 switch (align) {
1906 case ScrollAlign::START:
1907 case ScrollAlign::NONE:
1908 if (visible != VisibleType::GONE && !groupPattern->IsDisplayStart()) {
1909 return false;
1910 }
1911 targetPos = startPos;
1912 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1913 targetPos -= contentStartOffset_;
1914 }
1915 break;
1916 case ScrollAlign::CENTER:
1917 if (visible != VisibleType::GONE && (!groupPattern->IsDisplayStart() || !groupPattern->IsDisplayEnd())) {
1918 return false;
1919 }
1920 targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1921 break;
1922 case ScrollAlign::END:
1923 if (visible != VisibleType::GONE && !groupPattern->IsDisplayEnd()) {
1924 return false;
1925 }
1926 targetPos = endPos - contentMainSize_;
1927 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1928 targetPos += contentEndOffset_;
1929 }
1930 break;
1931 case ScrollAlign::AUTO:
1932 if (targetIndex_.has_value()) {
1933 targetPos = CalculateTargetPos(startPos, endPos);
1934 return true;
1935 }
1936 return false;
1937 }
1938
1939 return true;
1940 }
1941
GetListItemGroupAnimatePosWithIndexInGroup(int32_t index,int32_t indexInGroup,float startPos,ScrollAlign align,float & targetPos)1942 bool ListPattern::GetListItemGroupAnimatePosWithIndexInGroup(
1943 int32_t index, int32_t indexInGroup, float startPos, ScrollAlign align, float& targetPos)
1944 {
1945 auto host = GetHost();
1946 CHECK_NULL_RETURN(host, false);
1947 auto groupWrapper = host->GetChildByIndex(index);
1948 CHECK_NULL_RETURN(groupWrapper, false);
1949 auto groupNode = groupWrapper->GetHostNode();
1950 CHECK_NULL_RETURN(groupNode, false);
1951 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1952 CHECK_NULL_RETURN(groupPattern, false);
1953 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
1954 CHECK_NULL_RETURN(listLayoutProperty, false);
1955 auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1956 auto itemsPosInGroup = groupPattern->GetItemPosition();
1957 auto it = itemsPosInGroup.find(indexInGroup);
1958 if (it == itemsPosInGroup.end()) {
1959 return false;
1960 }
1961 auto axis = GetAxis();
1962 std::optional<float> padding;
1963 std::optional<float> margin;
1964 if (axis == Axis::HORIZONTAL) {
1965 padding = IsReverse() ? groupWrapper->GetGeometryNode()->GetPadding()->right
1966 : groupWrapper->GetGeometryNode()->GetPadding()->left;
1967 margin = IsReverse() ? groupWrapper->GetGeometryNode()->GetMargin()->right
1968 : groupWrapper->GetGeometryNode()->GetMargin()->left;
1969 } else {
1970 padding = groupWrapper->GetGeometryNode()->GetPadding()->top;
1971 margin = groupWrapper->GetGeometryNode()->GetMargin()->top;
1972 }
1973 auto marginValue = margin.value_or(0.f);
1974 auto paddingValue = padding.value_or(0.f);
1975 if (align == ScrollAlign::CENTER) {
1976 targetPos = paddingValue + marginValue + startPos + (it->second.startPos + it->second.endPos) / 2.0f -
1977 contentMainSize_ / 2.0f;
1978 } else {
1979 float itemStartPos = paddingValue + marginValue + startPos + it->second.startPos;
1980 float itemEndPos = paddingValue + marginValue + startPos + it->second.endPos;
1981 if (stickyStyle == V2::StickyStyle::HEADER || stickyStyle == V2::StickyStyle::BOTH) {
1982 itemStartPos -= groupPattern->GetHeaderMainSize();
1983 }
1984 if (stickyStyle == V2::StickyStyle::FOOTER || stickyStyle == V2::StickyStyle::BOTH) {
1985 itemEndPos += groupPattern->GetFooterMainSize();
1986 }
1987 if (align == ScrollAlign::AUTO) {
1988 targetPos = CalculateTargetPos(itemStartPos, itemEndPos);
1989 } else {
1990 if (!IsScrollSnapAlignCenter() || childrenSize_) {
1991 itemStartPos -= contentStartOffset_;
1992 itemEndPos += contentEndOffset_;
1993 }
1994 targetPos = align == ScrollAlign::END ? itemEndPos - contentMainSize_ : itemStartPos;
1995 }
1996 }
1997 return true;
1998 }
1999
AnimateToTarget(int32_t index,std::optional<int32_t> indexInGroup,ScrollAlign align)2000 bool ListPattern::AnimateToTarget(int32_t index, std::optional<int32_t> indexInGroup, ScrollAlign align)
2001 {
2002 auto iter = itemPosition_.find(index);
2003 if (iter == itemPosition_.end()) {
2004 return false;
2005 }
2006 float targetPos = 0.0f;
2007 if (iter->second.isGroup) {
2008 if (indexInGroup.has_value()) {
2009 if (!GetListItemGroupAnimatePosWithIndexInGroup(index, indexInGroup.value(), iter->second.startPos,
2010 align, targetPos)) {
2011 return false;
2012 }
2013 } else {
2014 if (!GetListItemGroupAnimatePosWithoutIndexInGroup(index, iter->second.startPos, iter->second.endPos,
2015 align, targetPos)) {
2016 return false;
2017 }
2018 }
2019 } else {
2020 if (indexInGroup.has_value()) {
2021 return false;
2022 }
2023 GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
2024 }
2025 float extraOffset = 0.0f;
2026 if (GetExtraOffset().has_value()) {
2027 extraOffset = GetExtraOffset().value();
2028 targetPos += extraOffset;
2029 ResetExtraOffset();
2030 }
2031 if (lastSnapTargetIndex_.has_value()) {
2032 if ((Positive(targetPos) && IsAtBottom()) || (Negative(targetPos) && IsAtTop())) {
2033 ResetLastSnapTargetIndex();
2034 return true;
2035 }
2036 }
2037 if (!NearZero(targetPos)) {
2038 AnimateTo(targetPos + currentOffset_, -1, nullptr, true, false, false);
2039 if (predictSnapOffset_.has_value() && AnimateRunning()) {
2040 scrollSnapVelocity_ = 0.0f;
2041 predictSnapOffset_.reset();
2042 snapTrigOnScrollStart_ = false;
2043 }
2044 if (!indexInGroup.has_value()) {
2045 scrollTarget_ = { index, extraOffset, align, targetPos + currentOffset_ };
2046 }
2047 } else {
2048 ResetScrollToIndexParams();
2049 StopAnimate();
2050 }
2051 return true;
2052 }
2053
ScrollPage(bool reverse,bool smooth,AccessibilityScrollType scrollType)2054 void ListPattern::ScrollPage(bool reverse, bool smooth, AccessibilityScrollType scrollType)
2055 {
2056 float distance = reverse ? contentMainSize_ : -contentMainSize_;
2057 if (scrollType == AccessibilityScrollType::SCROLL_HALF) {
2058 distance = distance / 2.f;
2059 }
2060 if (smooth) {
2061 float position = -GetTotalOffset() + distance;
2062 AnimateTo(-position, -1, nullptr, true, false, false);
2063 } else {
2064 StopAnimate();
2065 UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
2066 isScrollEnd_ = true;
2067 }
2068 }
2069
ScrollBy(float offset)2070 void ListPattern::ScrollBy(float offset)
2071 {
2072 StopAnimate();
2073 SetIsOverScroll(false);
2074 UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
2075 isScrollEnd_ = true;
2076 }
2077
GetCurrentOffset() const2078 Offset ListPattern::GetCurrentOffset() const
2079 {
2080 if (GetAxis() == Axis::HORIZONTAL) {
2081 return { GetTotalOffset(), 0.0 };
2082 }
2083 return { 0.0, GetTotalOffset() };
2084 }
2085
HandleScrollBarOutBoundary()2086 void ListPattern::HandleScrollBarOutBoundary()
2087 {
2088 if (itemPosition_.empty()) {
2089 return;
2090 }
2091 if (!GetScrollBar() && !GetScrollBarProxy()) {
2092 return;
2093 }
2094 if (!isScrollable_) {
2095 ScrollablePattern::HandleScrollBarOutBoundary(0);
2096 return;
2097 }
2098 auto res = GetOutBoundaryOffset(0.0f);
2099 float overScroll = std::max(res.start, res.end);
2100 ScrollablePattern::HandleScrollBarOutBoundary(overScroll);
2101 }
2102
GetItemRect(int32_t index) const2103 Rect ListPattern::GetItemRect(int32_t index) const
2104 {
2105 if (index < 0 || index < startIndex_ || index > endIndex_) {
2106 return Rect();
2107 }
2108 auto host = GetHost();
2109 CHECK_NULL_RETURN(host, Rect());
2110 auto item = host->GetChildByIndex(index + itemStartIndex_);
2111 CHECK_NULL_RETURN(item, Rect());
2112 auto itemGeometry = item->GetGeometryNode();
2113 CHECK_NULL_RETURN(itemGeometry, Rect());
2114 return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
2115 itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
2116 }
2117
GetItemIndex(double x,double y) const2118 int32_t ListPattern::GetItemIndex(double x, double y) const
2119 {
2120 for (int32_t index = startIndex_; index <= endIndex_; ++index) {
2121 Rect rect = GetItemRect(index);
2122 if (rect.IsInRegion({x, y})) {
2123 return index;
2124 }
2125 }
2126 return -1;
2127 }
2128
GetItemIndexInGroup(double x,double y) const2129 ListItemIndex ListPattern::GetItemIndexInGroup(double x, double y) const
2130 {
2131 ListItemIndex itemIndex = { -1, -1, -1 };
2132
2133 auto host = GetHost();
2134 CHECK_NULL_RETURN(host, itemIndex);
2135 for (int32_t index = startIndex_; index <= endIndex_; ++index) {
2136 auto item = host->GetChildByIndex(index);
2137 if (!AceType::InstanceOf<FrameNode>(item)) {
2138 continue;
2139 }
2140 auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
2141 auto groupItemPattern = itemFrameNode->GetPattern<ListItemGroupPattern>();
2142 if (groupItemPattern) {
2143 if (GetGroupItemIndex(x, y, itemFrameNode, index, itemIndex)) {
2144 return itemIndex;
2145 }
2146 } else {
2147 Rect rect = GetItemRect(index);
2148 if (rect.IsInRegion({x, y})) {
2149 itemIndex.index = index;
2150 return itemIndex;
2151 }
2152 }
2153 }
2154 return itemIndex;
2155 }
2156
GetGroupItemIndex(double x,double y,RefPtr<FrameNode> itemFrameNode,int32_t & index,ListItemIndex & itemIndex) const2157 bool ListPattern::GetGroupItemIndex(double x, double y, RefPtr<FrameNode> itemFrameNode,
2158 int32_t& index, ListItemIndex& itemIndex) const
2159 {
2160 auto groupItemPattern = itemFrameNode->GetPattern<ListItemGroupPattern>();
2161 Rect rect = GetItemRect(index);
2162 if (groupItemPattern && rect.IsInRegion({x, y})) {
2163 itemIndex.index = index;
2164 for (int32_t groupIndex = groupItemPattern->GetDisplayStartIndexInGroup();
2165 groupIndex <= groupItemPattern->GetDisplayEndIndexInGroup(); ++groupIndex) {
2166 Rect groupRect = GetItemRectInGroup(index, groupIndex);
2167 if (groupRect.IsInRegion({x, y})) {
2168 itemIndex.index = index;
2169 itemIndex.area = 1; // item area
2170 itemIndex.indexInGroup = groupIndex;
2171 return true;
2172 }
2173 }
2174
2175 int32_t areaValue = 0;
2176 if (GetAxis() == Axis::VERTICAL) {
2177 areaValue = ProcessAreaVertical(x, y, rect, index, groupItemPattern);
2178 } else {
2179 areaValue = ProcessAreaHorizontal(x, y, rect, index, groupItemPattern);
2180 }
2181 if (areaValue != -1) {
2182 itemIndex.index = index;
2183 itemIndex.area = areaValue;
2184 itemIndex.indexInGroup = -1;
2185 return true;
2186 }
2187 }
2188
2189 return false;
2190 }
2191
ProcessAreaVertical(double & x,double & y,Rect & groupRect,int32_t & index,RefPtr<ListItemGroupPattern> groupItemPattern) const2192 int32_t ListPattern::ProcessAreaVertical(double& x, double& y, Rect& groupRect, int32_t& index,
2193 RefPtr<ListItemGroupPattern> groupItemPattern) const
2194 {
2195 if (groupItemPattern->GetTotalItemCount() > 0) { // has item
2196 Rect firstRect = GetItemRectInGroup(index, 0); //first item Rect
2197 Rect endRect = GetItemRectInGroup(index, groupItemPattern->GetDisplayEndIndexInGroup()); //end item Rect
2198
2199 if (groupItemPattern->IsHasHeader() && LessOrEqual(y, firstRect.Top()) && GreatOrEqual(y, groupRect.Top())) {
2200 return DEFAULT_HEADER_VALUE;
2201 }
2202
2203 if (groupItemPattern->IsHasFooter() && GreatOrEqual(y, endRect.Bottom()) &&
2204 LessOrEqual(y, groupRect.Bottom())) {
2205 return DEFAULT_FOOTER_VALUE;
2206 }
2207 } else if (groupItemPattern->IsHasHeader() || groupItemPattern->IsHasFooter()) {
2208 float headerHeight = groupItemPattern->GetHeaderMainSize();
2209 float footerHeight = groupItemPattern->GetFooterMainSize();
2210 float topPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->top.value_or(0.0f);
2211 float bottomPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->bottom.value_or(0.0f);
2212 if (LessOrEqual(y, groupRect.Top() + headerHeight + topPaddng) && GreatOrEqual(y, groupRect.Top())) { //header
2213 return DEFAULT_HEADER_VALUE;
2214 } else if (GreatOrEqual(y, groupRect.Bottom() - footerHeight - bottomPaddng) &&
2215 LessOrEqual(y, groupRect.Bottom())) {
2216 return DEFAULT_FOOTER_VALUE;
2217 }
2218 } else if (GreatOrEqual(y, groupRect.Top()) && LessOrEqual(y, groupRect.Bottom())) {
2219 return 0;
2220 }
2221
2222 return -1;
2223 }
2224
ProcessAreaHorizontal(double & x,double & y,Rect & groupRect,int32_t & index,RefPtr<ListItemGroupPattern> groupItemPattern) const2225 int32_t ListPattern::ProcessAreaHorizontal(double& x, double& y, Rect& groupRect, int32_t& index,
2226 RefPtr<ListItemGroupPattern> groupItemPattern) const
2227 {
2228 if (groupItemPattern->GetTotalItemCount() > 0) { // has item
2229 Rect firstRect = GetItemRectInGroup(index, 0); //first item Rect
2230 Rect endRect = GetItemRectInGroup(index, groupItemPattern->GetDisplayEndIndexInGroup()); //end item Rect
2231
2232 if (groupItemPattern->IsHasHeader() && LessOrEqual(x, firstRect.Left()) && GreatOrEqual(x, groupRect.Left())) {
2233 return DEFAULT_HEADER_VALUE;
2234 }
2235
2236 if (groupItemPattern->IsHasFooter() && GreatOrEqual(x, endRect.Right()) && LessOrEqual(x, groupRect.Right())) {
2237 return DEFAULT_FOOTER_VALUE;
2238 }
2239 } else if (groupItemPattern->IsHasHeader() || groupItemPattern->IsHasFooter()) {
2240 float headerHeight = groupItemPattern->GetHeaderMainSize();
2241 float footerHeight = groupItemPattern->GetFooterMainSize();
2242 float leftPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->left.value_or(0.0f);
2243 float rightPaddng = groupItemPattern->GetHost()->GetGeometryNode()->GetPadding()->right.value_or(0.0f);
2244 if (LessOrEqual(x, groupRect.Left() + headerHeight + leftPaddng) && GreatOrEqual(x, groupRect.Left())) {
2245 return DEFAULT_HEADER_VALUE;
2246 } else if (GreatOrEqual(x, groupRect.Right() - footerHeight - rightPaddng) &&
2247 LessOrEqual(x, groupRect.Right())) {
2248 return DEFAULT_FOOTER_VALUE;
2249 }
2250 } else if (GreatOrEqual(x, groupRect.Left()) && LessOrEqual(x, groupRect.Right())) {
2251 return 0;
2252 }
2253
2254 return -1;
2255 }
2256
GetItemRectInGroup(int32_t index,int32_t indexInGroup) const2257 Rect ListPattern::GetItemRectInGroup(int32_t index, int32_t indexInGroup) const
2258 {
2259 if (index < 0 || indexInGroup < 0 || index < startIndex_ || index > endIndex_) {
2260 return Rect();
2261 }
2262 auto host = GetHost();
2263 CHECK_NULL_RETURN(host, Rect());
2264 auto itemGroupWrapper = host->GetChildByIndex(index);
2265 CHECK_NULL_RETURN(itemGroupWrapper, Rect());
2266 auto itemGroup = itemGroupWrapper->GetHostNode();
2267 CHECK_NULL_RETURN(itemGroup, Rect());
2268 if (!(itemGroup->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG)) {
2269 return Rect();
2270 }
2271 auto itemGroupGeometry = itemGroup->GetGeometryNode();
2272 CHECK_NULL_RETURN(itemGroupGeometry, Rect());
2273 auto groupPattern = itemGroup->GetPattern<ListItemGroupPattern>();
2274 CHECK_NULL_RETURN(groupPattern, Rect());
2275 if (indexInGroup < groupPattern->GetDisplayStartIndexInGroup() ||
2276 indexInGroup > groupPattern->GetDisplayEndIndexInGroup()) {
2277 return Rect();
2278 }
2279 auto groupItem = itemGroup->GetChildByIndex(indexInGroup + groupPattern->GetItemStartIndex());
2280 CHECK_NULL_RETURN(groupItem, Rect());
2281 auto groupItemGeometry = groupItem->GetGeometryNode();
2282 CHECK_NULL_RETURN(groupItemGeometry, Rect());
2283 return Rect(itemGroupGeometry->GetFrameRect().GetX() + groupItemGeometry->GetFrameRect().GetX(),
2284 itemGroupGeometry->GetFrameRect().GetY() + groupItemGeometry->GetFrameRect().GetY(),
2285 groupItemGeometry->GetFrameRect().Width(), groupItemGeometry->GetFrameRect().Height());
2286 }
2287
CalculateJumpOffset()2288 bool ListPattern::CalculateJumpOffset()
2289 {
2290 for (const auto& pos : itemPosition_) {
2291 if (pos.second.groupInfo && !pos.second.groupInfo.value().atStart) {
2292 continue;
2293 }
2294 ListPositionInfo info = posMap_->GetPositionInfo(pos.first);
2295 if (!Negative(info.mainSize)) {
2296 currentOffset_ = info.mainPos - pos.second.startPos;
2297 return true;
2298 }
2299 }
2300 return false;
2301 }
2302
UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm> & listLayoutAlgorithm,bool isJump)2303 float ListPattern::UpdateTotalOffset(const RefPtr<ListLayoutAlgorithm>& listLayoutAlgorithm, bool isJump)
2304 {
2305 if (prevMeasureBreak_) {
2306 return 0;
2307 }
2308 float relativeOffset = listLayoutAlgorithm->GetCurrentOffset();
2309 double prevOffset = currentOffset_;
2310 if (childrenSize_) {
2311 listTotalHeight_ = posMap_->GetTotalHeight();
2312 currentOffset_ = itemPosition_.empty() ? 0.0f :
2313 posMap_->GetPos(itemPosition_.begin()->first, itemPosition_.begin()->second.startPos);
2314 } else {
2315 if (isJump && !needReEstimateOffset_) {
2316 if (CalculateJumpOffset()) {
2317 relativeOffset = 0.0f;
2318 } else {
2319 needReEstimateOffset_ = true;
2320 }
2321 }
2322 if (needReEstimateOffset_) {
2323 posMap_->ClearPosMap();
2324 auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis(), itemStartIndex_);
2325 calculate.SetPosMap(posMap_, true);
2326 calculate.GetEstimateHeightAndOffset(GetHost());
2327 currentOffset_ = calculate.GetEstimateOffset();
2328 listTotalHeight_ = calculate.GetEstimateHeight();
2329 relativeOffset = 0;
2330 needReEstimateOffset_ = false;
2331 heightEstimated_ = true;
2332 }
2333 CalculateCurrentOffset(relativeOffset, listLayoutAlgorithm->GetRecycledItemPosition());
2334 }
2335 CheckAndUpdateAnimateTo(relativeOffset, prevOffset);
2336
2337 return currentOffset_ - prevOffset;
2338 }
2339
CheckAndUpdateAnimateTo(float relativeOffset,float prevOffset)2340 void ListPattern::CheckAndUpdateAnimateTo(float relativeOffset, float prevOffset)
2341 {
2342 if (!scrollTarget_) {
2343 return;
2344 }
2345 auto& target = scrollTarget_.value();
2346 auto posInfo = posMap_->GetPositionInfo(target.index);
2347 if (Negative(posInfo.mainPos)) {
2348 return;
2349 }
2350 float startPos = posInfo.mainPos - currentOffset_;
2351 float targetPos = 0.0f;
2352 GetListItemAnimatePos(startPos + target.extraOffset, startPos + posInfo.mainSize + target.extraOffset,
2353 target.align, targetPos);
2354 targetPos += currentOffset_;
2355 const float epsilon = 0.1f;
2356 if (!NearEqual(relativeOffset + prevOffset, currentOffset_, epsilon) ||
2357 !NearEqual(target.targetOffset, targetPos, epsilon)) {
2358 target.targetOffset = targetPos;
2359 if (NearEqual(currentOffset_, targetPos)) {
2360 StopAnimate();
2361 } else {
2362 AnimateTo(targetPos, -1, nullptr, true, false, false);
2363 }
2364 }
2365 }
2366
CalculateCurrentOffset(float delta,const ListLayoutAlgorithm::PositionMap & recycledItemPosition)2367 void ListPattern::CalculateCurrentOffset(float delta, const ListLayoutAlgorithm::PositionMap& recycledItemPosition)
2368 {
2369 posMap_->UpdateTotalCount(maxListItemIndex_ + 1);
2370 if (itemPosition_.empty()) {
2371 return;
2372 }
2373 auto itemPos = itemPosition_;
2374 for (auto& [index, pos] : recycledItemPosition) {
2375 itemPos.try_emplace(index, pos);
2376 }
2377 float startPos = itemPos.begin()->second.startPos;
2378 int32_t startIndex = itemPos.begin()->first;
2379 auto& groupInfo = itemPos.begin()->second.groupInfo;
2380 bool groupAtStart = (!groupInfo || groupInfo.value().atStart);
2381 if (startIndex == 0 && groupAtStart) {
2382 // startPos has a small accuracy error.
2383 currentOffset_ = NearZero(startPos) ? 0.0f : -startPos;
2384 UpdatePosMap(itemPos);
2385 return;
2386 }
2387 currentOffset_ += delta;
2388 auto res = posMap_->GetStartIndexAndPos();
2389 if (res.first >= 0) {
2390 auto iter = itemPos.lower_bound(res.first);
2391 // skip same line for lanes
2392 if (iter->first == res.first && iter != itemPos.end()) {
2393 do {
2394 startPos = iter->second.startPos;
2395 iter++;
2396 } while (iter != itemPos.end() && NearEqual(iter->second.startPos, startPos));
2397 }
2398 if (iter != itemPos.end()) {
2399 groupAtStart = iter->second.groupInfo ? iter->second.groupInfo.value().atStart : true;
2400 startPos = iter->second.startPos;
2401 posMap_->UpdatePosMapStart(delta, currentOffset_, spaceWidth_, iter->first, startPos, groupAtStart);
2402 }
2403 }
2404 UpdatePosMap(itemPos);
2405 }
2406
UpdatePosMap(const ListLayoutAlgorithm::PositionMap & itemPos)2407 void ListPattern::UpdatePosMap(const ListLayoutAlgorithm::PositionMap& itemPos)
2408 {
2409 for (auto& [index, pos] : itemPos) {
2410 float height = pos.endPos - pos.startPos;
2411 if (pos.groupInfo) {
2412 bool groupAtStart = pos.groupInfo.value().atStart;
2413 bool groupAtEnd = pos.groupInfo.value().atEnd;
2414 if (groupAtStart && groupAtEnd) {
2415 posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height, pos.isGroup });
2416 } else {
2417 posMap_->UpdatePosWithCheck(index, { currentOffset_ + pos.startPos, height, pos.isGroup });
2418 }
2419 } else {
2420 posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height, pos.isGroup });
2421 }
2422 }
2423 auto& endGroupInfo = itemPos.rbegin()->second.groupInfo;
2424 bool groupAtEnd = (!endGroupInfo || endGroupInfo.value().atEnd);
2425 posMap_->UpdatePosMapEnd(itemPos.rbegin()->first, spaceWidth_, groupAtEnd);
2426 }
2427
UpdateChildPosInfo(int32_t index,float delta,float sizeChange)2428 void ListPattern::UpdateChildPosInfo(int32_t index, float delta, float sizeChange)
2429 {
2430 if (itemPosition_.find(index) == itemPosition_.end()) {
2431 return;
2432 }
2433 auto posInfo = posMap_->GetPositionInfo(index);
2434 auto prevPosInfo = posMap_->GetPositionInfo(index - 1);
2435 delta = isStackFromEnd_ ? -(delta + sizeChange) : delta;
2436 if (Negative(prevPosInfo.mainPos)) {
2437 posMap_->UpdatePos(index, {posInfo.mainPos + delta, posInfo.mainSize + sizeChange, posInfo.isGroup});
2438 }
2439 if (index == GetStartIndex()) {
2440 sizeChange += delta;
2441 float startPos = itemPosition_.begin()->second.startPos;
2442 auto iter = itemPosition_.begin();
2443 while (iter != itemPosition_.end() && NearEqual(startPos, iter->second.startPos)) {
2444 iter->second.startPos += delta;
2445 iter++;
2446 }
2447 }
2448 if (index == GetEndIndex()) {
2449 float endPos = itemPosition_.rbegin()->second.endPos;
2450 auto iter = itemPosition_.rbegin();
2451 while (iter != itemPosition_.rend() && NearEqual(endPos, iter->second.endPos)) {
2452 iter->second.endPos += sizeChange;
2453 iter++;
2454 }
2455 }
2456 CalculateCurrentOffset(0.0f, ListLayoutAlgorithm::PositionMap());
2457 }
2458
UpdateScrollBarOffset()2459 void ListPattern::UpdateScrollBarOffset()
2460 {
2461 CheckScrollBarOff();
2462 if (!GetScrollBar() && !GetScrollBarProxy()) {
2463 heightEstimated_ = false;
2464 return;
2465 }
2466 auto host = GetHost();
2467 CHECK_NULL_VOID(host);
2468 const auto& geometryNode = host->GetGeometryNode();
2469 auto frameSize = geometryNode->GetFrameSize();
2470 Size size(frameSize.Width(), frameSize.Height());
2471 if (itemPosition_.empty()) {
2472 UpdateScrollBarRegion(0.f, 0.f, size, Offset(0.0f, 0.0f));
2473 heightEstimated_ = false;
2474 return;
2475 }
2476 float currentOffset = 0.0f;
2477 float estimatedHeight = 0.0f;
2478 if (childrenSize_ || heightEstimated_) {
2479 currentOffset = currentOffset_;
2480 estimatedHeight = listTotalHeight_;
2481 heightEstimated_ = false;
2482 } else {
2483 auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis(), itemStartIndex_);
2484 calculate.SetPosMap(posMap_);
2485 calculate.GetEstimateHeightAndOffset(GetHost());
2486 currentOffset = calculate.GetEstimateOffset();
2487 estimatedHeight = calculate.GetEstimateHeight();
2488 }
2489 if (GetAlwaysEnabled()) {
2490 estimatedHeight = estimatedHeight - spaceWidth_;
2491 }
2492
2493 // calculate padding offset of list
2494 auto layoutPriority = host->GetLayoutProperty();
2495 CHECK_NULL_VOID(layoutPriority);
2496 auto padding = layoutPriority->CreatePaddingAndBorder();
2497 auto paddingMain = GetAxis() == Axis::VERTICAL ? padding.Height() : padding.Width();
2498
2499 auto mainSize = GetAxis() == Axis::VERTICAL ? size.Height() : size.Width();
2500 if (IsNeedAddContentOffset(LessOrEqual(estimatedHeight + paddingMain, mainSize))) {
2501 currentOffset += contentStartOffset_;
2502 estimatedHeight += contentStartOffset_ + contentEndOffset_;
2503 }
2504 float expandHeight = ScrollableUtils::CheckHeightExpansion(layoutPriority, GetAxis());
2505 estimatedHeight -= expandHeight;
2506
2507 UpdateScrollBarRegion(currentOffset, estimatedHeight + paddingMain, size, Offset(0.0f, 0.0f));
2508 }
2509
GetTotalHeight() const2510 float ListPattern::GetTotalHeight() const
2511 {
2512 auto currentOffset = GetTotalOffset();
2513 if (endIndex_ >= maxListItemIndex_) {
2514 return currentOffset + endMainPos_ + contentEndOffset_;
2515 }
2516 if (itemPosition_.empty()) {
2517 return 0.0f;
2518 }
2519 int32_t remainCount = maxListItemIndex_ - endIndex_;
2520 float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
2521 float remainOffset = itemsSize / itemPosition_.size() * remainCount - spaceWidth_;
2522 return currentOffset + endMainPos_ + remainOffset + contentEndOffset_;
2523 }
2524
TriggerModifyDone()2525 void ListPattern::TriggerModifyDone()
2526 {
2527 OnModifyDone();
2528 }
2529
SetChainAnimationCallback()2530 void ListPattern::SetChainAnimationCallback()
2531 {
2532 CHECK_NULL_VOID(chainAnimation_);
2533 chainAnimation_->SetAnimationCallback([weak = AceType::WeakClaim(this)]() {
2534 auto list = weak.Upgrade();
2535 CHECK_NULL_VOID(list);
2536 list->MarkDirtyNodeSelf();
2537 });
2538 auto scrollEffect = AceType::DynamicCast<ScrollSpringEffect>(GetScrollEdgeEffect());
2539 CHECK_NULL_VOID(scrollEffect);
2540 scrollEffect->SetOnWillStartSpringCallback([weak = AceType::WeakClaim(this)]() {
2541 auto list = weak.Upgrade();
2542 CHECK_NULL_VOID(list);
2543 if (!list->dragFromSpring_ && list->chainAnimation_) {
2544 auto delta = list->chainAnimation_->SetControlIndex(list->IsAtTop() ? 0 : list->maxListItemIndex_);
2545 list->currentDelta_ -= delta;
2546 list->dragFromSpring_ = true;
2547 }
2548 });
2549 }
2550
SetChainAnimation()2551 void ListPattern::SetChainAnimation()
2552 {
2553 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
2554 CHECK_NULL_VOID(listLayoutProperty);
2555 auto edgeEffect = GetEdgeEffect();
2556 int32_t lanes = std::max(listLayoutProperty->GetLanes().value_or(1), 1);
2557 bool autoLanes = listLayoutProperty->HasLaneMinLength() || listLayoutProperty->HasLaneMaxLength();
2558 bool animation = listLayoutProperty->GetChainAnimation().value_or(false);
2559 bool enable = edgeEffect == EdgeEffect::SPRING && lanes == 1 && !autoLanes && animation;
2560 if (!enable || GetEffectEdge() != EffectEdge::ALL) {
2561 chainAnimation_.Reset();
2562 return;
2563 }
2564 auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
2565 if (Negative(space)) {
2566 space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
2567 }
2568 if (!chainAnimation_ || (chainAnimation_ && space != chainAnimation_->GetSpace())) {
2569 springProperty_ =
2570 AceType::MakeRefPtr<SpringProperty>(CHAIN_SPRING_MASS, CHAIN_SPRING_STIFFNESS, CHAIN_SPRING_DAMPING);
2571 if (chainAnimationOptions_.has_value()) {
2572 float maxSpace = chainAnimationOptions_.value().maxSpace.ConvertToPx();
2573 float minSpace = chainAnimationOptions_.value().minSpace.ConvertToPx();
2574 minSpace = Negative(minSpace) ? 0.0f : minSpace;
2575 minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
2576 maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
2577 springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
2578 springProperty_->SetDamping(chainAnimationOptions_.value().damping);
2579 chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
2580 auto conductivity = chainAnimationOptions_.value().conductivity;
2581 if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
2582 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
2583 }
2584 chainAnimation_->SetConductivity(conductivity);
2585 auto intensity = chainAnimationOptions_.value().intensity;
2586 if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
2587 intensity = ChainAnimation::DEFAULT_INTENSITY;
2588 }
2589 chainAnimation_->SetIntensity(intensity);
2590 auto effect = chainAnimationOptions_.value().edgeEffect;
2591 chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
2592 } else {
2593 auto minSpace = space * DEFAULT_MIN_SPACE_SCALE;
2594 auto maxSpace = space * DEFAULT_MAX_SPACE_SCALE;
2595 chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
2596 }
2597 SetChainAnimationCallback();
2598 }
2599 }
2600
SetChainAnimationOptions(const ChainAnimationOptions & options)2601 void ListPattern::SetChainAnimationOptions(const ChainAnimationOptions& options)
2602 {
2603 chainAnimationOptions_ = options;
2604 if (chainAnimation_) {
2605 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
2606 CHECK_NULL_VOID(listLayoutProperty);
2607 auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
2608 if (Negative(space)) {
2609 space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
2610 }
2611 float maxSpace = options.maxSpace.ConvertToPx();
2612 float minSpace = options.minSpace.ConvertToPx();
2613 minSpace = Negative(minSpace) ? 0.0f : minSpace;
2614 minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
2615 maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
2616 chainAnimation_->SetSpace(space, maxSpace, minSpace);
2617 auto conductivity = chainAnimationOptions_.value().conductivity;
2618 if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
2619 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
2620 }
2621 chainAnimation_->SetConductivity(conductivity);
2622 auto intensity = chainAnimationOptions_.value().intensity;
2623 if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
2624 intensity = ChainAnimation::DEFAULT_INTENSITY;
2625 }
2626 chainAnimation_->SetIntensity(intensity);
2627 auto effect = options.edgeEffect;
2628 chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
2629 }
2630 if (springProperty_) {
2631 springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
2632 springProperty_->SetDamping(chainAnimationOptions_.value().damping);
2633 }
2634 }
2635
OnTouchDown(const TouchEventInfo & info)2636 void ListPattern::OnTouchDown(const TouchEventInfo& info)
2637 {
2638 ScrollablePattern::OnTouchDown(info);
2639 auto& touches = info.GetTouches();
2640 if (touches.empty()) {
2641 return;
2642 }
2643 auto offset = touches.front().GetLocalLocation();
2644 float startPosition = GetAxis() == Axis::HORIZONTAL ? offset.GetX() : offset.GetY();
2645 ProcessDragStart(startPosition);
2646 }
2647
ProcessDragStart(float startPosition)2648 void ListPattern::ProcessDragStart(float startPosition)
2649 {
2650 CHECK_NULL_VOID(chainAnimation_);
2651 auto host = GetHost();
2652 CHECK_NULL_VOID(host);
2653 auto globalOffset = host->GetTransformRelativeOffset();
2654 int32_t index = -1;
2655 auto offset = startPosition - GetMainAxisOffset(globalOffset, GetAxis());
2656 auto it = std::find_if(
2657 itemPosition_.begin(), itemPosition_.end(), [offset](auto pos) { return offset <= pos.second.endPos; });
2658 if (it != itemPosition_.end()) {
2659 index = it->first;
2660 } else if (!itemPosition_.empty()) {
2661 index = itemPosition_.rbegin()->first + 1;
2662 }
2663 dragFromSpring_ = false;
2664 float delta = chainAnimation_->SetControlIndex(index);
2665 currentDelta_ -= delta;
2666 chainAnimation_->SetMaxIndex(maxListItemIndex_);
2667 }
2668
ProcessDragUpdate(float dragOffset,int32_t source)2669 void ListPattern::ProcessDragUpdate(float dragOffset, int32_t source)
2670 {
2671 CHECK_NULL_VOID(chainAnimation_);
2672 if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR_FLING) {
2673 return;
2674 }
2675 if (NeedScrollSnapAlignEffect()) {
2676 auto delta = 0.0f;
2677 if (chainAnimation_->GetControlIndex() < startIndex_ - 1) {
2678 delta = chainAnimation_->SetControlIndex(std::max(startIndex_ - 1, 0));
2679 }
2680 if (chainAnimation_->GetControlIndex() > endIndex_ + 1) {
2681 delta = chainAnimation_->SetControlIndex(std::min(endIndex_ + 1, maxListItemIndex_));
2682 }
2683 if (!NearZero(delta)) {
2684 auto scrollableEvent = GetScrollableEvent();
2685 CHECK_NULL_VOID(scrollableEvent);
2686 auto scrollable = scrollableEvent->GetScrollable();
2687 CHECK_NULL_VOID(scrollable);
2688 scrollable->UpdateScrollSnapStartOffset(delta);
2689 currentDelta_ -= delta;
2690 }
2691 }
2692 float overOffset = 0.0f;
2693 if (!itemPosition_.empty()) {
2694 auto res = GetOutBoundaryOffset(0.0f, true);
2695 overOffset = std::max(res.start, res.end);
2696 if (!NearZero(res.end)) {
2697 overOffset = -overOffset;
2698 }
2699 }
2700 if (source == SCROLL_FROM_UPDATE && !NearZero(overOffset)) {
2701 dragOffset = 0.0f;
2702 }
2703 chainAnimation_->SetDelta(-dragOffset, overOffset);
2704 if (source == SCROLL_FROM_UPDATE && GetCanOverScroll()) {
2705 float tempDelta = currentDelta_;
2706 currentDelta_ -= dragOffset;
2707 bool isAtEdge = IsAtTop() || IsAtBottom(true);
2708 currentDelta_ = tempDelta;
2709 SetCanOverScroll(isAtEdge);
2710 }
2711 }
2712
GetChainDelta(int32_t index) const2713 float ListPattern::GetChainDelta(int32_t index) const
2714 {
2715 CHECK_NULL_RETURN(chainAnimation_, 0.0f);
2716 float chainDelta = chainAnimation_->GetValue(index);
2717 auto pipeline = GetContext();
2718 if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
2719 chainDelta = Round(chainDelta);
2720 }
2721 return chainDelta;
2722 }
2723
MultiSelectWithoutKeyboard(const RectF & selectedZone)2724 void ListPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
2725 {
2726 auto host = GetHost();
2727 CHECK_NULL_VOID(host);
2728 std::list<RefPtr<FrameNode>> childrens;
2729 host->GenerateOneDepthVisibleFrame(childrens);
2730 for (const auto& item : childrens) {
2731 if (item->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2732 auto itemGroupPattern = item->GetPattern<ListItemGroupPattern>();
2733 CHECK_NULL_VOID(itemGroupPattern);
2734 auto itemGroupGeometry = item->GetGeometryNode();
2735 CHECK_NULL_VOID(itemGroupGeometry);
2736 auto itemGroupRect = itemGroupGeometry->GetFrameRect();
2737 if (!selectedZone.IsIntersectWith(itemGroupRect)) {
2738 continue;
2739 }
2740 HandleCardModeSelectedEvent(selectedZone, item, itemGroupRect.GetOffset());
2741 continue;
2742 }
2743 auto itemPattern = item->GetPattern<ListItemPattern>();
2744 CHECK_NULL_VOID(itemPattern);
2745 if (!itemPattern->Selectable()) {
2746 continue;
2747 }
2748
2749 auto itemGeometry = item->GetGeometryNode();
2750 CHECK_NULL_VOID(itemGeometry);
2751
2752 auto itemRect = itemGeometry->GetFrameRect();
2753 if (!selectedZone.IsIntersectWith(itemRect)) {
2754 itemPattern->MarkIsSelected(false);
2755 } else {
2756 itemPattern->MarkIsSelected(true);
2757 }
2758 }
2759
2760 DrawSelectedZone(selectedZone);
2761 }
2762
HandleCardModeSelectedEvent(const RectF & selectedZone,const RefPtr<FrameNode> & itemGroupNode,const OffsetF & groupOffset)2763 void ListPattern::HandleCardModeSelectedEvent(
2764 const RectF& selectedZone, const RefPtr<FrameNode>& itemGroupNode, const OffsetF& groupOffset)
2765 {
2766 CHECK_NULL_VOID(itemGroupNode);
2767 std::list<RefPtr<FrameNode>> childrens;
2768 itemGroupNode->GenerateOneDepthVisibleFrame(childrens);
2769 for (const auto& item : childrens) {
2770 auto itemPattern = item->GetPattern<ListItemPattern>();
2771 if (!itemPattern) {
2772 continue;
2773 }
2774 if (!itemPattern->Selectable()) {
2775 continue;
2776 }
2777 auto itemGeometry = item->GetGeometryNode();
2778 CHECK_NULL_VOID(itemGeometry);
2779 auto context = item->GetRenderContext();
2780 CHECK_NULL_VOID(context);
2781 auto itemRect = itemGeometry->GetFrameRect();
2782 RectF itemRectInGroup(itemRect.GetX() + groupOffset.GetX(),
2783 itemRect.GetY() + groupOffset.GetY(), itemRect.Width(), itemRect.Height());
2784 if (!selectedZone.IsIntersectWith(itemRectInGroup)) {
2785 itemPattern->MarkIsSelected(false);
2786 } else {
2787 itemPattern->MarkIsSelected(true);
2788 }
2789 }
2790 }
2791
ClearMultiSelect()2792 void ListPattern::ClearMultiSelect()
2793 {
2794 auto host = GetHost();
2795 CHECK_NULL_VOID(host);
2796 std::list<RefPtr<FrameNode>> children;
2797 host->GenerateOneDepthAllFrame(children);
2798 for (const auto& child : children) {
2799 if (!child) {
2800 continue;
2801 }
2802 auto itemPattern = child->GetPattern<ListItemPattern>();
2803 if (itemPattern) {
2804 itemPattern->MarkIsSelected(false);
2805 continue;
2806 }
2807 auto itemGroupPattern = child->GetPattern<ListItemGroupPattern>();
2808 if (itemGroupPattern) {
2809 std::list<RefPtr<FrameNode>> itemChildren;
2810 child->GenerateOneDepthAllFrame(itemChildren);
2811 for (const auto& item : itemChildren) {
2812 if (!item) {
2813 continue;
2814 }
2815 itemPattern = item->GetPattern<ListItemPattern>();
2816 if (itemPattern) {
2817 itemPattern->MarkIsSelected(false);
2818 }
2819 }
2820 }
2821 }
2822
2823 ClearSelectedZone();
2824 }
2825
IsItemSelected(float offsetX,float offsetY)2826 bool ListPattern::IsItemSelected(float offsetX, float offsetY)
2827 {
2828 auto host = GetHost();
2829 CHECK_NULL_RETURN(host, false);
2830 auto node = host->FindChildByPosition(offsetX, offsetY);
2831 CHECK_NULL_RETURN(node, false);
2832 auto itemPattern = node->GetPattern<ListItemPattern>();
2833 if (itemPattern) {
2834 return itemPattern->IsSelected();
2835 }
2836 auto itemGroupPattern = node->GetPattern<ListItemGroupPattern>();
2837 if (itemGroupPattern) {
2838 auto itemNode = node->FindChildByPosition(offsetX, offsetY);
2839 CHECK_NULL_RETURN(itemNode, false);
2840 itemPattern = itemNode->GetPattern<ListItemPattern>();
2841 CHECK_NULL_RETURN(itemPattern, false);
2842 return itemPattern->IsSelected();
2843 }
2844 return false;
2845 }
2846
SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)2847 void ListPattern::SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)
2848 {
2849 // swiper item only can be replaced when no other items be dragged
2850 if (canReplaceSwiperItem_) {
2851 if (swiperItem != swiperItem_) {
2852 auto item = swiperItem_.Upgrade();
2853 if (item) {
2854 item->ResetSwipeStatus();
2855 }
2856 swiperItem_ = std::move(swiperItem);
2857 }
2858 canReplaceSwiperItem_ = false;
2859 }
2860 FireAndCleanScrollingListener();
2861 }
2862
GetItemIndexByPosition(float xOffset,float yOffset)2863 int32_t ListPattern::GetItemIndexByPosition(float xOffset, float yOffset)
2864 {
2865 auto host = GetHost();
2866 CHECK_NULL_RETURN(host, GetStartIndex());
2867 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
2868 CHECK_NULL_RETURN(listProperty, GetStartIndex());
2869 auto globalOffset = host->GetTransformRelativeOffset();
2870 float relativeX = xOffset - globalOffset.GetX();
2871 float relativeY = yOffset - globalOffset.GetY();
2872 float mainOffset = GetAxis() == Axis::VERTICAL ? relativeY : relativeX;
2873 float crossOffset = GetAxis() == Axis::VERTICAL ? relativeX : relativeY;
2874 float mainSize = GetMainAxisSize(GetContentSize(), GetAxis());
2875 float crossSize = GetCrossAxisSize(GetContentSize(), GetAxis());
2876 if (TextDirection::RTL == listProperty->GetNonAutoLayoutDirection()) {
2877 if (Axis::VERTICAL == GetAxis()) {
2878 crossOffset = crossSize - crossOffset;
2879 } else {
2880 mainOffset = mainSize - mainOffset;
2881 }
2882 }
2883 int32_t lanesOffset = 0;
2884 if (lanes_ > 1 && !NearZero(crossSize + laneGutter_)) {
2885 lanesOffset = static_cast<int32_t>(crossOffset / ((crossSize + laneGutter_) / lanes_));
2886 }
2887 for (auto& pos : itemPosition_) {
2888 if (mainOffset <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
2889 return std::min(pos.first + lanesOffset, maxListItemIndex_ + 1);
2890 }
2891 }
2892 if (!itemPosition_.empty()) {
2893 return itemPosition_.rbegin()->first + 1;
2894 }
2895 return 0;
2896 }
2897
FocusWrapModeToString(FocusWrapMode mode)2898 std::string static FocusWrapModeToString(FocusWrapMode mode)
2899 {
2900 switch (mode) {
2901 case FocusWrapMode::WRAP_WITH_ARROW:
2902 return "WRAP_WITH_ARROW";
2903 default:
2904 return "DEFAULT";
2905 }
2906 }
2907
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const2908 void ListPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
2909 {
2910 ScrollablePattern::ToJsonValue(json, filter);
2911 /* no fixed attr below, just return */
2912 if (filter.IsFastFilter()) {
2913 return;
2914 }
2915 json->PutExtAttr("multiSelectable", multiSelectable_, filter);
2916 json->PutExtAttr("startIndex", startIndex_, filter);
2917 if (!itemPosition_.empty()) {
2918 json->PutExtAttr("itemStartPos", itemPosition_.begin()->second.startPos, filter);
2919 }
2920 json->PutExtAttr("maintainVisibleContentPosition", maintainVisibleContentPosition_, filter);
2921 auto nestedScrollOptions = JsonUtil::Create(true);
2922 auto nestedScroll = GetNestedScroll();
2923 nestedScrollOptions->Put("scrollForward", nestedScroll.GetNestedScrollModeStr(nestedScroll.forward).c_str());
2924 nestedScrollOptions->Put("scrollBackward", nestedScroll.GetNestedScrollModeStr(nestedScroll.backward).c_str());
2925 json->PutExtAttr("nestedScroll", nestedScrollOptions, filter);
2926 json->PutExtAttr("focusWrapMode", FocusWrapModeToString(focusWrapMode_).c_str(), filter);
2927 }
2928
FromJson(const std::unique_ptr<JsonValue> & json)2929 void ListPattern::FromJson(const std::unique_ptr<JsonValue>& json)
2930 {
2931 ScrollToIndex(json->GetInt("startIndex"));
2932 if (json->Contains("itemStartPos")) {
2933 ScrollBy(-json->GetDouble("itemStartPos"));
2934 }
2935 auto host = GetHost();
2936 CHECK_NULL_VOID(host);
2937 host->GetRenderContext()->UpdateClipEdge(true);
2938 ScrollablePattern::FromJson(json);
2939 }
2940
GetListItemGroupParameter(const RefPtr<FrameNode> & node)2941 ListItemGroupPara ListPattern::GetListItemGroupParameter(const RefPtr<FrameNode>& node)
2942 {
2943 ListItemGroupPara listItemGroupPara = { -1, -1, -1, -1 };
2944 auto curFrameParent = node->GetParent();
2945 auto curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2946 while (curFrameParent && (!curFrameParentNode)) {
2947 curFrameParent = curFrameParent->GetParent();
2948 curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
2949 }
2950 CHECK_NULL_RETURN(curFrameParentNode, listItemGroupPara);
2951 if (curFrameParent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
2952 auto itemGroupPattern = curFrameParentNode->GetPattern<ListItemGroupPattern>();
2953 CHECK_NULL_RETURN(itemGroupPattern, listItemGroupPara);
2954 listItemGroupPara.displayEndIndex = itemGroupPattern->GetDisplayEndIndexInGroup();
2955 listItemGroupPara.displayStartIndex = itemGroupPattern->GetDisplayStartIndexInGroup();
2956 listItemGroupPara.lanes = itemGroupPattern->GetLanesInGroup();
2957 listItemGroupPara.itemEndIndex = itemGroupPattern->GetEndIndexInGroup();
2958 listItemGroupPara.hasHeader = itemGroupPattern->IsHasHeader();
2959 listItemGroupPara.hasFooter = itemGroupPattern->IsHasFooter();
2960 }
2961 return listItemGroupPara;
2962 }
2963
IsListItemGroup(int32_t listIndex,RefPtr<FrameNode> & node)2964 bool ListPattern::IsListItemGroup(int32_t listIndex, RefPtr<FrameNode>& node)
2965 {
2966 auto listFrame = GetHost();
2967 CHECK_NULL_RETURN(listFrame, false);
2968 auto listFocus = listFrame->GetFocusHub();
2969 CHECK_NULL_RETURN(listFocus, false);
2970 bool isItemGroup = false;
2971 listFocus->AnyChildFocusHub([&isItemGroup, &node, listIndex](const RefPtr<FocusHub>& childFocus) {
2972 if (!childFocus->IsFocusable()) {
2973 return false;
2974 }
2975 if (auto childFrame = childFocus->GetFrameNode()) {
2976 if (auto childPattern = AceType::DynamicCast<ListItemPattern>(childFrame->GetPattern())) {
2977 auto curIndex = childPattern->GetIndexInList();
2978 auto curIndexInGroup = childPattern->GetIndexInListItemGroup();
2979 if (curIndex == listIndex) {
2980 node = childFrame;
2981 isItemGroup = curIndexInGroup > -1;
2982 return true;
2983 }
2984 }
2985 }
2986 return false;
2987 });
2988 return isItemGroup;
2989 }
2990
RefreshLanesItemRange()2991 void ListPattern::RefreshLanesItemRange()
2992 {
2993 auto host = GetHost();
2994 CHECK_NULL_VOID(host);
2995 auto updatePos = host->GetChildrenUpdated();
2996 if (updatePos == -1) {
2997 return;
2998 }
2999 host->ChildrenUpdatedFrom(-1);
3000 if (updatePos == 0) {
3001 lanesItemRange_.clear();
3002 return;
3003 }
3004 for (auto it = lanesItemRange_.begin(); it != lanesItemRange_.end();) {
3005 if (it->second < updatePos) {
3006 it++;
3007 } else if (it->first >= updatePos) {
3008 it = lanesItemRange_.erase(it);
3009 } else {
3010 it->second = updatePos - 1;
3011 it++;
3012 }
3013 }
3014 }
3015
ProvideRestoreInfo()3016 std::string ListPattern::ProvideRestoreInfo()
3017 {
3018 return std::to_string(startIndex_);
3019 }
3020
CloseAllSwipeActions(OnFinishFunc && onFinishCallback)3021 void ListPattern::CloseAllSwipeActions(OnFinishFunc&& onFinishCallback)
3022 {
3023 auto item = swiperItem_.Upgrade();
3024 if (item) {
3025 return item->CloseSwipeAction(std::move(onFinishCallback));
3026 }
3027 }
3028
OnRestoreInfo(const std::string & restoreInfo)3029 void ListPattern::OnRestoreInfo(const std::string& restoreInfo)
3030 {
3031 jumpIndex_ = StringUtils::StringToInt(restoreInfo);
3032 }
3033
DumpAdvanceInfo()3034 void ListPattern::DumpAdvanceInfo()
3035 {
3036 ScrollablePattern::DumpAdvanceInfo();
3037 DumpLog::GetInstance().AddDesc("maxListItemIndex:" + std::to_string(maxListItemIndex_));
3038 DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(startIndex_));
3039 DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(endIndex_));
3040 DumpLog::GetInstance().AddDesc("centerIndex:" + std::to_string(centerIndex_));
3041 DumpLog::GetInstance().AddDesc("startMainPos:" + std::to_string(startMainPos_));
3042 DumpLog::GetInstance().AddDesc("endMainPos:" + std::to_string(endMainPos_));
3043 DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(currentOffset_));
3044 DumpLog::GetInstance().AddDesc("contentMainSize:" + std::to_string(contentMainSize_));
3045 DumpLog::GetInstance().AddDesc("contentStartOffset:" + std::to_string(contentStartOffset_));
3046 DumpLog::GetInstance().AddDesc("contentEndOffset:" + std::to_string(contentEndOffset_));
3047 DumpLog::GetInstance().AddDesc("currentDelta:" + std::to_string(currentDelta_));
3048 DumpLog::GetInstance().AddDesc("stackFromEnd:" + std::to_string(isStackFromEnd_));
3049 crossMatchChild_ ? DumpLog::GetInstance().AddDesc("crossMatchChild:true")
3050 : DumpLog::GetInstance().AddDesc("crossMatchChild:false");
3051 smooth_ ? DumpLog::GetInstance().AddDesc("smooth:true") : DumpLog::GetInstance().AddDesc("smooth:false");
3052 if (jumpIndex_.has_value()) {
3053 DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(jumpIndex_.value()));
3054 } else {
3055 DumpLog::GetInstance().AddDesc("jumpIndex:null");
3056 }
3057 if (jumpIndexInGroup_.has_value()) {
3058 DumpLog::GetInstance().AddDesc("jumpIndexInGroup:" + std::to_string(jumpIndexInGroup_.value()));
3059 } else {
3060 DumpLog::GetInstance().AddDesc("jumpIndexInGroup:null");
3061 }
3062 if (targetIndex_.has_value()) {
3063 DumpLog::GetInstance().AddDesc("targetIndex:" + std::to_string(targetIndex_.value()));
3064 } else {
3065 DumpLog::GetInstance().AddDesc("targetIndex:null");
3066 }
3067 if (predictSnapOffset_.has_value()) {
3068 DumpLog::GetInstance().AddDesc("predictSnapOffset:" + std::to_string(predictSnapOffset_.value()));
3069 } else {
3070 DumpLog::GetInstance().AddDesc("predictSnapOffset:null");
3071 }
3072 if (predictSnapEndPos_.has_value()) {
3073 DumpLog::GetInstance().AddDesc("predictSnapEndPos:" + std::to_string(predictSnapEndPos_.value()));
3074 } else {
3075 DumpLog::GetInstance().AddDesc("predictSnapEndPos:null");
3076 }
3077 // DumpLog::GetInstance().AddDesc("scrollAlign:%{public}d", scrollAlign_);
3078 paintStateFlag_ ? DumpLog::GetInstance().AddDesc("paintStateFlag:true")
3079 : DumpLog::GetInstance().AddDesc("paintStateFlag:false");
3080 isFramePaintStateValid_ ? DumpLog::GetInstance().AddDesc("isFramePaintStateValid:true")
3081 : DumpLog::GetInstance().AddDesc("isFramePaintStateValid:false");
3082 for (auto item : itemPosition_) {
3083 DumpLog::GetInstance().AddDesc("------------------------------------------");
3084 DumpLog::GetInstance().AddDesc("itemPosition.first:" + std::to_string(item.first));
3085 DumpLog::GetInstance().AddDesc("startPos:" + std::to_string(item.second.startPos));
3086 DumpLog::GetInstance().AddDesc("endPos:" + std::to_string(item.second.endPos));
3087 DumpLog::GetInstance().AddDesc("isGroup:" + std::to_string(item.second.isGroup));
3088 }
3089 DumpLog::GetInstance().AddDesc("------------------------------------------");
3090 scrollStop_ ? DumpLog::GetInstance().AddDesc("scrollStop:true")
3091 : DumpLog::GetInstance().AddDesc("scrollStop:false");
3092 for (auto item : lanesItemRange_) {
3093 DumpLog::GetInstance().AddDesc("------------------------------------------");
3094 DumpLog::GetInstance().AddDesc("lanesItemRange.first:" + std::to_string(item.first));
3095 DumpLog::GetInstance().AddDesc("lanesItemRange.second:" + std::to_string(item.second));
3096 }
3097 DumpLog::GetInstance().AddDesc("------------------------------------------");
3098 DumpLog::GetInstance().AddDesc("lanes:" + std::to_string(lanes_));
3099 DumpLog::GetInstance().AddDesc("laneGutter:" + std::to_string(laneGutter_));
3100 dragFromSpring_ ? DumpLog::GetInstance().AddDesc("dragFromSpring:true")
3101 : DumpLog::GetInstance().AddDesc("dragFromSpring:false");
3102 isScrollEnd_ ? DumpLog::GetInstance().AddDesc("isScrollEnd:true")
3103 : DumpLog::GetInstance().AddDesc("isScrollEnd:false");
3104 IsAtTop() ? DumpLog::GetInstance().AddDesc("IsAtTop:true") : DumpLog::GetInstance().AddDesc("IsAtTop:false");
3105 IsAtBottom() ? DumpLog::GetInstance().AddDesc("IsAtBottom:true")
3106 : DumpLog::GetInstance().AddDesc("IsAtBottom:false");
3107 }
3108
GetEventDumpInfo()3109 void ListPattern::GetEventDumpInfo()
3110 {
3111 ScrollablePattern::GetEventDumpInfo();
3112 auto host = GetHost();
3113 CHECK_NULL_VOID(host);
3114 auto hub = host->GetOrCreateEventHub<ListEventHub>();
3115 CHECK_NULL_VOID(hub);
3116 auto onScrollIndex = hub->GetOnScrollIndex();
3117 onScrollIndex ? DumpLog::GetInstance().AddDesc("hasOnScrollIndex: true")
3118 : DumpLog::GetInstance().AddDesc("hasOnScrollIndex: false");
3119 auto onJSFrameNodeScrollIndex = hub->GetJSFrameNodeOnListScrollIndex();
3120 onJSFrameNodeScrollIndex ? DumpLog::GetInstance().AddDesc("nodeOnScrollIndex: true")
3121 : DumpLog::GetInstance().AddDesc("nodeOnScrollIndex: false");
3122 auto onScrollVisibleContentChange = hub->GetOnScrollVisibleContentChange();
3123 onScrollVisibleContentChange ? DumpLog::GetInstance().AddDesc("hasOnScrollChange: true")
3124 : DumpLog::GetInstance().AddDesc("hasOnScrollChange: false");
3125 auto onJSFrameNodeScrollVisibleContentChange = hub->GetJSFrameNodeOnScrollVisibleContentChange();
3126 onJSFrameNodeScrollVisibleContentChange
3127 ? DumpLog::GetInstance().AddDesc("nodeOnScrollChange: true")
3128 : DumpLog::GetInstance().AddDesc("nodeOnScrollChange: false");
3129 }
3130
GetEventDumpInfo(std::unique_ptr<JsonValue> & json)3131 void ListPattern::GetEventDumpInfo(std::unique_ptr<JsonValue>& json)
3132 {
3133 ScrollablePattern::GetEventDumpInfo(json);
3134 auto host = GetHost();
3135 CHECK_NULL_VOID(host);
3136 auto hub = host->GetOrCreateEventHub<ListEventHub>();
3137 CHECK_NULL_VOID(hub);
3138 auto onScrollIndex = hub->GetOnScrollIndex();
3139 json->Put("hasOnScrollIndex", onScrollIndex ? "true" : "false");
3140 auto onJSFrameNodeScrollIndex = hub->GetJSFrameNodeOnListScrollIndex();
3141 json->Put("nodeOnScrollIndex", onJSFrameNodeScrollIndex ? "true" : "false");
3142 auto onScrollVisibleContentChange = hub->GetOnScrollVisibleContentChange();
3143 json->Put("hasOnScrollChange", onScrollVisibleContentChange ? "true" : "false");
3144 auto onJSFrameNodeScrollVisibleContentChange = hub->GetJSFrameNodeOnScrollVisibleContentChange();
3145 json->Put("nodeOnScrollChange", onJSFrameNodeScrollVisibleContentChange ? "true" : "false");
3146 }
3147
GetDefaultScrollBarDisplayMode() const3148 DisplayMode ListPattern::GetDefaultScrollBarDisplayMode() const
3149 {
3150 auto defaultDisplayMode = DisplayMode::OFF;
3151 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
3152 defaultDisplayMode = DisplayMode::AUTO;
3153 }
3154 return defaultDisplayMode;
3155 }
3156
GetVisibleSelectedItems()3157 std::vector<RefPtr<FrameNode>> ListPattern::GetVisibleSelectedItems()
3158 {
3159 std::vector<RefPtr<FrameNode>> children;
3160 auto host = GetHost();
3161 CHECK_NULL_RETURN(host, children);
3162 for (int32_t index = startIndex_; index <= endIndex_; ++index) {
3163 auto item = host->GetChildByIndex(index + itemStartIndex_);
3164 if (!AceType::InstanceOf<FrameNode>(item)) {
3165 continue;
3166 }
3167 auto itemFrameNode = AceType::DynamicCast<FrameNode>(item);
3168 auto itemPattern = itemFrameNode->GetPattern<ListItemPattern>();
3169 if (!itemPattern) {
3170 continue;
3171 }
3172 if (!itemPattern->IsSelected()) {
3173 continue;
3174 }
3175 children.emplace_back(itemFrameNode);
3176 }
3177 return children;
3178 }
3179
GetOrCreateListChildrenMainSize()3180 RefPtr<ListChildrenMainSize> ListPattern::GetOrCreateListChildrenMainSize()
3181 {
3182 if (childrenSize_) {
3183 return childrenSize_;
3184 }
3185 childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>();
3186 auto callback = [weakPattern = WeakClaim(this)](std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag) {
3187 auto pattern = weakPattern.Upgrade();
3188 CHECK_NULL_VOID(pattern);
3189 auto context = pattern->GetContext();
3190 CHECK_NULL_VOID(context);
3191 context->AddBuildFinishCallBack([weakPattern, change, flag]() {
3192 auto pattern = weakPattern.Upgrade();
3193 CHECK_NULL_VOID(pattern);
3194 pattern->OnChildrenSizeChanged(change, flag);
3195 });
3196 context->RequestFrame();
3197 };
3198 childrenSize_->SetOnDataChange(callback);
3199 auto pipeline = GetContext();
3200 if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
3201 childrenSize_->SetIsRoundingMode();
3202 }
3203 return childrenSize_;
3204 }
3205
OnChildrenSizeChanged(std::tuple<int32_t,int32_t,int32_t> change,ListChangeFlag flag)3206 void ListPattern::OnChildrenSizeChanged(std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag)
3207 {
3208 if (!posMap_) {
3209 posMap_ = MakeRefPtr<ListPositionMap>();
3210 }
3211 posMap_->MarkDirty(flag);
3212 MarkDirtyNodeSelf();
3213 }
3214
SetListChildrenMainSize(float defaultSize,const std::vector<float> & mainSize)3215 void ListPattern::SetListChildrenMainSize(float defaultSize, const std::vector<float>& mainSize)
3216 {
3217 childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>(mainSize, defaultSize);
3218 OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
3219 auto pipeline = GetContext();
3220 if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
3221 childrenSize_->SetIsRoundingMode();
3222 }
3223 }
3224
ResetChildrenSize()3225 void ListPattern::ResetChildrenSize()
3226 {
3227 if (childrenSize_) {
3228 childrenSize_ = nullptr;
3229 MarkDirtyNodeSelf();
3230 OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
3231 }
3232 }
3233
OnScrollVisibleContentChange(const RefPtr<ListEventHub> & listEventHub,bool indexChanged)3234 void ListPattern::OnScrollVisibleContentChange(const RefPtr<ListEventHub>& listEventHub, bool indexChanged)
3235 {
3236 CHECK_NULL_VOID(listEventHub);
3237 bool startChanged = UpdateStartListItemIndex();
3238 bool endChanged = UpdateEndListItemIndex();
3239 auto onScrollVisibleContentChange = listEventHub->GetOnScrollVisibleContentChange();
3240 auto OnJSFrameNodeScrollVisibleContentChange = listEventHub->GetJSFrameNodeOnScrollVisibleContentChange();
3241 if (onScrollVisibleContentChange) {
3242 if (indexChanged || startChanged || endChanged) {
3243 onScrollVisibleContentChange(startInfo_, endInfo_);
3244 groupIndexChanged_ = true;
3245 ReportOnItemListScrollEvent("onScrollVisibleContentChange", startInfo_.index, endInfo_.index);
3246 }
3247 }
3248 if (OnJSFrameNodeScrollVisibleContentChange) {
3249 if (indexChanged || startChanged || endChanged) {
3250 OnJSFrameNodeScrollVisibleContentChange(startInfo_, endInfo_);
3251 }
3252 }
3253 }
3254
GetScrollUpdateFriction(float overScroll)3255 float ListPattern::GetScrollUpdateFriction(float overScroll)
3256 {
3257 return ScrollablePattern::CalculateFriction(std::abs(overScroll) / contentMainSize_);
3258 }
3259
NotifyDataChange(int32_t index,int32_t count)3260 void ListPattern::NotifyDataChange(int32_t index, int32_t count)
3261 {
3262 if (focusIndex_.has_value() && focusIndex_.value() >= index) {
3263 focusIndex_ = focusIndex_.value() + count;
3264 if (focusIndex_ < 0) {
3265 focusIndex_ = 0;
3266 }
3267 }
3268
3269 if (!maintainVisibleContentPosition_ || itemPosition_.empty() || count == 0) {
3270 return;
3271 }
3272 if (index == 0 && count > 0 && IsBackToTopRunning()) {
3273 SetUseTotalOffset(false);
3274 }
3275 auto startIndex = itemPosition_.begin()->first;
3276 auto endIndex = itemPosition_.rbegin()->first;
3277 if (!CheckDataChangeOutOfStart(index, count, startIndex, endIndex)) {
3278 return;
3279 }
3280 count = !isStackFromEnd_ ? std::max(count, index - startIndex) : - std::max(count, endIndex - index);
3281 int32_t mod = 0;
3282 if (count < 0 && lanes_ > 1 && !(itemPosition_.begin()->second.isGroup)) {
3283 mod = -count % lanes_;
3284 }
3285 auto prevPosMap = std::move(itemPosition_);
3286 for (auto &pos : prevPosMap) {
3287 if (mod > 0) {
3288 mod--;
3289 } else {
3290 itemPosition_[pos.first + count] = pos.second;
3291 }
3292 }
3293 needReEstimateOffset_ = true;
3294 }
3295
CheckDataChangeOutOfStart(int32_t index,int32_t count,int32_t startIndex,int32_t endIndex)3296 bool ListPattern::CheckDataChangeOutOfStart(int32_t index, int32_t count, int32_t startIndex, int32_t endIndex)
3297 {
3298 if (((count > 0 && index > startIndex) || (count < 0 && index >= startIndex)) && !isStackFromEnd_) {
3299 return false;
3300 }
3301 if (((count > 0 && index < endIndex) || (count < 0 && index <= endIndex)) && isStackFromEnd_) {
3302 return false;
3303 }
3304 return true;
3305 }
3306
CreatePositionInfo(std::unique_ptr<JsonValue> & json)3307 void ListPattern::CreatePositionInfo(std::unique_ptr<JsonValue>& json)
3308 {
3309 std::unique_ptr<JsonValue> children = JsonUtil::CreateArray(true);
3310 for (auto item : itemPosition_) {
3311 std::unique_ptr<JsonValue> child = JsonUtil::Create(true);
3312 child->Put("itemPosition.first", std::to_string(item.first).c_str());
3313 child->Put("startPos", std::to_string(item.second.startPos).c_str());
3314 child->Put("endPos", std::to_string(item.second.endPos).c_str());
3315 child->Put("isGroup", std::to_string(item.second.isGroup).c_str());
3316 children->Put(child);
3317 }
3318 json->Put("itemPosition", children);
3319 }
3320
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)3321 void ListPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
3322 {
3323 ScrollablePattern::DumpAdvanceInfo(json);
3324 json->Put("maxListItemIndex", maxListItemIndex_);
3325 json->Put("startIndex", startIndex_);
3326 json->Put("endIndex", endIndex_);
3327 json->Put("centerIndex", centerIndex_);
3328 json->Put("startMainPos", startMainPos_);
3329 json->Put("endMainPos", endMainPos_);
3330 json->Put("currentOffset", currentOffset_);
3331 json->Put("contentMainSize", contentMainSize_);
3332 json->Put("contentStartOffset", contentStartOffset_);
3333 json->Put("contentEndOffset", contentEndOffset_);
3334 json->Put("currentDelta", currentDelta_);
3335 json->Put("crossMatchChild", crossMatchChild_);
3336 json->Put("smooth", smooth_);
3337 json->Put("jumpIndex", jumpIndex_.has_value() ? std::to_string(jumpIndex_.value()).c_str() : "null");
3338 json->Put(
3339 "jumpIndexInGroup", jumpIndexInGroup_.has_value() ? std::to_string(jumpIndexInGroup_.value()).c_str() : "null");
3340 json->Put("targetIndex", targetIndex_.has_value() ? std::to_string(targetIndex_.value()).c_str() : "null");
3341 json->Put("predictSnapOffset",
3342 predictSnapOffset_.has_value() ? std::to_string(predictSnapOffset_.value()).c_str() : "null");
3343 json->Put("predictSnapEndPos",
3344 predictSnapEndPos_.has_value() ? std::to_string(predictSnapEndPos_.value()).c_str() : "null");
3345 json->Put("paintStateFlag", paintStateFlag_);
3346 json->Put("isFramePaintStateValid", isFramePaintStateValid_);
3347 CreatePositionInfo(json);
3348 json->Put("scrollStop", scrollStop_);
3349 std::unique_ptr<JsonValue> lanesChildrenArray = JsonUtil::CreateArray(true);
3350 for (auto item : lanesItemRange_) {
3351 std::unique_ptr<JsonValue> child = JsonUtil::Create(true);
3352 child->Put("lanesItemRange.first", std::to_string(item.first).c_str());
3353 child->Put("lanesItemRange.second", std::to_string(item.second).c_str());
3354 lanesChildrenArray->Put(child);
3355 }
3356 json->Put("lanesItemRange", lanesChildrenArray);
3357 json->Put("lanes", std::to_string(lanes_).c_str());
3358 json->Put("laneGutter", std::to_string(laneGutter_).c_str());
3359 json->Put("dragFromSpring", dragFromSpring_);
3360 json->Put("isScrollEnd", isScrollEnd_);
3361 json->Put("IsAtTop", IsAtTop());
3362 json->Put("IsAtBottom", IsAtBottom());
3363 }
3364
GetChildrenExpandedSize()3365 SizeF ListPattern::GetChildrenExpandedSize()
3366 {
3367 auto viewSize = GetViewSizeMinusPadding();
3368 auto axis = GetAxis();
3369 float estimatedHeight = 0.0f;
3370 if (childrenSize_) {
3371 estimatedHeight = listTotalHeight_;
3372 } else if (!itemPosition_.empty()) {
3373 auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, axis, itemStartIndex_);
3374 calculate.SetPosMap(posMap_);
3375 calculate.GetEstimateHeightAndOffset(GetHost());
3376 estimatedHeight = calculate.GetEstimateHeight();
3377 }
3378
3379 if (axis == Axis::VERTICAL) {
3380 return SizeF(viewSize.Width(), estimatedHeight);
3381 } else if (axis == Axis::HORIZONTAL) {
3382 return SizeF(estimatedHeight, viewSize.Height());
3383 }
3384 return SizeF();
3385 }
3386
LayoutItemInGroupForFocus(int32_t indexInList,int32_t nextIndexInGroup,int32_t curIndexInGroup,const ListItemGroupPara & listItemGroupPara,int32_t maxListItemIndex)3387 bool ListPattern::LayoutItemInGroupForFocus(int32_t indexInList, int32_t nextIndexInGroup, int32_t curIndexInGroup,
3388 const ListItemGroupPara& listItemGroupPara, int32_t maxListItemIndex)
3389 {
3390 // nextIndexInGroup = -1 indicates the current position is Header; nextIndexInGroup = maxListItemIndex indicates the
3391 // current position is Footer.
3392 // Header and Footer will always be laid out, directly return true.
3393 if (nextIndexInGroup == -1 || nextIndexInGroup == maxListItemIndex) {
3394 return true;
3395 }
3396 ScrollAlign align = ScrollAlign::AUTO;
3397 // The second "||" condition indicates that currently only the footer or header is displayed,
3398 // without displaying any ListItems.
3399 if ((nextIndexInGroup < curIndexInGroup && nextIndexInGroup < listItemGroupPara.displayStartIndex) ||
3400 (nextIndexInGroup < curIndexInGroup &&
3401 listItemGroupPara.displayStartIndex == listItemGroupPara.displayEndIndex)) {
3402 align = ScrollAlign::START;
3403 } else if ((nextIndexInGroup > curIndexInGroup && nextIndexInGroup > listItemGroupPara.displayEndIndex) ||
3404 (nextIndexInGroup > curIndexInGroup &&
3405 listItemGroupPara.displayStartIndex == listItemGroupPara.displayEndIndex)) {
3406 align = ScrollAlign::END;
3407 } else {
3408 return true;
3409 }
3410
3411 if (!IsLayout(indexInList, nextIndexInGroup, align)) {
3412 // Do the Layout for indexInList
3413 isLayoutListForFocus_ = true;
3414 targetIndex_ = indexInList;
3415 targetIndexInGroup_ = nextIndexInGroup;
3416 scrollAlign_ = align;
3417 auto pipeline = GetContext();
3418 CHECK_NULL_RETURN(pipeline, false);
3419 MarkDirtyNodeSelf();
3420 pipeline->FlushUITasks();
3421 return false;
3422 }
3423 return true;
3424 }
3425
LayoutListForFocus(int32_t nextIndex,std::optional<int32_t> indexInGroup)3426 bool ListPattern::LayoutListForFocus(int32_t nextIndex, std::optional<int32_t> indexInGroup)
3427 {
3428 if (!IsLayout(nextIndex, indexInGroup, ScrollAlign::AUTO)) {
3429 isLayoutListForFocus_ = true;
3430 targetIndex_ = nextIndex;
3431 if (nextIndex < startIndex_) {
3432 scrollAlign_ = ScrollAlign::START;
3433 } else if (nextIndex > endIndex_) {
3434 scrollAlign_ = ScrollAlign::END;
3435 }
3436 if (indexInGroup) {
3437 targetIndexInGroup_ = indexInGroup.value();
3438 }
3439 auto pipeline = GetContext();
3440 CHECK_NULL_RETURN(pipeline, false);
3441 MarkDirtyNodeSelf();
3442 pipeline->FlushUITasks();
3443 return false;
3444 }
3445
3446 return true;
3447 }
3448
IsLayout(int32_t index,std::optional<int32_t> indexInGroup,ScrollAlign align)3449 bool ListPattern::IsLayout(int32_t index, std::optional<int32_t> indexInGroup, ScrollAlign align)
3450 {
3451 auto pipeline = GetContext();
3452 CHECK_NULL_RETURN(pipeline, false);
3453 auto iter = itemPosition_.find(index);
3454 if (iter == itemPosition_.end()) {
3455 return false;
3456 }
3457 float targetPos = 0.0f;
3458 if (iter->second.isGroup) {
3459 if (indexInGroup.has_value()) {
3460 if (!GetListItemGroupAnimatePosWithIndexInGroup(
3461 index, indexInGroup.value(), iter->second.startPos, align, targetPos)) {
3462 return false;
3463 }
3464 } else {
3465 if (!GetListItemGroupAnimatePosWithoutIndexInGroup(
3466 index, iter->second.startPos, iter->second.endPos, align, targetPos)) {
3467 return false;
3468 }
3469 }
3470 }
3471 return true;
3472 }
3473
GetNextMoveStepForMultiLanes(int32_t curIndex,FocusStep focusStep,bool isVertical,int32_t & nextIndex)3474 int32_t ListPattern::GetNextMoveStepForMultiLanes(
3475 int32_t curIndex, FocusStep focusStep, bool isVertical, int32_t& nextIndex)
3476 {
3477 // Only for DetermineMultiLaneStep.
3478 // In mixed layout scenarios(List include ListItem and ListItemGroup), when increment nextIndex, if during the loop
3479 // a ListItemGroup is encountered, move to this ListItemGroup directly.
3480
3481 // Check if focusStep is within a valid range.
3482 bool isDown = true;
3483 if ((isVertical && (focusStep == FocusStep::DOWN)) || (!isVertical && focusStep == FocusStep::RIGHT)) {
3484 isDown = true;
3485 } else if ((isVertical && focusStep == FocusStep::UP) || (!isVertical && focusStep == FocusStep::LEFT)) {
3486 isDown = false;
3487 } else {
3488 return -1;
3489 }
3490
3491 // Determine moveStep and loop range based on focusStep.
3492 int32_t moveStep = isDown ? lanes_ : -lanes_;
3493 int32_t loopStart = isDown ? curIndex + 1 : curIndex - 1;
3494 int32_t loopEnd = isDown ? curIndex + lanes_ - 1 : curIndex - lanes_ + 1;
3495 int32_t loopStep = isDown ? 1 : -1;
3496
3497 for (int32_t loopIndex = loopStart; loopStep > 0 ? loopIndex <= loopEnd : loopIndex >= loopEnd;
3498 loopIndex += loopStep) {
3499 // Check if the index exists in itemPosition_ and cachedItemPosition_.
3500 auto it = itemPosition_.find(loopIndex);
3501 auto itCache = cachedItemPosition_.find(loopIndex);
3502 if (it == itemPosition_.end() && itCache == cachedItemPosition_.end()) {
3503 LayoutListForFocus(loopIndex, std::nullopt);
3504 it = itemPosition_.find(loopIndex);
3505 itCache = cachedItemPosition_.find(loopIndex);
3506 if (it == itemPosition_.end() && itCache == cachedItemPosition_.end()) {
3507 continue;
3508 }
3509 }
3510
3511 // Check if the index corresponds to a ListItemGroup.
3512 if ((it != itemPosition_.end() && it->second.isGroup) ||
3513 (itCache != cachedItemPosition_.end() && itCache->second.isGroup)) {
3514 nextIndex = loopIndex;
3515 return (focusStep == FocusStep::DOWN || focusStep == FocusStep::RIGHT) ? 1 : -1;
3516 }
3517 }
3518 nextIndex = curIndex + moveStep;
3519 return moveStep;
3520 }
3521
GetNextFocusNodeInList(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)3522 WeakPtr<FocusHub> ListPattern::GetNextFocusNodeInList(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
3523 {
3524 auto curFocus = currentFocusNode.Upgrade();
3525 CHECK_NULL_RETURN(curFocus, nullptr);
3526 auto curFrame = curFocus->GetFrameNode();
3527 CHECK_NULL_RETURN(curFrame, nullptr);
3528 auto curPattern = curFrame->GetPattern();
3529 CHECK_NULL_RETURN(curPattern, nullptr);
3530 auto curIndex = GetCurrentFocusIndex(curPattern);
3531
3532 int32_t moveStep = 0;
3533 int32_t nextIndex = curIndex;
3534 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
3535 CHECK_NULL_RETURN(listProperty, nullptr);
3536 auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
3537
3538 AdjustFocusStepForRtl(step, isVertical);
3539
3540 if (lanes_ <= 1) {
3541 DetermineSingleLaneStep(step, isVertical, curIndex, moveStep, nextIndex);
3542 } else {
3543 DetermineMultiLaneStep(step, isVertical, curIndex, moveStep, nextIndex);
3544 }
3545
3546 bool loopFlag = true;
3547 while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
3548 if (nextIndex == curIndex) {
3549 return nullptr;
3550 }
3551 LayoutListForFocus(nextIndex, std::nullopt);
3552 auto nextFocusNode = FindChildFocusNodeByIndex(nextIndex, step, curIndex);
3553 auto isDefault = GetFocusWrapMode() == FocusWrapMode::DEFAULT;
3554 if (nextFocusNode.Upgrade()) {
3555 const ListItemInfo* curPos = GetPosition(curIndex);
3556 const ListItemInfo* nextPos = GetPosition(nextIndex);
3557 const bool isForward = (isVertical && step == FocusStep::RIGHT) || (!isVertical && step == FocusStep::DOWN);
3558 const bool isBackward = (isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP);
3559 if ((isForward || isBackward) && NextPositionBlocksMove(curPos, nextPos, isVertical) && isDefault) {
3560 return nullptr;
3561 }
3562 // Scroll and display the ListItem.
3563 if (IsListItem(nextFocusNode)) {
3564 AdjustScrollPosition(nextIndex, curIndex);
3565 }
3566 return nextFocusNode;
3567 }
3568 nextIndex += moveStep;
3569 HandleIndexToBounds(nextIndex, loopFlag);
3570 }
3571
3572 return nullptr;
3573 }
3574
IsListItemGroupByIndex(int32_t index)3575 bool ListPattern::IsListItemGroupByIndex(int32_t index)
3576 {
3577 if (index < 0) {
3578 return false;
3579 }
3580 auto list = GetHost();
3581 CHECK_NULL_RETURN(list, false);
3582 auto layoutWapper = list->GetChildByIndex(static_cast<uint32_t>(index));
3583 CHECK_NULL_RETURN(layoutWapper, false);
3584 auto frameNode = layoutWapper->GetHostNode();
3585 CHECK_NULL_RETURN(frameNode, false);
3586 return frameNode && frameNode->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
3587 }
3588
JudgeFocusStep(int32_t tarMainIndex,const FocusStep & step,int32_t curFocusIndex,FocusWrapMode focusWrapMode,bool isVertical)3589 FocusStep static JudgeFocusStep(
3590 int32_t tarMainIndex, const FocusStep& step, int32_t curFocusIndex, FocusWrapMode focusWrapMode, bool isVertical)
3591 {
3592 if (isVertical && focusWrapMode == FocusWrapMode::WRAP_WITH_ARROW) {
3593 if (curFocusIndex < tarMainIndex && step == FocusStep::RIGHT) {
3594 return FocusStep::UP_END;
3595 } else if (curFocusIndex > tarMainIndex && step == FocusStep::LEFT) {
3596 return FocusStep::DOWN_END;
3597 }
3598 }
3599 if (!isVertical && focusWrapMode == FocusWrapMode::WRAP_WITH_ARROW) {
3600 if (curFocusIndex < tarMainIndex && step == FocusStep::DOWN) {
3601 return FocusStep::LEFT_END;
3602 } else if (curFocusIndex > tarMainIndex && step == FocusStep::UP) {
3603 return FocusStep::RIGHT_END;
3604 }
3605 }
3606 return step;
3607 }
3608
FindChildFocusNodeByIndex(int32_t tarMainIndex,const FocusStep & step,int32_t curFocusIndex)3609 WeakPtr<FocusHub> ListPattern::FindChildFocusNodeByIndex(
3610 int32_t tarMainIndex, const FocusStep& step, int32_t curFocusIndex)
3611 {
3612 auto focusWrapMode = GetFocusWrapMode();
3613 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
3614 auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
3615 CHECK_NULL_RETURN(listProperty, nullptr);
3616 // Only for GetNextFocusNodeInList.
3617 auto listFrame = GetHost();
3618 CHECK_NULL_RETURN(listFrame, nullptr);
3619 auto listFocus = listFrame->GetFocusHub();
3620 CHECK_NULL_RETURN(listFocus, nullptr);
3621 WeakPtr<FocusHub> target;
3622 listFocus->AnyChildFocusHub([&target, tarMainIndex, step, curFocusIndex, focusWrapMode, isVertical](
3623 const RefPtr<FocusHub>& childFocus) {
3624 if (!childFocus->IsFocusable()) {
3625 return false;
3626 }
3627 auto childFrame = childFocus->GetFrameNode();
3628 if (!childFrame) {
3629 return false;
3630 }
3631 auto childPattern = childFrame->GetPattern();
3632 if (!childPattern) {
3633 return false;
3634 }
3635 auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
3636 if (!childItemPattern) {
3637 auto childItemGroupPattern = AceType::DynamicCast<ListItemGroupPattern>(childPattern);
3638 CHECK_NULL_RETURN(childItemGroupPattern, false);
3639 if (childItemGroupPattern->GetIndexInList() == tarMainIndex) {
3640 auto tempStep = JudgeFocusStep(tarMainIndex, step, curFocusIndex, focusWrapMode, isVertical);
3641 bool isFindTailOrHead = childItemGroupPattern->FindHeadOrTailChild(childFocus, tempStep, target);
3642 target = isFindTailOrHead ? target : childFocus;
3643 return true;
3644 }
3645 return false;
3646 }
3647 auto curIndex = childItemPattern->GetIndexInList();
3648 if (curIndex == tarMainIndex) {
3649 auto isFindTailOrHead = childItemPattern->FindHeadOrTailChild(childFocus, step, target);
3650 target = !isFindTailOrHead ? childFocus : target;
3651 return true;
3652 }
3653 return false;
3654 });
3655 return target;
3656 }
3657
IsForwardStep(FocusStep step,bool isVertical,bool isDefault)3658 bool ListPattern::IsForwardStep(FocusStep step, bool isVertical, bool isDefault)
3659 {
3660 if (step == FocusStep::TAB) {
3661 return true;
3662 }
3663 if (isVertical) {
3664 if ((step == FocusStep::DOWN) || (step == FocusStep::RIGHT && !isDefault)) {
3665 return true;
3666 }
3667 } else {
3668 if ((step == FocusStep::RIGHT) || (step == FocusStep::DOWN && !isDefault)) {
3669 return true;
3670 }
3671 }
3672 return false;
3673 }
3674
IsBackwardStep(FocusStep step,bool isVertical,bool isDefault)3675 bool ListPattern::IsBackwardStep(FocusStep step, bool isVertical, bool isDefault)
3676 {
3677 if (step == FocusStep::SHIFT_TAB) {
3678 return true;
3679 }
3680 if (isVertical) {
3681 if ((step == FocusStep::UP) || (step == FocusStep::LEFT && !isDefault)) {
3682 return true;
3683 }
3684 } else {
3685 if ((step == FocusStep::LEFT) || (step == FocusStep::UP && !isDefault)) {
3686 return true;
3687 }
3688 }
3689 return false;
3690 }
3691
DetermineSingleLaneStep(FocusStep step,bool isVertical,int32_t curIndex,int32_t & moveStep,int32_t & nextIndex)3692 void ListPattern::DetermineSingleLaneStep(
3693 FocusStep step, bool isVertical, int32_t curIndex, int32_t& moveStep, int32_t& nextIndex)
3694 {
3695 // Only for GetNextFocusNodeInList
3696 auto isDefault = GetFocusWrapMode() == FocusWrapMode::DEFAULT;
3697 if (step == FocusStep::UP_END || step == FocusStep::LEFT_END) {
3698 moveStep = 1;
3699 nextIndex = 0;
3700 // Clear focus retention flag when HOME/END is pressed
3701 focusIndex_.reset();
3702 } else if (step == FocusStep::DOWN_END || step == FocusStep::RIGHT_END) {
3703 moveStep = -1;
3704 nextIndex = maxListItemIndex_;
3705 } else if (IsForwardStep(step, isVertical, isDefault)) {
3706 moveStep = 1;
3707 nextIndex = curIndex + moveStep;
3708 } else if (IsBackwardStep(step, isVertical, isDefault)) {
3709 moveStep = -1;
3710 nextIndex = curIndex + moveStep;
3711 }
3712 }
3713
DetermineMultiLaneStep(FocusStep step,bool isVertical,int32_t curIndex,int32_t & moveStep,int32_t & nextIndex)3714 void ListPattern::DetermineMultiLaneStep(
3715 FocusStep step, bool isVertical, int32_t curIndex, int32_t& moveStep, int32_t& nextIndex)
3716 {
3717 // Only for GetNextFocusNodeInList.
3718 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
3719 moveStep = 1;
3720 nextIndex = 0;
3721 // Clear focus retention flag when HOME/END is pressed
3722 focusIndex_.reset();
3723 } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
3724 moveStep = -1;
3725 nextIndex = maxListItemIndex_;
3726 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
3727 if (IsListItemGroupByIndex(curIndex)) {
3728 // If the current item is a ListItemGroupPattern, the moveStep must be 1.
3729 moveStep = 1;
3730 nextIndex = nextIndex + moveStep;
3731 } else {
3732 // If the current item is a ListItem, the moveStep needs to be determined.
3733 // In the next step, sequentially check up to lanes-1. If a ListItemGroup is encountered, move only one
3734 // step. Otherwise, proceed according to the lanes.
3735 moveStep = GetNextMoveStepForMultiLanes(curIndex, step, isVertical, nextIndex);
3736 }
3737 nextIndex = AdjustNextIndexForEdgeRow(nextIndex, moveStep, curIndex);
3738 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
3739 if (IsListItemGroupByIndex(curIndex)) {
3740 // If the current item is a ListItemGroupPattern, the moveStep must be 1.
3741 moveStep = -1;
3742 nextIndex = curIndex + moveStep;
3743 } else {
3744 moveStep = GetNextMoveStepForMultiLanes(curIndex, step, isVertical, nextIndex);
3745 }
3746 } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
3747 moveStep = 1;
3748 nextIndex = GetCrossAxisNextIndex(curIndex, isVertical, moveStep, step);
3749 } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
3750 moveStep = -1;
3751 nextIndex = GetCrossAxisNextIndex(curIndex, isVertical, moveStep, step);
3752 } else if (step == FocusStep::TAB) {
3753 moveStep = 1;
3754 nextIndex = curIndex + 1;
3755 } else if (step == FocusStep::SHIFT_TAB) {
3756 moveStep = -1;
3757 nextIndex = curIndex - 1;
3758 }
3759 }
3760
GetCurrentFocusIndex(const RefPtr<Pattern> & curPattern)3761 int32_t ListPattern::GetCurrentFocusIndex(const RefPtr<Pattern>& curPattern)
3762 {
3763 auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
3764 if (!curItemPattern) {
3765 auto curItemGroupPattern = AceType::DynamicCast<ListItemGroupPattern>(curPattern);
3766 CHECK_NULL_RETURN(curItemGroupPattern, -1);
3767 return curItemGroupPattern->GetIndexInList();
3768 }
3769 return curItemPattern->GetIndexInList();
3770 }
3771
AdjustFocusStepForRtl(FocusStep & step,bool isVertical)3772 void ListPattern::AdjustFocusStepForRtl(FocusStep& step, bool isVertical)
3773 {
3774 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
3775 bool isRtl = false;
3776 if (listLayoutProperty) {
3777 isRtl = TextDirection::RTL == listLayoutProperty->GetNonAutoLayoutDirection();
3778 }
3779
3780 // FocusStep LEFT/RIGH reverse flag
3781 bool reverseHorizontal = false;
3782 // FocusStep UP/DOWN reverse flag
3783 bool reverseVertical = false;
3784
3785 if ((!isRtl && !isVertical && isStackFromEnd_)) {
3786 reverseVertical = true;
3787 } else if ((!isRtl && isVertical && isStackFromEnd_) || (isRtl && !isStackFromEnd_)) {
3788 reverseHorizontal = true;
3789 } else if (isRtl && !isVertical && isStackFromEnd_) {
3790 reverseHorizontal = true;
3791 reverseVertical = true;
3792 }
3793
3794 if (reverseHorizontal) {
3795 if (step == FocusStep::LEFT) {
3796 step = FocusStep::RIGHT;
3797 } else if (step == FocusStep::RIGHT) {
3798 step = FocusStep::LEFT;
3799 }
3800 }
3801
3802 if (reverseVertical) {
3803 if (step == FocusStep::UP) {
3804 step = FocusStep::DOWN;
3805 } else if (step == FocusStep::DOWN) {
3806 step = FocusStep::UP;
3807 }
3808 }
3809 }
3810
IsListItem(const WeakPtr<FocusHub> & focusNode)3811 bool ListPattern::IsListItem(const WeakPtr<FocusHub>& focusNode)
3812 {
3813 // Determine if it is a list item.
3814 auto focusHub = focusNode.Upgrade();
3815 CHECK_NULL_RETURN(focusHub, false);
3816 auto frameNode = focusHub->GetFrameNode();
3817 return frameNode &&
3818 (frameNode->GetTag() == V2::LIST_ITEM_ETS_TAG || frameNode->GetTag() == V2::ARC_LIST_ITEM_ETS_TAG);
3819 }
3820
AdjustScrollPosition(int32_t nextIndex,int32_t curIndex)3821 void ListPattern::AdjustScrollPosition(int32_t nextIndex, int32_t curIndex)
3822 {
3823 // Only for GetNextFocusNodeInList
3824 // Adjust the scroll position.
3825 if (nextIndex < startIndex_) {
3826 ScrollToIndex(nextIndex, false, ScrollAlign::START);
3827 } else if (nextIndex > endIndex_) {
3828 ScrollToIndex(nextIndex, false, ScrollAlign::END);
3829 } else {
3830 HandleDisplayedChildFocus(nextIndex, curIndex);
3831 }
3832 }
3833
HandleIndexToBounds(int32_t & nextIndex,bool & loopFlag)3834 void ListPattern::HandleIndexToBounds(int32_t& nextIndex, bool& loopFlag)
3835 {
3836 // Only for GetNextFocusNodeInList.
3837 // Handle boundary conditions.
3838 if ((nextIndex < 0 || nextIndex > maxListItemIndex_) && loopFlag) {
3839 loopFlag = false; // Single loop protection.
3840 nextIndex = (nextIndex < 0) ? 0 : maxListItemIndex_;
3841 }
3842 }
3843
GetCrossAxisNextIndex(int32_t curIndex,bool isVertical,int32_t moveStep,FocusStep step)3844 int32_t ListPattern::GetCrossAxisNextIndex(int32_t curIndex, bool isVertical, int32_t moveStep, FocusStep step)
3845 {
3846 // Only for DetermineMultiLaneStep
3847 auto focusWrapMode = GetFocusWrapMode();
3848 int32_t nextIndex = curIndex + moveStep;
3849 const bool isForward = (isVertical && step == FocusStep::RIGHT) || (!isVertical && step == FocusStep::DOWN);
3850 const bool isBackward = (isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP);
3851
3852 if (focusWrapMode == FocusWrapMode::WRAP_WITH_ARROW) {
3853 return nextIndex;
3854 }
3855
3856 if (!isForward && !isBackward) {
3857 return nextIndex;
3858 }
3859
3860 const bool isGroup = IsListItemGroupByIndex(curIndex);
3861 if (isGroup) {
3862 // Currently at a group or the last column, do not move.
3863 return curIndex;
3864 }
3865
3866 // Check if the next position is a group or in the same column.
3867 const ListItemInfo* curPos = GetPosition(curIndex);
3868 const ListItemInfo* nextPos = GetPosition(nextIndex);
3869 if (NextPositionBlocksMove(curPos, nextPos, isVertical)) {
3870 return curIndex;
3871 }
3872 return nextIndex;
3873 }
3874
GetPosition(int32_t index) const3875 const ListItemInfo* ListPattern::GetPosition(int32_t index) const
3876 {
3877 // Only for GetCrossAxisNextIndex
3878 auto it = itemPosition_.find(index);
3879 if (it != itemPosition_.end()) {
3880 return &it->second;
3881 }
3882 auto cachedIt = cachedItemPosition_.find(index);
3883 return (cachedIt != cachedItemPosition_.end()) ? &cachedIt->second : nullptr;
3884 }
3885
NextPositionBlocksMove(const ListItemInfo * curPos,const ListItemInfo * nextPos,bool isVertical) const3886 bool ListPattern::NextPositionBlocksMove(const ListItemInfo* curPos, const ListItemInfo* nextPos, bool isVertical) const
3887 {
3888 // Only for GetCrossAxisNextIndex, determine if the next position blocks movement.
3889
3890 if (!nextPos) {
3891 // No position information, allow movement (or handle externally).
3892 return false;
3893 }
3894 if (nextPos->isGroup) {
3895 // The next position is a group, block the movement.
3896 return true;
3897 }
3898
3899 // Check if the current and next positions are in the same column.
3900 // If the endPos and startPos of two items are the same, it indicates they are in the same row or column, allowing
3901 // focus movement; otherwise, it is considered to have reached the first column (row) or the last column (row),
3902 // disallowing focus movement.
3903 return curPos && (!NearEqual(curPos->endPos, nextPos->endPos) && !NearEqual(curPos->startPos, nextPos->startPos));
3904 }
3905
AdjustNextIndexForEdgeRow(int32_t nextIndex,int32_t moveStep,int32_t curIndex)3906 int32_t ListPattern::AdjustNextIndexForEdgeRow(int32_t nextIndex, int32_t moveStep, int32_t curIndex)
3907 {
3908 // Only for DetermineMultiLaneStep
3909 if (nextIndex >= maxListItemIndex_ && moveStep == lanes_) {
3910 auto row = maxListItemIndex_ / lanes_;
3911 auto curRow = curIndex / lanes_;
3912 if (curRow == row - 1) {
3913 return maxListItemIndex_;
3914 }
3915 }
3916 return nextIndex;
3917 }
3918
GetScopeFocusAlgorithm()3919 ScopeFocusAlgorithm ListPattern::GetScopeFocusAlgorithm()
3920 {
3921 auto property = GetLayoutProperty<ListLayoutProperty>();
3922 if (!property) {
3923 return {};
3924 }
3925 return ScopeFocusAlgorithm(property->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL, true,
3926 ScopeType::OTHERS,
3927 [wp = WeakClaim(this)](
3928 FocusStep step, const WeakPtr<FocusHub>& currFocusNode, WeakPtr<FocusHub>& nextFocusNode) -> bool {
3929 auto list = wp.Upgrade();
3930 if (list) {
3931 nextFocusNode = list->GetNextFocusNodeInList(step, currFocusNode);
3932 }
3933 return nextFocusNode.Upgrade() != currFocusNode.Upgrade();
3934 });
3935 }
3936
UpdateDefaultColor()3937 void ListPattern::UpdateDefaultColor()
3938 {
3939 auto host = GetHost();
3940 CHECK_NULL_VOID(host);
3941 auto pipeline = host->GetContextWithCheck();
3942 CHECK_NULL_VOID(pipeline);
3943 auto theme = pipeline->GetTheme<ListTheme>();
3944 CHECK_NULL_VOID(theme);
3945 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
3946 CHECK_NULL_VOID(listLayoutProperty);
3947 if (listLayoutProperty->GetDivider()->color != Color::TRANSPARENT &&
3948 (!listLayoutProperty->HasDividerColorSetByUser() ||
3949 !listLayoutProperty->GetDividerColorSetByUserValue(false))) {
3950 V2::ItemDivider divider = listLayoutProperty->GetDivider().value_or(V2::ItemDivider());
3951 divider.color = theme->GetDividerColor();
3952 listLayoutProperty->UpdateDivider(divider);
3953 }
3954 }
3955
OnColorModeChange(uint32_t colorMode)3956 void ListPattern::OnColorModeChange(uint32_t colorMode)
3957 {
3958 Pattern::OnColorModeChange(colorMode);
3959 auto host = GetHost();
3960 CHECK_NULL_VOID(host);
3961 CHECK_NULL_VOID(SystemProperties::ConfigChangePerform());
3962 UpdateDefaultColor();
3963 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
3964 CHECK_NULL_VOID(paintProperty);
3965 if (paintProperty->GetScrollBarProperty()) {
3966 SetScrollBar(paintProperty->GetScrollBarProperty());
3967 }
3968 host->MarkDirtyNode(PROPERTY_UPDATE_NORMAL);
3969 }
3970
OnMidIndexChanged()3971 void ListPattern::OnMidIndexChanged()
3972 {
3973 #ifdef SUPPORT_DIGITAL_CROWN
3974 if (!GetCrownEventDragging()) {
3975 return;
3976 }
3977 VibratorUtils::StartVibraFeedback(HAPTIC_STRENGTH1);
3978 #endif
3979 }
3980
ReportOnItemListEvent(const std::string & event)3981 void ListPattern::ReportOnItemListEvent(const std::string& event)
3982 {
3983 auto host = GetHost();
3984 CHECK_NULL_VOID(host);
3985 std::string value = std::string("List.") + event;
3986 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", value);
3987 TAG_LOGI(AceLogTag::ACE_LIST, "nodeId:[%{public}d] List reportComponentChangeEvent %{public}s", host->GetId(),
3988 event.c_str());
3989 }
3990
ReportOnItemListScrollEvent(const std::string & event,int32_t startindex,int32_t endindex)3991 void ListPattern::ReportOnItemListScrollEvent(const std::string& event, int32_t startindex, int32_t endindex)
3992 {
3993 auto host = GetHost();
3994 CHECK_NULL_VOID(host);
3995 std::string value = std::string("List.") + event;
3996
3997 auto params = JsonUtil::Create();
3998 CHECK_NULL_VOID(params);
3999 params->Put("StartX", startindex);
4000 params->Put("StartY", endindex);
4001
4002 auto eventData = JsonUtil::Create();
4003 CHECK_NULL_VOID(eventData);
4004 eventData->Put("name", value.c_str());
4005 eventData->Put("params", params);
4006
4007 auto json = JsonUtil::Create();
4008 CHECK_NULL_VOID(json);
4009 json->Put("nodeId", host->GetId());
4010 json->Put("event", eventData);
4011
4012 auto result = JsonUtil::Create();
4013 CHECK_NULL_VOID(result);
4014 result->Put("result", json);
4015
4016 UiSessionManager::GetInstance()->ReportComponentChangeEvent("result", result->ToString());
4017 TAG_LOGI(AceLogTag::ACE_LIST,
4018 "nodeId:[%{public}d] List reportComponentChangeEvent %{public}s startindex:%{public}d endindex:%{public}d",
4019 host->GetId(), event.c_str(), startindex, endindex);
4020 }
4021
OnInjectionEvent(const std::string & command)4022 int32_t ListPattern::OnInjectionEvent(const std::string& command)
4023 {
4024 TAG_LOGI(AceLogTag::ACE_LIST, "OnInjectionEvent command: %{public}s", command.c_str());
4025
4026 std::string ret = ScrollablePattern::ParseCommand(command);
4027 if (ret == "scrollForward") {
4028 ScrollPage(true);
4029 } else if (ret == "scrollBackward") {
4030 ScrollPage(false);
4031 } else {
4032 return RET_FAILED;
4033 }
4034 return RET_SUCCESS;
4035 }
4036
SetFocusWrapMode(FocusWrapMode focusWrapMode)4037 void ListPattern::SetFocusWrapMode(FocusWrapMode focusWrapMode)
4038 {
4039 if (focusWrapMode != FocusWrapMode::DEFAULT && focusWrapMode != FocusWrapMode::WRAP_WITH_ARROW) {
4040 focusWrapMode_ = FocusWrapMode::DEFAULT;
4041 } else {
4042 focusWrapMode_ = focusWrapMode;
4043 }
4044 }
4045
GetFocusWrapMode() const4046 FocusWrapMode ListPattern::GetFocusWrapMode() const
4047 {
4048 return focusWrapMode_;
4049 }
4050
ScrollToLastFocusIndex(const KeyEvent & event)4051 bool ListPattern::ScrollToLastFocusIndex(const KeyEvent& event)
4052 {
4053 auto pipeline = GetContext();
4054 CHECK_NULL_RETURN(pipeline, false);
4055 CHECK_NULL_RETURN(pipeline->GetIsFocusActive(), false);
4056 auto host = GetHost();
4057 CHECK_NULL_RETURN(host, false);
4058 auto focusHub = host->GetFocusHub();
4059 CHECK_NULL_RETURN(focusHub, false);
4060 CHECK_NULL_RETURN(focusHub->IsCurrentFocus(), false);
4061 CHECK_NULL_RETURN(focusIndex_, false);
4062 auto focusIndex = focusIndex_.value();
4063 auto focusGroupIndex = focusGroupIndex_.value_or(-1);
4064 keyEvent_ = event;
4065 if (!IsInViewport(focusIndex) || !GetIsInViewInGroup(focusIndex, focusGroupIndex)) {
4066 StopAnimate();
4067 return UpdateStartIndex(focusIndex, focusGroupIndex);
4068 }
4069 return false;
4070 }
4071
IsInViewport(int32_t index) const4072 bool ListPattern::IsInViewport(int32_t index) const
4073 {
4074 return index >= startIndex_ && index <= endIndex_;
4075 }
4076
CheckValidInList(int32_t index)4077 bool ListPattern::CheckValidInList(int32_t index)
4078 {
4079 return index >= 0 && index <= maxListItemIndex_;
4080 }
4081
AdjustFocusGroupIndex(int32_t index,int32_t & indexInGroup)4082 void ListPattern::AdjustFocusGroupIndex(int32_t index, int32_t& indexInGroup)
4083 {
4084 if (!CheckValidInList(index) || indexInGroup < 0) {
4085 return;
4086 }
4087
4088 auto host = GetHost();
4089 CHECK_NULL_VOID(host);
4090 auto child = host->GetOrCreateChildByIndex(index);
4091 CHECK_NULL_VOID(child);
4092 auto childNode = child->GetHostNode();
4093 CHECK_NULL_VOID(childNode);
4094 auto itemInGroup = AceType::DynamicCast<ListItemGroupPattern>(childNode->GetPattern());
4095 CHECK_NULL_VOID(itemInGroup);
4096
4097 auto totalCount = child->GetTotalChildCount();
4098 auto footerCount = itemInGroup->GetFooter() ? 1 : 0;
4099 auto headerCount = itemInGroup->GetHeader() ? 1 : 0;
4100 if (indexInGroup >= totalCount - footerCount - headerCount) {
4101 indexInGroup = totalCount - 1;
4102 }
4103 if (indexInGroup < 0) {
4104 indexInGroup = 0;
4105 }
4106 focusGroupIndex_ = indexInGroup;
4107 }
4108
UpdateStartIndex(int32_t index,int32_t indexInGroup)4109 bool ListPattern::UpdateStartIndex(int32_t index, int32_t indexInGroup)
4110 {
4111 if (!CheckValidInList(index)) {
4112 return false;
4113 }
4114 jumpIndex_ = index;
4115
4116 if (focusGroupIndex_.has_value() && indexInGroup >= 0) {
4117 jumpIndexInGroup_ = indexInGroup;
4118 }
4119
4120 auto host = GetHost();
4121 CHECK_NULL_RETURN(host, false);
4122 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
4123 SetScrollSource(SCROLL_FROM_FOCUS_JUMP);
4124
4125 auto pipeline = host->GetContext();
4126 CHECK_NULL_RETURN(pipeline, false);
4127 pipeline->FlushUITasks();
4128 RequestFocusForItem(index, focusGroupIndex_.has_value() && indexInGroup >= 0 ? indexInGroup : -1);
4129 auto child = host->GetChildByIndex(focusIndex_.value_or(-1));
4130 if (child && focusGroupIndex_.has_value()) {
4131 auto childNode = child->GetHostNode();
4132 CHECK_NULL_RETURN(childNode, false);
4133 auto itemInGroup = AceType::DynamicCast<ListItemGroupPattern>(childNode->GetPattern());
4134 CHECK_NULL_RETURN(itemInGroup, false);
4135 auto itemInGroupFocusHub = childNode->GetFocusHub();
4136 CHECK_NULL_RETURN(itemInGroupFocusHub, false);
4137 return itemInGroupFocusHub->GetNextFocusByStep(keyEvent_);
4138 }
4139 return false;
4140 }
4141
ProcessFocusEvent(bool indexChanged)4142 void ListPattern::ProcessFocusEvent(bool indexChanged)
4143 {
4144 if (indexChanged && focusIndex_.has_value()) {
4145 FireFocus();
4146 }
4147 }
4148
HandleFocusParentCheck(const RefPtr<FocusHub> & childFocusHub,const RefPtr<FocusHub> & focusHub)4149 void ListPattern::HandleFocusParentCheck(const RefPtr<FocusHub>& childFocusHub, const RefPtr<FocusHub>& focusHub)
4150 {
4151 CHECK_NULL_VOID(focusHub);
4152 CHECK_NULL_VOID(childFocusHub);
4153 auto child = childFocusHub->GetFrameNode();
4154 CHECK_NULL_VOID(child);
4155 auto childNode = child->GetHostNode();
4156 CHECK_NULL_VOID(childNode);
4157 auto parentNode = childNode->GetParent();
4158 while (parentNode && !AceType::InstanceOf<FrameNode>(parentNode)) {
4159 if (AceType::InstanceOf<LazyForEachNode>(parentNode) ||
4160 AceType::InstanceOf<RepeatVirtualScroll2Node>(parentNode) ||
4161 AceType::InstanceOf<RepeatVirtualScrollNode>(parentNode)) {
4162 focusHub->LostChildFocusToSelf();
4163 return;
4164 }
4165 parentNode = parentNode->GetParent();
4166 }
4167 }
4168
FireFocus()4169 void ListPattern::FireFocus()
4170 {
4171 CHECK_NULL_VOID(focusIndex_);
4172 auto host = GetHost();
4173 CHECK_NULL_VOID(host);
4174 auto focusHub = host->GetFocusHub();
4175 CHECK_NULL_VOID(focusHub);
4176 CHECK_NULL_VOID(focusHub->IsCurrentFocus());
4177
4178 auto childFocusHub = focusHub->GetLastWeakFocusNode().Upgrade();
4179 if (childFocusHub && childFocusHub->IsCurrentFocus()) {
4180 // If the focus is on the group's header or footer, clear the focus retention state.
4181 // Otherwise, after user scrolling, the focus will return to the previously retained list item.
4182 if (CheckFocusOnHeaderOrFooter(childFocusHub)) {
4183 focusIndex_.reset();
4184 focusGroupIndex_.reset();
4185 return;
4186 }
4187 }
4188
4189 if (IsInViewport(focusIndex_.value())) {
4190 if (focusGroupIndex_.has_value()) {
4191 auto groupWrapper = host->GetChildByIndex(focusIndex_.value());
4192 CHECK_NULL_VOID(groupWrapper);
4193 auto groupNode = groupWrapper->GetHostNode();
4194 CHECK_NULL_VOID(groupNode);
4195 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
4196 CHECK_NULL_VOID(groupPattern);
4197 FireFocusInListItemGroup(focusIndex_.value());
4198 return;
4199 }
4200 auto child = host->GetChildByIndex(focusIndex_.value());
4201 CHECK_NULL_VOID(child);
4202 auto childNode = child->GetHostNode();
4203 CHECK_NULL_VOID(childNode);
4204 auto childFocusHub = childNode->GetFocusHub();
4205 CHECK_NULL_VOID(childFocusHub);
4206 if (!childFocusHub->IsCurrentFocus()) {
4207 ResetFocusIndex();
4208 focusHub->SetFocusDependence(FocusDependence::AUTO);
4209 childFocusHub->RequestFocusImmediately(FocusReason::FOCUS_TRAVEL);
4210 }
4211 } else {
4212 auto childFocusHub = focusHub->GetLastWeakFocusNode().Upgrade();
4213 CHECK_NULL_VOID(childFocusHub);
4214 if (childFocusHub->IsCurrentFocus()) {
4215 HandleFocusParentCheck(childFocusHub, focusHub);
4216 }
4217 }
4218 }
4219
CheckFocusOnHeaderOrFooter(const RefPtr<FocusHub> & childFocusHub)4220 bool ListPattern::CheckFocusOnHeaderOrFooter(const RefPtr<FocusHub>& childFocusHub)
4221 {
4222 CHECK_NULL_RETURN(childFocusHub, false);
4223 auto groupNode = childFocusHub->GetFrameNode();
4224 CHECK_NULL_RETURN(groupNode, false);
4225 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
4226 CHECK_NULL_RETURN(groupPattern, false);
4227
4228 auto focus = childFocusHub->GetLastWeakFocusNode().Upgrade();
4229 CHECK_NULL_RETURN(focus, false);
4230
4231 auto curFrame = focus->GetFrameNode();
4232 CHECK_NULL_RETURN(curFrame, false);
4233 auto curPattern = curFrame->GetPattern();
4234 CHECK_NULL_RETURN(curPattern, false);
4235 auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
4236 CHECK_NULL_RETURN(curFrame, false);
4237
4238 if (groupPattern->GetHeader() == curFrame || groupPattern->GetFooter() == curFrame) {
4239 return true;
4240 }
4241 return false;
4242 }
4243
GetChildFocusHubInGroup(int32_t indexInList,int32_t indexInListItemGroup) const4244 RefPtr<FocusHub> ListPattern::GetChildFocusHubInGroup(int32_t indexInList, int32_t indexInListItemGroup) const
4245 {
4246 auto host = GetHost();
4247 CHECK_NULL_RETURN(host, nullptr);
4248 auto groupWrapper = host->GetChildByIndex(indexInList);
4249 CHECK_NULL_RETURN(groupWrapper, nullptr);
4250 auto groupNode = groupWrapper->GetHostNode();
4251 CHECK_NULL_RETURN(groupNode, nullptr);
4252 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
4253 CHECK_NULL_RETURN(groupPattern, nullptr);
4254 auto itemWrapper = groupNode->GetOrCreateChildByIndex(indexInListItemGroup + groupPattern->GetItemStartIndex());
4255 CHECK_NULL_RETURN(itemWrapper, nullptr);
4256 auto itemNode = itemWrapper->GetHostNode();
4257 CHECK_NULL_RETURN(itemNode, nullptr);
4258 return itemNode->GetFocusHub();
4259 }
4260
FireFocusInListItemGroup(int32_t groupIndexInList)4261 void ListPattern::FireFocusInListItemGroup(int32_t groupIndexInList)
4262 {
4263 CHECK_NULL_VOID(focusIndex_);
4264 CHECK_NULL_VOID(focusGroupIndex_);
4265 if (groupIndexInList != focusIndex_.value()) {
4266 return;
4267 }
4268
4269 auto host = GetHost();
4270 CHECK_NULL_VOID(host);
4271 auto focusHub = host->GetFocusHub();
4272 CHECK_NULL_VOID(focusHub);
4273 CHECK_NULL_VOID(focusHub->IsCurrentFocus());
4274 CHECK_NULL_VOID(focusGroupIndex_);
4275 if (GetIsInViewInGroup(focusIndex_.value(), focusGroupIndex_.value())) {
4276 auto childFocusHub = GetChildFocusHubInGroup(focusIndex_.value(), focusGroupIndex_.value());
4277 CHECK_NULL_VOID(childFocusHub);
4278 if (!childFocusHub->IsCurrentFocus()) {
4279 ResetFocusIndex();
4280 ResetGroupFocusIndex();
4281 ResetGroupIndexChanged();
4282 focusHub->SetFocusDependence(FocusDependence::AUTO);
4283 childFocusHub->RequestFocusImmediately(FocusReason::FOCUS_TRAVEL);
4284 }
4285 } else {
4286 auto childFocusHub = focusHub->GetLastWeakFocusNode().Upgrade();
4287 CHECK_NULL_VOID(childFocusHub);
4288 if (childFocusHub->IsCurrentFocus()) {
4289 focusHub->LostChildFocusToSelf();
4290 }
4291 }
4292 }
4293
GetIsInViewInGroup(int32_t groupIndex,int32_t index)4294 bool ListPattern::GetIsInViewInGroup(int32_t groupIndex, int32_t index)
4295 {
4296 auto result = true;
4297 if (index < 0) {
4298 return result;
4299 }
4300 auto host = GetHost();
4301 CHECK_NULL_RETURN(host, true);
4302 auto startWrapper = host->GetChildByIndex(groupIndex);
4303 bool startIsGroup = startWrapper && startWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
4304 if (startIsGroup) {
4305 auto groupNode = startWrapper->GetHostNode();
4306 CHECK_NULL_RETURN(groupNode, true);
4307 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
4308 CHECK_NULL_RETURN(groupPattern, true);
4309 result = groupPattern->IsInViewport(index);
4310 }
4311 return result;
4312 }
4313
RequestFocusForItem(int32_t focusIndex,int32_t focusGroupIndex)4314 void ListPattern::RequestFocusForItem(int32_t focusIndex, int32_t focusGroupIndex)
4315 {
4316 auto host = GetHost();
4317 CHECK_NULL_VOID(host);
4318 auto child = host->GetChildByIndex(focusIndex);
4319 CHECK_NULL_VOID(child);
4320 auto childNode = child->GetHostNode();
4321 CHECK_NULL_VOID(childNode);
4322 if (focusGroupIndex >= 0) {
4323 auto itemInGroupFocusHub = GetChildFocusHubInGroup(focusIndex, focusGroupIndex);
4324 CHECK_NULL_VOID(itemInGroupFocusHub);
4325 if (!itemInGroupFocusHub->IsCurrentFocus() && itemInGroupFocusHub->IsFocusable()) {
4326 itemInGroupFocusHub->SetFocusDependence(FocusDependence::AUTO);
4327 itemInGroupFocusHub->RequestFocusImmediately();
4328 }
4329 } else {
4330 auto childFocusHub = childNode->GetFocusHub();
4331 CHECK_NULL_VOID(childFocusHub);
4332 if (!childFocusHub->IsCurrentFocus() && childFocusHub->IsFocusable()) {
4333 childFocusHub->SetFocusDependence(FocusDependence::AUTO);
4334 childFocusHub->RequestFocusImmediately();
4335 }
4336 }
4337 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
4338 }
4339
GetFocusNodeIndex(const RefPtr<FocusHub> & focusNode)4340 int32_t ListPattern::GetFocusNodeIndex(const RefPtr<FocusHub>& focusNode)
4341 {
4342 auto tarFrame = focusNode->GetFrameNode();
4343 CHECK_NULL_RETURN(tarFrame, -1);
4344 auto tarPattern = tarFrame->GetPattern();
4345 CHECK_NULL_RETURN(tarPattern, -1);
4346 auto tarItemPattern = AceType::DynamicCast<ListItemPattern>(tarPattern);
4347 if (!tarItemPattern) {
4348 auto tarGroupPattern = AceType::DynamicCast<ListItemGroupPattern>(tarPattern);
4349 CHECK_NULL_RETURN(tarGroupPattern, -1);
4350 return tarGroupPattern->GetIndexInList();
4351 }
4352 return tarItemPattern->GetIndexInList();
4353 }
4354
ScrollToFocusNodeIndex(int32_t index)4355 void ListPattern::ScrollToFocusNodeIndex(int32_t index)
4356 {
4357 int32_t indexInGroup = -1;
4358 if (focusIndex_.has_value()) {
4359 index = focusIndex_.value();
4360 indexInGroup = focusGroupIndex_.value_or(-1);
4361 }
4362
4363 StopAnimate();
4364 if (!IsInViewport(index) || !GetIsInViewInGroup(index, indexInGroup)) {
4365 auto host = GetHost();
4366 CHECK_NULL_VOID(host);
4367
4368 // If the group that needs to maintain focus is not mounted when DataChangeNotify occurs,
4369 // focusGroupIndex_ will not be updated. If the focus is on the last item of this group,
4370 // and that item is deleted, focusGroupIndex_ should be set to the new last item after deletion.
4371 AdjustFocusGroupIndex(index, indexInGroup);
4372 jumpIndex_ = index;
4373 if (indexInGroup >= 0) {
4374 jumpIndexInGroup_ = indexInGroup;
4375 }
4376
4377 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
4378 SetScrollSource(SCROLL_FROM_FOCUS_JUMP);
4379 auto pipeline = host->GetContext();
4380 if (pipeline) {
4381 pipeline->FlushUITasks();
4382 }
4383 }
4384 RequestFocusForItem(index, indexInGroup);
4385 }
4386
UpdateGroupFocusIndexForDataChange(int32_t groupIndexInList,int32_t indexInGroup,int32_t count)4387 void ListPattern::UpdateGroupFocusIndexForDataChange(int32_t groupIndexInList, int32_t indexInGroup, int32_t count)
4388 {
4389 if (focusIndex_.has_value() && focusIndex_.value() == groupIndexInList && focusGroupIndex_.has_value() &&
4390 focusGroupIndex_ >= indexInGroup) {
4391 focusGroupIndex_ = focusGroupIndex_.value() + count;
4392 if (focusGroupIndex_ < 0) {
4393 focusGroupIndex_ = 0;
4394 }
4395 }
4396 }
4397
ResetForExtScroll()4398 void ListPattern::ResetForExtScroll()
4399 {
4400 currentDelta_ = 0;
4401 }
4402 } // namespace OHOS::Ace::NG
4403