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