1 /*
2 * Copyright (c) 2022-2023 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 <string>
19
20 #include "base/geometry/axis.h"
21 #include "base/geometry/rect.h"
22 #include "base/log/dump_log.h"
23 #include "base/memory/referenced.h"
24 #include "base/perfmonitor/perf_constants.h"
25 #include "base/perfmonitor/perf_monitor.h"
26 #include "base/utils/utils.h"
27 #include "core/animation/bilateral_spring_node.h"
28 #include "core/animation/spring_model.h"
29 #include "core/common/container.h"
30 #include "core/components/common/layout/constants.h"
31 #include "core/components/scroll/scroll_bar_theme.h"
32 #include "core/components_ng/pattern/scrollable/scrollable.h"
33 #include "core/components_ng/pattern/list/list_height_offset_calculator.h"
34 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
35 #include "core/components_ng/pattern/list/list_item_pattern.h"
36 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
37 #include "core/components_ng/pattern/list/list_layout_algorithm.h"
38 #include "core/components_ng/pattern/list/list_layout_property.h"
39 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
40 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
41 #include "core/components_ng/property/measure_utils.h"
42 #include "core/components_ng/property/property.h"
43 #include "core/components_v2/inspector/inspector_constants.h"
44
45 namespace OHOS::Ace::NG {
46 namespace {
47 constexpr Dimension CHAIN_INTERVAL_DEFAULT = 20.0_vp;
48 constexpr double CHAIN_SPRING_MASS = 1.0;
49 constexpr double CHAIN_SPRING_DAMPING = 30.0;
50 constexpr double CHAIN_SPRING_STIFFNESS = 228;
51 constexpr float DEFAULT_MIN_SPACE_SCALE = 0.75f;
52 constexpr float DEFAULT_MAX_SPACE_SCALE = 2.0f;
53 } // namespace
54
OnModifyDone()55 void ListPattern::OnModifyDone()
56 {
57 if (!isInitialized_) {
58 jumpIndex_ = GetLayoutProperty<ListLayoutProperty>()->GetInitialIndex().value_or(0);
59 if (NeedScrollSnapAlignEffect()) {
60 scrollAlign_ = GetScrollAlignByScrollSnapAlign();
61 }
62 }
63 auto host = GetHost();
64 CHECK_NULL_VOID(host);
65 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
66 CHECK_NULL_VOID(listLayoutProperty);
67 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
68 if (axis != GetAxis()) {
69 SetAxis(axis);
70 ChangeAxis(GetHost());
71 }
72 if (!GetScrollableEvent()) {
73 InitScrollableEvent();
74 }
75
76 SetEdgeEffect();
77
78 auto paintProperty = GetPaintProperty<ScrollablePaintProperty>();
79 CHECK_NULL_VOID(paintProperty);
80 if (paintProperty->GetScrollBarProperty()) {
81 SetScrollBar(paintProperty->GetScrollBarProperty());
82 }
83
84 SetChainAnimation();
85 if (multiSelectable_ && !isMouseEventInit_) {
86 InitMouseEvent();
87 }
88 if (!multiSelectable_ && isMouseEventInit_) {
89 UninitMouseEvent();
90 }
91 auto focusHub = host->GetFocusHub();
92 CHECK_NULL_VOID(focusHub);
93 InitOnKeyEvent(focusHub);
94 Register2DragDropManager();
95 SetAccessibilityAction();
96 if (IsNeedInitClickEventRecorder()) {
97 Pattern::InitClickEventRecorder();
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 ChangeAxis(child);
117 }
118 } else {
119 ChangeAxis(child);
120 }
121 }
122 }
123
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)124 bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
125 {
126 if (config.skipMeasure && config.skipLayout) {
127 return false;
128 }
129 bool isJump = false;
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 maxListItemIndex_ = listLayoutAlgorithm->GetMaxListItemIndex();
136 spaceWidth_ = listLayoutAlgorithm->GetSpaceWidth();
137 float relativeOffset = listLayoutAlgorithm->GetCurrentOffset();
138 auto predictSnapOffset = listLayoutAlgorithm->GetPredictSnapOffset();
139 auto predictSnapEndPos = listLayoutAlgorithm->GetPredictSnapEndPosition();
140 if (listLayoutAlgorithm->NeedEstimateOffset()) {
141 lanes_ = listLayoutAlgorithm->GetLanes();
142 auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis());
143 calculate.GetEstimateHeightAndOffset(GetHost());
144 currentOffset_ = calculate.GetEstimateOffset();
145 isJump = true;
146 } else {
147 // correct the currentOffset when the startIndex is 0.
148 if (listLayoutAlgorithm->GetStartIndex() == 0) {
149 currentOffset_ = -itemPosition_.begin()->second.startPos;
150 } else {
151 currentOffset_ = currentOffset_ + relativeOffset;
152 }
153 }
154 if (targetIndex_) {
155 AnimateToTarget(targetIndex_.value(), targetIndexInGroup_, scrollAlign_);
156 targetIndex_.reset();
157 targetIndexInGroup_.reset();
158 }
159 if (predictSnapOffset.has_value()) {
160 if (scrollableTouchEvent_) {
161 scrollableTouchEvent_->StartScrollSnapMotion(predictSnapOffset.value(), scrollSnapVelocity_);
162 scrollSnapVelocity_ = 0.0f;
163 }
164 predictSnapOffset_.reset();
165 if (predictSnapEndPos.has_value()) {
166 predictSnapEndPos_ = predictSnapEndPos;
167 } else {
168 predictSnapEndPos_.reset();
169 }
170 }
171 if (predictSnapEndPos.has_value() && predictSnapEndPos_.has_value() &&
172 !NearEqual(predictSnapEndPos.value(), predictSnapEndPos_.value())) {
173 if (scrollableTouchEvent_) {
174 scrollableTouchEvent_->UpdateScrollSnapEndWithOffset(
175 predictSnapEndPos.value() - predictSnapEndPos_.value());
176 }
177 predictSnapEndPos_.reset();
178 }
179
180 if (isScrollEnd_) {
181 // AccessibilityEventType::SCROLL_END
182 isScrollEnd_ = false;
183 }
184 currentDelta_ = 0.0f;
185 isNeedCheckOffset_ = false;
186 float prevStartOffset = startMainPos_;
187 float prevEndOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
188 float prevContentSize = contentMainSize_ - contentStartOffset_ - contentEndOffset_;
189 float prevTotalSize = endMainPos_ - startMainPos_;
190 contentMainSize_ = listLayoutAlgorithm->GetContentMainSize();
191 contentStartOffset_ = listLayoutAlgorithm->GetContentStartOffset();
192 contentEndOffset_ = listLayoutAlgorithm->GetContentEndOffset();
193 startMainPos_ = listLayoutAlgorithm->GetStartPosition();
194 endMainPos_ = listLayoutAlgorithm->GetEndPosition();
195 crossMatchChild_ = listLayoutAlgorithm->IsCrossMatchChild();
196 bool sizeDiminished =
197 LessNotEqual(endMainPos_ - startMainPos_, contentMainSize_ - contentStartOffset_ - contentEndOffset_) &&
198 GreatOrEqual(prevTotalSize, prevContentSize) && LessNotEqual(endMainPos_ - startMainPos_, prevTotalSize);
199 auto lanesLayoutAlgorithm = DynamicCast<ListLanesLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
200 if (lanesLayoutAlgorithm) {
201 lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
202 if (lanesLayoutAlgorithm->GetLanes() != lanes_) {
203 auto item = swiperItem_.Upgrade();
204 if (item) {
205 item->SwiperReset();
206 }
207 }
208 lanes_ = lanesLayoutAlgorithm->GetLanes();
209 laneGutter_ = lanesLayoutAlgorithm->GetLaneGutter();
210 }
211 CheckScrollable();
212
213 bool indexChanged = false;
214 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
215 indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
216 (endIndex_ != listLayoutAlgorithm->GetEndIndex()) ||
217 (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty)));
218 } else {
219 indexChanged =
220 (startIndex_ != listLayoutAlgorithm->GetStartIndex()) || (endIndex_ != listLayoutAlgorithm->GetEndIndex());
221 }
222 endIndexChanged_ = endIndex_ != listLayoutAlgorithm->GetEndIndex();
223 if (indexChanged) {
224 startIndex_ = listLayoutAlgorithm->GetStartIndex();
225 endIndex_ = listLayoutAlgorithm->GetEndIndex();
226 centerIndex_ = listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty));
227 }
228 ProcessEvent(indexChanged, relativeOffset, isJump, prevStartOffset, prevEndOffset);
229 HandleScrollBarOutBoundary();
230 UpdateScrollBarOffset();
231 if (config.frameSizeChange) {
232 if (GetScrollBar() != nullptr) {
233 GetScrollBar()->ScheduleDisappearDelayTask();
234 }
235 }
236 CheckRestartSpring(sizeDiminished);
237
238 DrivenRender(dirty);
239
240 SetScrollSource(SCROLL_FROM_NONE);
241 isInitialized_ = true;
242 MarkSelectedItems();
243 UpdateListDirectionInCardStyle();
244 return true;
245 }
246
UpdateListDirectionInCardStyle()247 void ListPattern::UpdateListDirectionInCardStyle()
248 {
249 if (isNeedToUpdateListDirection_) {
250 auto layoutProperty = GetLayoutProperty<ListLayoutProperty>();
251 layoutProperty->UpdateListDirection(Axis::VERTICAL);
252 isNeedToUpdateListDirection_ = false;
253 }
254 }
255
GetScrollAlignByScrollSnapAlign() const256 ScrollAlign ListPattern::GetScrollAlignByScrollSnapAlign() const
257 {
258 auto scrollAlign = ScrollAlign::START;
259 auto host = GetHost();
260 CHECK_NULL_RETURN(host, scrollAlign);
261 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
262 CHECK_NULL_RETURN(listProperty, scrollAlign);
263 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
264 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
265 scrollAlign = ScrollAlign::CENTER;
266 }
267 return scrollAlign;
268 }
269
CalculateTargetPos(float startPos,float endPos)270 float ListPattern::CalculateTargetPos(float startPos, float endPos)
271 {
272 float topOffset = startPos - contentStartOffset_;
273 float bottomOffset = endPos - contentMainSize_ + contentEndOffset_;
274 if (GreatOrEqual(topOffset, 0.0f) && LessOrEqual(bottomOffset, 0.0f)) {
275 return 0.0f;
276 }
277 if ((NearEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) ||
278 (LessNotEqual(topOffset, 0.0f) && NearEqual(bottomOffset, 0.0f))) {
279 return 0.0f;
280 }
281 if (LessNotEqual(topOffset, 0.0f) && GreatNotEqual(bottomOffset, 0.0f)) {
282 if (GreatOrEqual(std::abs(topOffset), std::abs(bottomOffset))) {
283 return bottomOffset;
284 } else {
285 return topOffset;
286 }
287 }
288 if (GreatNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
289 return bottomOffset;
290 } else if (LessNotEqual(std::abs(topOffset), std::abs(bottomOffset))) {
291 return topOffset;
292 } else {
293 if (LessNotEqual(topOffset, 0.0f)) {
294 return topOffset;
295 } else {
296 return bottomOffset;
297 }
298 }
299 return 0.0f;
300 }
301
CreateNodePaintMethod()302 RefPtr<NodePaintMethod> ListPattern::CreateNodePaintMethod()
303 {
304 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
305 V2::ItemDivider divider;
306 if (!chainAnimation_ && listLayoutProperty->HasDivider()) {
307 divider = listLayoutProperty->GetDivider().value();
308 }
309 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
310 auto drawVertical = (axis == Axis::HORIZONTAL);
311 auto paint = MakeRefPtr<ListPaintMethod>(divider, drawVertical, lanes_, spaceWidth_);
312 paint->SetScrollBar(GetScrollBar());
313 CreateScrollBarOverlayModifier();
314 paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
315 paint->SetTotalItemCount(maxListItemIndex_ + 1);
316 auto scrollEffect = GetScrollEdgeEffect();
317 if (scrollEffect && scrollEffect->IsFadeEffect()) {
318 paint->SetEdgeEffect(scrollEffect);
319 }
320 if (!listContentModifier_) {
321 auto host = GetHost();
322 CHECK_NULL_RETURN(host, paint);
323 const auto& geometryNode = host->GetGeometryNode();
324 auto size = geometryNode->GetPaddingSize();
325 OffsetF offset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
326 listContentModifier_ = AceType::MakeRefPtr<ListContentModifier>(offset, size);
327 }
328
329 paint->SetLaneGutter(laneGutter_);
330 paint->SetItemsPosition(itemPosition_);
331 paint->SetContentModifier(listContentModifier_);
332 return paint;
333 }
334
ProcessEvent(bool indexChanged,float finalOffset,bool isJump,float prevStartOffset,float prevEndOffset)335 void ListPattern::ProcessEvent(
336 bool indexChanged, float finalOffset, bool isJump, float prevStartOffset, float prevEndOffset)
337 {
338 auto host = GetHost();
339 CHECK_NULL_VOID(host);
340 auto listEventHub = host->GetEventHub<ListEventHub>();
341 CHECK_NULL_VOID(listEventHub);
342
343 paintStateFlag_ = !NearZero(finalOffset) && !isJump;
344 isFramePaintStateValid_ = true;
345 auto onScroll = listEventHub->GetOnScroll();
346 if (onScroll) {
347 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
348 FireOnScroll(finalOffset, onScroll);
349 } else {
350 if (!NearZero(finalOffset)) {
351 auto offsetPX = Dimension(finalOffset);
352 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
353 auto source = GetScrollSource();
354 if (source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR ||
355 source == SCROLL_FROM_ANIMATION_CONTROLLER) {
356 source = SCROLL_FROM_NONE;
357 }
358 onScroll(offsetVP, GetScrollState(source));
359 }
360 }
361 }
362
363 if (indexChanged) {
364 auto onScrollIndex = listEventHub->GetOnScrollIndex();
365 if (onScrollIndex) {
366 int32_t startIndex = startIndex_ == -1 ? 0 : startIndex_;
367 int32_t endIndex = endIndex_ == -1 ? 0 : endIndex_;
368 onScrollIndex(startIndex, endIndex, centerIndex_);
369 }
370 }
371
372 auto onReachStart = listEventHub->GetOnReachStart();
373 if (onReachStart && (startIndex_ == 0)) {
374 bool scrollUpToStart = GreatNotEqual(prevStartOffset, contentStartOffset_) &&
375 LessOrEqual(startMainPos_, contentStartOffset_);
376 bool scrollDownToStart = (LessNotEqual(prevStartOffset, contentStartOffset_) || !isInitialized_) &&
377 GreatOrEqual(startMainPos_, contentStartOffset_);
378 if (scrollUpToStart || scrollDownToStart) {
379 onReachStart();
380 }
381 }
382 auto onReachEnd = listEventHub->GetOnReachEnd();
383 if (onReachEnd && (endIndex_ == maxListItemIndex_)) {
384 float endOffset = endMainPos_ - contentMainSize_ + contentEndOffset_;
385 // deltaOffset passes through multiple items also needs to fire reachEnd
386 bool scrollUpToEnd =
387 (endIndexChanged_ || (Positive(prevEndOffset) || !isInitialized_)) && NonPositive(endOffset);
388 bool scrollDownToEnd = Negative(prevEndOffset) && NonNegative(endOffset);
389 if (scrollUpToEnd || (scrollDownToEnd && GetScrollSource() != SCROLL_FROM_NONE)) {
390 onReachEnd();
391 }
392 }
393
394 OnScrollStop(listEventHub->GetOnScrollStop());
395 }
396
DrivenRender(const RefPtr<LayoutWrapper> & layoutWrapper)397 void ListPattern::DrivenRender(const RefPtr<LayoutWrapper>& layoutWrapper)
398 {
399 auto host = GetHost();
400 CHECK_NULL_VOID(host);
401 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
402 auto listPaintProperty = host->GetPaintProperty<ScrollablePaintProperty>();
403 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
404 auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
405 bool barNeedPaint = GetScrollBar() ? GetScrollBar()->NeedPaint() : false;
406 auto chainAnimation = listLayoutProperty->GetChainAnimation().value_or(false);
407 bool drivenRender = !(axis != Axis::VERTICAL || stickyStyle != V2::StickyStyle::NONE || barNeedPaint ||
408 chainAnimation || !scrollable_);
409
410 auto renderContext = host->GetRenderContext();
411 CHECK_NULL_VOID(renderContext);
412 renderContext->MarkDrivenRender(drivenRender);
413 if (drivenRender && isFramePaintStateValid_) {
414 // Mark items
415 int32_t indexStep = 0;
416 int32_t startIndex = itemPosition_.empty() ? 0 : itemPosition_.begin()->first;
417 for (auto& pos : itemPosition_) {
418 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
419 CHECK_NULL_VOID(wrapper);
420 auto itemHost = wrapper->GetHostNode();
421 CHECK_NULL_VOID(itemHost);
422 auto itemRenderContext = itemHost->GetRenderContext();
423 CHECK_NULL_VOID(itemRenderContext);
424 itemRenderContext->MarkDrivenRenderItemIndex(startIndex + indexStep);
425 indexStep++;
426 }
427 renderContext->MarkDrivenRenderFramePaintState(paintStateFlag_);
428 isFramePaintStateValid_ = false;
429 }
430 }
431
CheckScrollable()432 void ListPattern::CheckScrollable()
433 {
434 auto host = GetHost();
435 CHECK_NULL_VOID(host);
436 auto hub = host->GetEventHub<EventHub>();
437 CHECK_NULL_VOID(hub);
438 auto gestureHub = hub->GetOrCreateGestureEventHub();
439 CHECK_NULL_VOID(gestureHub);
440 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
441 CHECK_NULL_VOID(listProperty);
442 if (itemPosition_.empty()) {
443 scrollable_ = false;
444 } else {
445 if ((itemPosition_.begin()->first == 0) && (itemPosition_.rbegin()->first == maxListItemIndex_) &&
446 !IsScrollSnapAlignCenter()) {
447 scrollable_ = GetAlwaysEnabled() || GreatNotEqual(endMainPos_ - startMainPos_,
448 contentMainSize_ - contentStartOffset_ - contentEndOffset_);
449 } else {
450 scrollable_ = true;
451 }
452 }
453
454 SetScrollEnable(scrollable_);
455
456 if (!listProperty->GetScrollEnabled().value_or(scrollable_)) {
457 SetScrollEnable(false);
458 }
459 }
460
CreateLayoutAlgorithm()461 RefPtr<LayoutAlgorithm> ListPattern::CreateLayoutAlgorithm()
462 {
463 auto host = GetHost();
464 CHECK_NULL_RETURN(host, nullptr);
465 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
466 RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm;
467 if (listLayoutProperty->HasLanes() || listLayoutProperty->HasLaneMinLength() ||
468 listLayoutProperty->HasLaneMaxLength()) {
469 auto lanesLayoutAlgorithm = MakeRefPtr<ListLanesLayoutAlgorithm>();
470 RefreshLanesItemRange();
471 lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
472 lanesLayoutAlgorithm->SetLanes(lanes_);
473 listLayoutAlgorithm.Swap(lanesLayoutAlgorithm);
474 } else {
475 listLayoutAlgorithm.Swap(MakeRefPtr<ListLayoutAlgorithm>());
476 }
477 if (jumpIndex_) {
478 listLayoutAlgorithm->SetIndex(jumpIndex_.value());
479 listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
480 jumpIndex_.reset();
481 }
482 if (targetIndex_) {
483 listLayoutAlgorithm->SetTargetIndex(targetIndex_.value());
484 listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
485 }
486 if (jumpIndexInGroup_) {
487 listLayoutAlgorithm->SetIndexInGroup(jumpIndexInGroup_.value());
488 jumpIndexInGroup_.reset();
489 }
490 if (predictSnapOffset_.has_value()) {
491 listLayoutAlgorithm->SetPredictSnapOffset(predictSnapOffset_.value());
492 }
493 listLayoutAlgorithm->SetTotalOffset(GetTotalOffset());
494 listLayoutAlgorithm->SetCurrentDelta(currentDelta_);
495 listLayoutAlgorithm->SetIsNeedCheckOffset(isNeedCheckOffset_);
496 listLayoutAlgorithm->SetItemsPosition(itemPosition_);
497 listLayoutAlgorithm->SetPrevContentMainSize(contentMainSize_);
498 if (IsOutOfBoundary(false) && GetScrollSource() != SCROLL_FROM_AXIS) {
499 listLayoutAlgorithm->SetOverScrollFeature();
500 }
501 listLayoutAlgorithm->SetIsSpringEffect(IsScrollableSpringEffect());
502 listLayoutAlgorithm->SetCanOverScroll(CanOverScroll(GetScrollSource()));
503 if (chainAnimation_) {
504 SetChainAnimationLayoutAlgorithm(listLayoutAlgorithm, listLayoutProperty);
505 }
506 if (predictSnapEndPos_.has_value()) {
507 listLayoutAlgorithm->SetPredictSnapEndPosition(predictSnapEndPos_.value());
508 }
509 return listLayoutAlgorithm;
510 }
511
SetChainAnimationLayoutAlgorithm(RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm,RefPtr<ListLayoutProperty> listLayoutProperty)512 void ListPattern::SetChainAnimationLayoutAlgorithm(
513 RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm, RefPtr<ListLayoutProperty> listLayoutProperty)
514 {
515 CHECK_NULL_VOID(listLayoutAlgorithm);
516 CHECK_NULL_VOID(listLayoutProperty);
517 listLayoutAlgorithm->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
518 auto list = weak.Upgrade();
519 CHECK_NULL_RETURN(list, 0.0f);
520 return list->GetChainDelta(index);
521 });
522 if (!listLayoutProperty->GetSpace().has_value() && chainAnimation_) {
523 listLayoutAlgorithm->SetChainInterval(CHAIN_INTERVAL_DEFAULT.ConvertToPx());
524 }
525 }
526
IsScrollSnapAlignCenter() const527 bool ListPattern::IsScrollSnapAlignCenter() const
528 {
529 auto host = GetHost();
530 CHECK_NULL_RETURN(host, false);
531 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
532 CHECK_NULL_RETURN(listProperty, false);
533 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
534 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
535 return true;
536 }
537
538 return false;
539 }
540
UpdateScrollSnap()541 void ListPattern::UpdateScrollSnap()
542 {
543 if (!AnimateStoped()) {
544 return;
545 }
546 predictSnapOffset_ = 0.0f;
547 }
548
NeedScrollSnapAlignEffect() const549 bool ListPattern::NeedScrollSnapAlignEffect() const
550 {
551 auto host = GetHost();
552 CHECK_NULL_RETURN(host, false);
553 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
554 CHECK_NULL_RETURN(listProperty, false);
555 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
556 if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
557 return false;
558 }
559
560 return true;
561 }
562
IsAtTop() const563 bool ListPattern::IsAtTop() const
564 {
565 if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
566 float startItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
567 return (startIndex_ == 0) && GreatOrEqual(startMainPos_ - currentDelta_ + GetChainDelta(0),
568 contentMainSize_ / 2.0f - startItemHeight / 2.0f);
569 }
570
571 return (startIndex_ == 0) && NonNegative(startMainPos_ - currentDelta_ + GetChainDelta(0) - contentStartOffset_);
572 }
573
IsAtBottom() const574 bool ListPattern::IsAtBottom() const
575 {
576 if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
577 float endItemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
578 return (endIndex_ == maxListItemIndex_) && LessOrEqual(endMainPos_ - currentDelta_ + GetChainDelta(endIndex_),
579 contentMainSize_ / 2.0f + endItemHeight / 2.0f);
580 }
581
582 return endIndex_ == maxListItemIndex_ &&
583 LessOrEqual(endMainPos_ - currentDelta_ + GetChainDelta(endIndex_), contentMainSize_ - contentEndOffset_);
584 }
585
OutBoundaryCallback()586 bool ListPattern::OutBoundaryCallback()
587 {
588 bool outBoundary = IsOutOfBoundary();
589 if (!dragFromSpring_ && outBoundary && chainAnimation_) {
590 chainAnimation_->SetOverDrag(false);
591 auto delta = chainAnimation_->SetControlIndex(IsAtTop() ? 0 : maxListItemIndex_);
592 currentDelta_ -= delta;
593 dragFromSpring_ = true;
594 }
595 return outBoundary;
596 }
597
GetListItemGroupEdge(bool & groupAtStart,bool & groupAtEnd) const598 void ListPattern::GetListItemGroupEdge(bool& groupAtStart, bool& groupAtEnd) const
599 {
600 if (itemPosition_.empty()) {
601 return;
602 }
603 bool firstIsGroup = startIndex_ == 0 && itemPosition_.begin()->second.isGroup;
604 bool lastIsGroup = endIndex_ == maxListItemIndex_ && itemPosition_.rbegin()->second.isGroup;
605 if (!firstIsGroup && !lastIsGroup) {
606 return;
607 }
608 auto host = GetHost();
609 CHECK_NULL_VOID(host);
610 std::list<RefPtr<FrameNode>> childrens;
611 host->GenerateOneDepthVisibleFrame(childrens);
612 if (childrens.empty()) {
613 return;
614 }
615 if (firstIsGroup) {
616 auto itemGroup = (*childrens.begin())->GetPattern<ListItemGroupPattern>();
617 if (itemGroup) {
618 groupAtStart = itemGroup->GetDisplayStartIndexInGroup() == 0;
619 }
620 }
621 if (lastIsGroup) {
622 auto itemGroup = (*childrens.rbegin())->GetPattern<ListItemGroupPattern>();
623 if (itemGroup) {
624 groupAtEnd = itemGroup->GetDisplayEndIndexInGroup() == itemGroup->GetEndIndexInGroup() ||
625 LessOrEqual(itemPosition_.rbegin()->second.endPos, contentMainSize_);
626 }
627 }
628 }
629
GetOffsetWithLimit(float offset) const630 float ListPattern::GetOffsetWithLimit(float offset) const
631 {
632 auto currentOffset = GetTotalOffset() + contentStartOffset_;
633 if (Positive(offset)) {
634 return std::min(currentOffset, offset);
635 } else if (Negative(offset)) {
636 auto remainHeight = GetTotalHeight() - currentOffset;
637 return std::max(offset, -remainHeight);
638 }
639 return 0;
640 }
641
GetOverScrollOffset(double delta) const642 OverScrollOffset ListPattern::GetOverScrollOffset(double delta) const
643 {
644 OverScrollOffset offset = { 0, 0 };
645 bool groupAtStart = true;
646 bool groupAtEnd = true;
647 GetListItemGroupEdge(groupAtStart, groupAtEnd);
648 if (startIndex_ == 0 && groupAtStart) {
649 auto startPos = startMainPos_ + GetChainDelta(0);
650 auto newStartPos = startPos + delta;
651 if (startPos > contentStartOffset_ && newStartPos > contentStartOffset_) {
652 offset.start = delta;
653 }
654 if (startPos > contentStartOffset_ && newStartPos <= contentStartOffset_) {
655 offset.start = contentStartOffset_ - startPos;
656 }
657 if (startPos <= contentStartOffset_ && newStartPos > contentStartOffset_) {
658 offset.start = newStartPos - contentStartOffset_;
659 }
660 if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
661 float startItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
662 if (newStartPos > (contentMainSize_ / 2.0f - startItemHeight / 2.0f - spaceWidth_ / 2.0f)) {
663 offset.start = newStartPos - (contentMainSize_ / 2.0f - startItemHeight / 2.0f - spaceWidth_ / 2.0f);
664 } else {
665 offset.start = 0.0;
666 }
667 }
668 }
669 if (endIndex_ == maxListItemIndex_ && groupAtEnd) {
670 auto endPos = endMainPos_ + GetChainDelta(endIndex_);
671 auto contentEndPos = contentMainSize_ - contentEndOffset_;
672 if (GreatNotEqual(contentEndPos, endMainPos_ - startMainPos_)) {
673 endPos = startMainPos_ + contentEndPos;
674 }
675 auto newEndPos = endPos + delta;
676 if (endPos < contentEndPos && newEndPos < contentEndPos) {
677 offset.end = delta;
678 }
679 if (endPos < contentEndPos && newEndPos >= contentEndPos) {
680 offset.end = contentEndPos - endPos;
681 }
682 if (endPos >= contentEndPos && newEndPos < contentEndPos) {
683 offset.end = newEndPos - contentEndPos;
684 }
685 if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
686 float endItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
687 if (newEndPos < (contentMainSize_ / 2.0f + endItemHeight / 2.0f + spaceWidth_ / 2.0f)) {
688 offset.end = newEndPos - (contentMainSize_ / 2.0f + endItemHeight / 2.0f + spaceWidth_ / 2.0f);
689 } else {
690 offset.end = 0.0;
691 }
692 }
693 }
694 return offset;
695 }
696
UpdateCurrentOffset(float offset,int32_t source)697 bool ListPattern::UpdateCurrentOffset(float offset, int32_t source)
698 {
699 if (itemPosition_.empty()) {
700 return false;
701 }
702 // check edgeEffect is not springEffect
703 if (!jumpIndex_.has_value() && !targetIndex_.has_value() && !HandleEdgeEffect(offset, source, GetContentSize())) {
704 if (IsOutOfBoundary(false)) {
705 MarkDirtyNodeSelf();
706 }
707 return false;
708 }
709 SetScrollSource(source);
710 FireAndCleanScrollingListener();
711 currentDelta_ = currentDelta_ - offset;
712 if (source == SCROLL_FROM_BAR || source == SCROLL_FROM_BAR_FLING) {
713 isNeedCheckOffset_ = true;
714 }
715 if (!NearZero(offset)) {
716 MarkDirtyNodeSelf();
717 }
718 if (!IsOutOfBoundary() || !scrollable_) {
719 return true;
720 }
721
722 // over scroll in drag update from normal to over scroll.
723 float overScroll = 0.0f;
724 // over scroll in drag update during over scroll.
725 auto startPos = startMainPos_ - currentDelta_;
726 if ((itemPosition_.begin()->first == 0) && GreatNotEqual(startPos, contentStartOffset_)) {
727 overScroll = startPos - contentStartOffset_;
728 } else {
729 overScroll = contentMainSize_ - contentEndOffset_ - (endMainPos_ - currentDelta_);
730 }
731 if (IsScrollSnapAlignCenter()) {
732 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
733 auto endPos = endMainPos_ - currentDelta_;
734 if (startIndex_ == 0 && Positive(startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
735 overScroll = startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
736 } else if ((endIndex_ == maxListItemIndex_) &&
737 LessNotEqual(endPos - itemHeight / 2.0f, contentMainSize_ / 2.0f)) {
738 overScroll = endPos - itemHeight / 2.0f - contentMainSize_ / 2.0f;
739 }
740 }
741
742 if (GetScrollSource() == SCROLL_FROM_UPDATE) {
743 // adjust offset.
744 auto friction = ScrollablePattern::CalculateFriction(std::abs(overScroll) / contentMainSize_);
745 currentDelta_ = currentDelta_ * friction;
746 }
747 return true;
748 }
749
MarkDirtyNodeSelf()750 void ListPattern::MarkDirtyNodeSelf()
751 {
752 auto host = GetHost();
753 CHECK_NULL_VOID(host);
754 if (!crossMatchChild_) {
755 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
756 } else {
757 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
758 }
759 }
760
OnScrollEndCallback()761 void ListPattern::OnScrollEndCallback()
762 {
763 SetScrollSource(SCROLL_FROM_ANIMATION);
764 scrollStop_ = true;
765 MarkDirtyNodeSelf();
766 }
767
GetContentSize() const768 SizeF ListPattern::GetContentSize() const
769 {
770 auto host = GetHost();
771 CHECK_NULL_RETURN(host, SizeF());
772 auto geometryNode = host->GetGeometryNode();
773 CHECK_NULL_RETURN(geometryNode, SizeF());
774 return geometryNode->GetPaddingSize();
775 }
776
IsOutOfBoundary(bool useCurrentDelta)777 bool ListPattern::IsOutOfBoundary(bool useCurrentDelta)
778 {
779 if (itemPosition_.empty()) {
780 return false;
781 }
782 auto startPos = useCurrentDelta ? startMainPos_ - currentDelta_ : startMainPos_;
783 auto endPos = useCurrentDelta ? endMainPos_ - currentDelta_ : endMainPos_;
784 if (startIndex_ == 0) {
785 startPos += GetChainDelta(0);
786 }
787 if (endIndex_ == maxListItemIndex_) {
788 endPos += GetChainDelta(endIndex_);
789 }
790 auto contentEndPos = contentMainSize_ - contentEndOffset_;
791 bool outOfStart = (startIndex_ == 0) && GreatNotEqual(startPos, contentStartOffset_) &&
792 GreatNotEqual(endPos, contentEndPos);
793 if (GetAlwaysEnabled()) {
794 outOfStart = (startIndex_ == 0) && GreatNotEqual(startPos, contentStartOffset_);
795 }
796
797 bool outOfEnd = (endIndex_ == maxListItemIndex_) && LessNotEqual(endPos, contentEndPos) &&
798 LessNotEqual(startPos, contentStartOffset_);
799 return outOfStart || outOfEnd;
800 }
801
OnScrollCallback(float offset,int32_t source)802 bool ListPattern::OnScrollCallback(float offset, int32_t source)
803 {
804 if (source == SCROLL_FROM_START) {
805 ProcessDragStart(offset);
806 auto item = swiperItem_.Upgrade();
807 if (item) {
808 item->SwiperReset();
809 }
810 FireOnScrollStart();
811 return true;
812 }
813 ProcessDragUpdate(offset, source);
814 return UpdateCurrentOffset(offset, source);
815 }
816
InitScrollableEvent()817 void ListPattern::InitScrollableEvent()
818 {
819 AddScrollEvent();
820 auto host = GetHost();
821 CHECK_NULL_VOID(host);
822 auto listEventHub = host->GetEventHub<ListEventHub>();
823 auto onScrollFrameBegin = listEventHub->GetOnScrollFrameBegin();
824 SetScrollFrameBeginCallback(onScrollFrameBegin);
825 auto scrollableEvent = GetScrollableEvent();
826 CHECK_NULL_VOID(scrollableEvent);
827 scrollableTouchEvent_ = scrollableEvent->GetScrollable();
828 CHECK_NULL_VOID(scrollableTouchEvent_);
829 scrollableTouchEvent_->SetOnContinuousSliding([weak = AceType::WeakClaim(this)]() -> double {
830 auto list = weak.Upgrade();
831 return list->contentMainSize_;
832 });
833 }
834
OnScrollSnapCallback(double targetOffset,double velocity)835 bool ListPattern::OnScrollSnapCallback(double targetOffset, double velocity)
836 {
837 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
838 CHECK_NULL_RETURN(listProperty, false);
839 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
840 if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
841 return false;
842 }
843 predictSnapOffset_ = targetOffset;
844 scrollSnapVelocity_ = velocity;
845 MarkDirtyNodeSelf();
846 return true;
847 }
848
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)849 void ListPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
850 {
851 scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
852 auto list = weak.Upgrade();
853 CHECK_NULL_RETURN(list, 0.0);
854 return list->startMainPos_ + list->GetChainDelta(list->startIndex_) - list->currentDelta_;
855 });
856 scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
857 auto list = weak.Upgrade();
858 auto endPos = list->endMainPos_ + list->GetChainDelta(list->endIndex_);
859 auto startPos = list->startMainPos_ + list->GetChainDelta(list->startIndex_);
860 if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
861 float endItemHeight =
862 list->itemPosition_.rbegin()->second.endPos - list->itemPosition_.rbegin()->second.startPos;
863 return list->contentMainSize_ / 2.0f + endItemHeight / 2.0f - (endPos - startPos);
864 }
865 float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
866 return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
867 });
868 scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
869 auto list = weak.Upgrade();
870 CHECK_NULL_RETURN(list, 0.0);
871 if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
872 float startItemHeight =
873 list->itemPosition_.begin()->second.endPos - list->itemPosition_.begin()->second.startPos;
874 return list->contentMainSize_ / 2.0f - startItemHeight / 2.0f;
875 }
876
877 return list->contentStartOffset_;
878 });
879 scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
880 auto list = weak.Upgrade();
881 auto endPos = list->endMainPos_ + list->GetChainDelta(list->endIndex_);
882 auto startPos = list->startMainPos_ + list->GetChainDelta(list->startIndex_);
883 if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
884 float endItemHeight =
885 list->itemPosition_.rbegin()->second.endPos - list->itemPosition_.rbegin()->second.startPos;
886 return list->contentMainSize_ / 2.0f + endItemHeight / 2.0f - (endPos - startPos);
887 }
888 float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
889 return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
890 });
891 scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
892 auto list = weak.Upgrade();
893 CHECK_NULL_RETURN(list, 0.0);
894 if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
895 float startItemHeight =
896 list->itemPosition_.begin()->second.endPos - list->itemPosition_.begin()->second.startPos;
897 return list->contentMainSize_ / 2.0f - startItemHeight / 2.0f;
898 }
899
900 return list->contentStartOffset_;
901 });
902 }
903
CheckRestartSpring(bool sizeDiminished)904 void ListPattern::CheckRestartSpring(bool sizeDiminished)
905 {
906 auto edgeEffect = GetScrollEdgeEffect();
907 if (!edgeEffect || !edgeEffect->IsSpringEffect()) {
908 return;
909 }
910 // Check if need update Spring when itemTotalSize diminishes.
911 if (IsScrollableSpringMotionRunning() && sizeDiminished) {
912 edgeEffect->ProcessSpringUpdate();
913 }
914 if (!ScrollableIdle() || !IsOutOfBoundary()) {
915 return;
916 }
917 if (AnimateRunning()) {
918 return;
919 }
920 FireOnScrollStart();
921 edgeEffect->ProcessScrollOver(0);
922 }
923
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)924 void ListPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
925 {
926 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
927 auto pattern = wp.Upgrade();
928 CHECK_NULL_RETURN(pattern, false);
929 return pattern->OnKeyEvent(event);
930 };
931 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
932 }
933
OnKeyEvent(const KeyEvent & event)934 bool ListPattern::OnKeyEvent(const KeyEvent& event)
935 {
936 if (event.action != KeyAction::DOWN) {
937 return false;
938 }
939 if (event.code == KeyCode::KEY_PAGE_DOWN) {
940 ScrollPage(false);
941 return true;
942 }
943 if (event.code == KeyCode::KEY_PAGE_UP) {
944 ScrollPage(true);
945 return true;
946 }
947 return HandleDirectionKey(event);
948 }
949
HandleDirectionKey(const KeyEvent & event)950 bool ListPattern::HandleDirectionKey(const KeyEvent& event)
951 {
952 return false;
953 }
954
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)955 WeakPtr<FocusHub> ListPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
956 {
957 auto curFocus = currentFocusNode.Upgrade();
958 CHECK_NULL_RETURN(curFocus, nullptr);
959 auto curFrame = curFocus->GetFrameNode();
960 CHECK_NULL_RETURN(curFrame, nullptr);
961 auto curPattern = curFrame->GetPattern();
962 CHECK_NULL_RETURN(curPattern, nullptr);
963 auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
964 CHECK_NULL_RETURN(curItemPattern, nullptr);
965 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
966 CHECK_NULL_RETURN(listProperty, nullptr);
967
968 auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
969 auto curIndex = curItemPattern->GetIndexInList();
970 auto curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
971 auto curListItemGroupPara = GetListItemGroupParameter(curFrame);
972 if (curIndex < 0 || curIndex > maxListItemIndex_) {
973 return nullptr;
974 }
975
976 auto moveStep = 0;
977 auto nextIndex = curIndex;
978 auto nextIndexInGroup = curIndexInGroup;
979 if (lanes_ <= 1) {
980 if ((isVertical && step == FocusStep::UP_END) || (!isVertical && step == FocusStep::LEFT_END)) {
981 moveStep = 1;
982 nextIndex = 0;
983 nextIndexInGroup = -1;
984 } else if ((isVertical && step == FocusStep::DOWN_END) || (!isVertical && step == FocusStep::RIGHT_END)) {
985 moveStep = -1;
986 nextIndex = maxListItemIndex_;
987 nextIndexInGroup = -1;
988 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT) ||
989 (step == FocusStep::TAB)) {
990 moveStep = 1;
991 if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
992 nextIndex = curIndex + moveStep;
993 nextIndexInGroup = -1;
994 } else {
995 nextIndexInGroup = curIndexInGroup + moveStep;
996 }
997 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT) ||
998 (step == FocusStep::SHIFT_TAB)) {
999 moveStep = -1;
1000 if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1001 nextIndex = curIndex + moveStep;
1002 nextIndexInGroup = -1;
1003 } else {
1004 nextIndexInGroup = curIndexInGroup + moveStep;
1005 }
1006 }
1007 } else {
1008 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
1009 moveStep = 1;
1010 nextIndex = 0;
1011 nextIndexInGroup = -1;
1012 } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
1013 moveStep = -1;
1014 nextIndex = maxListItemIndex_;
1015 nextIndexInGroup = -1;
1016 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
1017 if (curIndexInGroup == -1) {
1018 moveStep = lanes_;
1019 nextIndex = curIndex + moveStep;
1020 nextIndexInGroup = -1;
1021 } else {
1022 moveStep = curListItemGroupPara.lanes;
1023 nextIndexInGroup = curIndexInGroup + moveStep;
1024 }
1025 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
1026 if (curIndexInGroup == -1) {
1027 moveStep = -lanes_;
1028 nextIndex = curIndex + moveStep;
1029 nextIndexInGroup = -1;
1030 } else {
1031 moveStep = -curListItemGroupPara.lanes;
1032 nextIndexInGroup = curIndexInGroup + moveStep;
1033 }
1034 } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1035 moveStep = 1;
1036 if (((curIndexInGroup == -1) && ((curIndex - (lanes_ - 1)) % lanes_ != 0)) ||
1037 ((curIndexInGroup != -1) &&
1038 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes == 0))) {
1039 nextIndex = curIndex + moveStep;
1040 nextIndexInGroup = -1;
1041 } else if ((curIndexInGroup != -1) &&
1042 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes != 0)) {
1043 nextIndexInGroup = curIndexInGroup + moveStep;
1044 }
1045 } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1046 moveStep = -1;
1047 if (((curIndexInGroup == -1) && (curIndex % lanes_ != 0)) ||
1048 ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes == 0))) {
1049 nextIndex = curIndex + moveStep;
1050 nextIndexInGroup = -1;
1051 } else if ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes != 0)) {
1052 nextIndexInGroup = curIndexInGroup + moveStep;
1053 }
1054 } else if (step == FocusStep::TAB) {
1055 moveStep = 1;
1056 if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1057 nextIndex = curIndex + moveStep;
1058 nextIndexInGroup = -1;
1059 } else {
1060 nextIndexInGroup = curIndexInGroup + moveStep;
1061 }
1062 } else if (step == FocusStep::SHIFT_TAB) {
1063 moveStep = -1;
1064 if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1065 nextIndex = curIndex + moveStep;
1066 nextIndexInGroup = -1;
1067 } else {
1068 nextIndexInGroup = curIndexInGroup + moveStep;
1069 }
1070 }
1071 }
1072 while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
1073 if ((nextIndex == curIndex) && (curIndexInGroup == nextIndexInGroup)) {
1074 return nullptr;
1075 }
1076 auto nextFocusNode =
1077 ScrollAndFindFocusNode(nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep, step);
1078 if (nextFocusNode.Upgrade()) {
1079 return nextFocusNode;
1080 }
1081 if (nextIndexInGroup > -1) {
1082 nextIndexInGroup += moveStep;
1083 } else {
1084 nextIndex += moveStep;
1085 }
1086 }
1087 return nullptr;
1088 }
1089
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarGroupIndex)1090 WeakPtr<FocusHub> ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarGroupIndex)
1091 {
1092 auto listFrame = GetHost();
1093 CHECK_NULL_RETURN(listFrame, nullptr);
1094 auto listFocus = listFrame->GetFocusHub();
1095 CHECK_NULL_RETURN(listFocus, nullptr);
1096 auto childFocusList = listFocus->GetChildren();
1097 for (const auto& childFocus : childFocusList) {
1098 if (!childFocus->IsFocusable()) {
1099 continue;
1100 }
1101 auto childFrame = childFocus->GetFrameNode();
1102 if (!childFrame) {
1103 continue;
1104 }
1105 auto childPattern = childFrame->GetPattern();
1106 if (!childPattern) {
1107 continue;
1108 }
1109 auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
1110 if (!childItemPattern) {
1111 continue;
1112 }
1113 auto curIndex = childItemPattern->GetIndexInList();
1114 auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
1115 if (curIndex == tarMainIndex && curIndexInGroup == tarGroupIndex) {
1116 return AceType::WeakClaim(AceType::RawPtr(childFocus));
1117 }
1118 }
1119 return nullptr;
1120 }
1121
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1122 bool ListPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1123 {
1124 CHECK_NULL_RETURN(focusFrameNode, false);
1125 auto focusPattern = focusFrameNode->GetPattern<ListItemPattern>();
1126 CHECK_NULL_RETURN(focusPattern, false);
1127 auto curIndex = focusPattern->GetIndexInList();
1128 ScrollToIndex(curIndex, smooth_, ScrollAlign::AUTO);
1129 auto pipeline = PipelineContext::GetCurrentContext();
1130 if (pipeline) {
1131 pipeline->FlushUITasks();
1132 }
1133 return true;
1134 }
1135
ScrollAndFindFocusNode(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step)1136 WeakPtr<FocusHub> ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1137 int32_t curIndexInGroup, int32_t moveStep, FocusStep step)
1138 {
1139 auto isScrollIndex = ScrollListForFocus(nextIndex, curIndex, nextIndexInGroup);
1140 auto groupIndexInGroup =
1141 ScrollListItemGroupForFocus(nextIndex, nextIndexInGroup, curIndexInGroup, moveStep, step, isScrollIndex);
1142
1143 return groupIndexInGroup ? GetChildFocusNodeByIndex(nextIndex, nextIndexInGroup) : nullptr;
1144 }
1145
ScrollListForFocus(int32_t nextIndex,int32_t curIndex,int32_t nextIndexInGroup)1146 bool ListPattern::ScrollListForFocus(int32_t nextIndex, int32_t curIndex, int32_t nextIndexInGroup)
1147 {
1148 auto isScrollIndex = false;
1149 auto pipeline = PipelineContext::GetCurrentContext();
1150 CHECK_NULL_RETURN(pipeline, isScrollIndex);
1151 if (nextIndex < startIndex_) {
1152 if (nextIndexInGroup == -1) {
1153 isScrollIndex = true;
1154 ScrollToIndex(nextIndex, smooth_, ScrollAlign::START);
1155 pipeline->FlushUITasks();
1156 } else {
1157 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1158 pipeline->FlushUITasks();
1159 }
1160 } else if (nextIndex > endIndex_) {
1161 if (nextIndexInGroup == -1) {
1162 isScrollIndex = true;
1163 ScrollToIndex(nextIndex, smooth_, ScrollAlign::END);
1164 pipeline->FlushUITasks();
1165 } else {
1166 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1167 pipeline->FlushUITasks();
1168 }
1169 }
1170 return isScrollIndex;
1171 }
1172
ScrollListItemGroupForFocus(int32_t nextIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step,bool isScrollIndex)1173 bool ListPattern::ScrollListItemGroupForFocus(int32_t nextIndex, int32_t& nextIndexInGroup, int32_t curIndexInGroup,
1174 int32_t moveStep, FocusStep step, bool isScrollIndex)
1175 {
1176 auto groupIndexInGroup = true;
1177 auto pipeline = PipelineContext::GetCurrentContext();
1178 CHECK_NULL_RETURN(pipeline, groupIndexInGroup);
1179 RefPtr<FrameNode> nextIndexNode;
1180 auto isNextInGroup = IsListItemGroup(nextIndex, nextIndexNode);
1181 CHECK_NULL_RETURN(nextIndexNode, groupIndexInGroup);
1182 if (!isNextInGroup) {
1183 nextIndexInGroup = -1;
1184 return groupIndexInGroup;
1185 }
1186 auto nextListItemGroupPara = GetListItemGroupParameter(nextIndexNode);
1187 if (nextIndexInGroup == -1) {
1188 auto scrollAlign = ScrollAlign::END;
1189 nextIndexInGroup = moveStep < 0 ? nextListItemGroupPara.itemEndIndex : 0;
1190 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END) || (step == FocusStep::DOWN_END) ||
1191 (step == FocusStep::RIGHT_END)) {
1192 scrollAlign = moveStep < 0 ? ScrollAlign::END : ScrollAlign::START;
1193 } else {
1194 scrollAlign = moveStep < 0 ? ScrollAlign::START : ScrollAlign::END;
1195 }
1196 if ((nextIndexInGroup < nextListItemGroupPara.displayStartIndex) ||
1197 (nextIndexInGroup > nextListItemGroupPara.displayEndIndex) || (isScrollIndex)) {
1198 ScrollToIndex(nextIndex, nextIndexInGroup, scrollAlign);
1199 pipeline->FlushUITasks();
1200 }
1201 } else if (nextIndexInGroup > nextListItemGroupPara.itemEndIndex) {
1202 nextIndexInGroup = -1;
1203 groupIndexInGroup = false;
1204 } else {
1205 if ((nextIndexInGroup < curIndexInGroup) && (nextIndexInGroup < nextListItemGroupPara.displayStartIndex)) {
1206 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1207 pipeline->FlushUITasks();
1208 } else if ((nextIndexInGroup > curIndexInGroup) && (nextIndexInGroup > nextListItemGroupPara.displayEndIndex)) {
1209 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1210 pipeline->FlushUITasks();
1211 }
1212 }
1213 return groupIndexInGroup;
1214 }
1215
OnAnimateStop()1216 void ListPattern::OnAnimateStop()
1217 {
1218 scrollStop_ = true;
1219 MarkDirtyNodeSelf();
1220 isScrollEnd_ = true;
1221 }
1222
ScrollTo(float position)1223 void ListPattern::ScrollTo(float position)
1224 {
1225 StopAnimate();
1226 jumpIndex_.reset();
1227 targetIndex_.reset();
1228 currentDelta_ = 0.0f;
1229 UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1230 MarkDirtyNodeSelf();
1231 isScrollEnd_ = true;
1232 }
1233
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align)1234 void ListPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align)
1235 {
1236 SetScrollSource(SCROLL_FROM_JUMP);
1237 StopAnimate();
1238 if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1239 currentDelta_ = 0.0f;
1240 smooth_ = smooth;
1241 if (smooth_) {
1242 if (!AnimateToTarget(index, std::nullopt, align)) {
1243 targetIndex_ = index;
1244 scrollAlign_ = align;
1245 }
1246 } else {
1247 jumpIndex_ = index;
1248 scrollAlign_ = align;
1249 }
1250 MarkDirtyNodeSelf();
1251 }
1252 isScrollEnd_ = true;
1253 FireAndCleanScrollingListener();
1254 }
1255
CheckTargetValid(int32_t index,int32_t indexInGroup)1256 bool ListPattern::CheckTargetValid(int32_t index, int32_t indexInGroup)
1257 {
1258 auto host = GetHost();
1259 auto totalItemCount = host->GetTotalChildCount();
1260 if ((index < 0) || (index >= totalItemCount)) {
1261 return false;
1262 }
1263 auto groupWrapper = host->GetOrCreateChildByIndex(index);
1264 CHECK_NULL_RETURN(groupWrapper, false);
1265 if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) {
1266 return false;
1267 }
1268 auto groupNode = groupWrapper->GetHostNode();
1269 CHECK_NULL_RETURN(groupNode, false);
1270 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1271 CHECK_NULL_RETURN(groupPattern, false);
1272 auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex();
1273 if ((indexInGroup < 0) || (indexInGroup >= groupItemCount)) {
1274 return false;
1275 }
1276 return true;
1277 }
1278
ScrollToItemInGroup(int32_t index,int32_t indexInGroup,bool smooth,ScrollAlign align)1279 void ListPattern::ScrollToItemInGroup(int32_t index, int32_t indexInGroup, bool smooth, ScrollAlign align)
1280 {
1281 SetScrollSource(SCROLL_FROM_JUMP);
1282 StopAnimate();
1283 if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1284 currentDelta_ = 0.0f;
1285 smooth_ = smooth;
1286 if (smooth_) {
1287 if (!AnimateToTarget(index, indexInGroup, align)) {
1288 if (CheckTargetValid(index, indexInGroup)) {
1289 targetIndex_ = index;
1290 currentDelta_ = 0;
1291 targetIndexInGroup_ = indexInGroup;
1292 scrollAlign_ = align;
1293 }
1294 }
1295 } else {
1296 jumpIndex_ = index;
1297 jumpIndexInGroup_ = indexInGroup;
1298 scrollAlign_ = align;
1299 }
1300 MarkDirtyNodeSelf();
1301 }
1302 isScrollEnd_ = true;
1303 FireAndCleanScrollingListener();
1304 }
1305
GetListItemAnimatePos(float startPos,float endPos,ScrollAlign align,float & targetPos)1306 bool ListPattern::GetListItemAnimatePos(float startPos, float endPos, ScrollAlign align, float& targetPos)
1307 {
1308 switch (align) {
1309 case ScrollAlign::START:
1310 case ScrollAlign::NONE:
1311 targetPos = startPos;
1312 if (!IsScrollSnapAlignCenter()) {
1313 targetPos -= contentStartOffset_;
1314 }
1315 break;
1316 case ScrollAlign::CENTER:
1317 targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1318 break;
1319 case ScrollAlign::END:
1320 targetPos = endPos - contentMainSize_;
1321 if (!IsScrollSnapAlignCenter()) {
1322 targetPos += contentEndOffset_;
1323 }
1324 break;
1325 case ScrollAlign::AUTO:
1326 targetPos = CalculateTargetPos(startPos, endPos);
1327 break;
1328 }
1329 return true;
1330 }
1331
GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index,float startPos,float endPos,ScrollAlign align,float & targetPos)1332 bool ListPattern::GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index, float startPos, float endPos,
1333 ScrollAlign align, float& targetPos)
1334 {
1335 auto host = GetHost();
1336 CHECK_NULL_RETURN(host, false);
1337 auto groupWrapper = host->GetChildByIndex(index);
1338 CHECK_NULL_RETURN(groupWrapper, false);
1339 auto groupNode = groupWrapper->GetHostNode();
1340 CHECK_NULL_RETURN(groupNode, false);
1341 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1342 CHECK_NULL_RETURN(groupPattern, false);
1343 auto groupLayoutProperty = groupNode->GetLayoutProperty<ListItemGroupLayoutProperty>();
1344 CHECK_NULL_RETURN(groupLayoutProperty, false);
1345 auto visible = groupLayoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
1346
1347 switch (align) {
1348 case ScrollAlign::START:
1349 case ScrollAlign::NONE:
1350 if (visible != VisibleType::GONE && !groupPattern->IsDisplayStart()) {
1351 return false;
1352 }
1353 targetPos = startPos;
1354 if (!IsScrollSnapAlignCenter()) {
1355 targetPos -= contentStartOffset_;
1356 }
1357 break;
1358 case ScrollAlign::CENTER:
1359 if (visible != VisibleType::GONE && (!groupPattern->IsDisplayStart() || !groupPattern->IsDisplayEnd())) {
1360 return false;
1361 }
1362 targetPos = (endPos + startPos) / 2.0f - contentMainSize_ / 2.0f;
1363 break;
1364 case ScrollAlign::END:
1365 if (visible != VisibleType::GONE && !groupPattern->IsDisplayEnd()) {
1366 return false;
1367 }
1368 targetPos = endPos - contentMainSize_;
1369 if (!IsScrollSnapAlignCenter()) {
1370 targetPos += contentEndOffset_;
1371 }
1372 break;
1373 case ScrollAlign::AUTO:
1374 if (targetIndex_.has_value()) {
1375 targetPos = CalculateTargetPos(startPos, endPos);
1376 return true;
1377 }
1378 return false;
1379 }
1380
1381 return true;
1382 }
1383
GetListItemGroupAnimatePosWithIndexInGroup(int32_t index,int32_t indexInGroup,float startPos,ScrollAlign align,float & targetPos)1384 bool ListPattern::GetListItemGroupAnimatePosWithIndexInGroup(int32_t index, int32_t indexInGroup, float startPos,
1385 ScrollAlign align, float& targetPos)
1386 {
1387 auto host = GetHost();
1388 CHECK_NULL_RETURN(host, false);
1389 auto groupWrapper = host->GetChildByIndex(index);
1390 CHECK_NULL_RETURN(groupWrapper, false);
1391 auto groupNode = groupWrapper->GetHostNode();
1392 CHECK_NULL_RETURN(groupNode, false);
1393 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>();
1394 CHECK_NULL_RETURN(groupPattern, false);
1395
1396 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
1397 CHECK_NULL_RETURN(listLayoutProperty, false);
1398 auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
1399
1400 auto itemsPosInGroup = groupPattern->GetItemPosition();
1401 auto it = itemsPosInGroup.find(indexInGroup);
1402 if (it == itemsPosInGroup.end()) {
1403 return false;
1404 }
1405 std::optional<std::pair<float, float>> itemPosInGroup = it->second;
1406 auto padding = groupWrapper->GetGeometryNode()->GetPadding()->top;
1407 float paddingBeforeContent = 0.0f;
1408 if (padding) {
1409 paddingBeforeContent = padding.value();
1410 }
1411 switch (align) {
1412 case ScrollAlign::START:
1413 case ScrollAlign::NONE:
1414 targetPos = paddingBeforeContent + startPos + itemPosInGroup.value().first;
1415 if (stickyStyle == V2::StickyStyle::HEADER || stickyStyle == V2::StickyStyle::BOTH) {
1416 targetPos -= groupPattern->GetHeaderMainSize();
1417 }
1418 if (!IsScrollSnapAlignCenter()) {
1419 targetPos -= contentStartOffset_;
1420 }
1421 break;
1422 case ScrollAlign::CENTER:
1423 targetPos = paddingBeforeContent + startPos +
1424 (itemPosInGroup.value().first + itemPosInGroup.value().second) / 2.0f -
1425 contentMainSize_ / 2.0f;
1426 break;
1427 case ScrollAlign::END:
1428 targetPos = paddingBeforeContent + startPos + itemPosInGroup.value().second - contentMainSize_;
1429 if (stickyStyle == V2::StickyStyle::FOOTER || stickyStyle == V2::StickyStyle::BOTH) {
1430 targetPos += groupPattern->GetFooterMainSize();
1431 }
1432 if (!IsScrollSnapAlignCenter()) {
1433 targetPos += contentEndOffset_;
1434 }
1435 break;
1436 case ScrollAlign::AUTO:
1437 float itemStartPos = paddingBeforeContent + startPos + itemPosInGroup.value().first;
1438 float itemEndPos = paddingBeforeContent + startPos + itemPosInGroup.value().second;
1439 if (stickyStyle == V2::StickyStyle::HEADER || stickyStyle == V2::StickyStyle::BOTH) {
1440 itemStartPos -= groupPattern->GetHeaderMainSize();
1441 }
1442 if (stickyStyle == V2::StickyStyle::FOOTER || stickyStyle == V2::StickyStyle::BOTH) {
1443 itemEndPos += groupPattern->GetFooterMainSize();
1444 }
1445 targetPos = CalculateTargetPos(itemStartPos, itemEndPos);
1446 break;
1447 }
1448 return true;
1449 }
1450
AnimateToTarget(int32_t index,std::optional<int32_t> indexInGroup,ScrollAlign align)1451 bool ListPattern::AnimateToTarget(int32_t index, std::optional<int32_t> indexInGroup, ScrollAlign align)
1452 {
1453 auto iter = itemPosition_.find(index);
1454 if (iter == itemPosition_.end()) {
1455 return false;
1456 }
1457 float targetPos = 0.0f;
1458 if (iter->second.isGroup) {
1459 if (indexInGroup.has_value()) {
1460 if (!GetListItemGroupAnimatePosWithIndexInGroup(index, indexInGroup.value(), iter->second.startPos,
1461 align, targetPos)) {
1462 return false;
1463 }
1464 } else {
1465 if (!GetListItemGroupAnimatePosWithoutIndexInGroup(index, iter->second.startPos, iter->second.endPos,
1466 align, targetPos)) {
1467 return false;
1468 }
1469 }
1470 } else {
1471 if (indexInGroup.has_value()) {
1472 return false;
1473 }
1474 GetListItemAnimatePos(iter->second.startPos, iter->second.endPos, align, targetPos);
1475 }
1476 if (!NearZero(targetPos)) {
1477 AnimateTo(targetPos + currentOffset_, -1, nullptr, true);
1478 }
1479 return true;
1480 }
1481
ScrollPage(bool reverse)1482 bool ListPattern::ScrollPage(bool reverse)
1483 {
1484 StopAnimate();
1485 float distance = reverse ? contentMainSize_ : -contentMainSize_;
1486 UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
1487 isScrollEnd_ = true;
1488 return true;
1489 }
1490
ScrollBy(float offset)1491 void ListPattern::ScrollBy(float offset)
1492 {
1493 StopAnimate();
1494 UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
1495 isScrollEnd_ = true;
1496 }
1497
GetCurrentOffset() const1498 Offset ListPattern::GetCurrentOffset() const
1499 {
1500 if (GetAxis() == Axis::HORIZONTAL) {
1501 return { GetTotalOffset(), 0.0 };
1502 }
1503 return { 0.0, GetTotalOffset() };
1504 }
1505
HandleScrollBarOutBoundary()1506 void ListPattern::HandleScrollBarOutBoundary()
1507 {
1508 if (itemPosition_.empty()) {
1509 return;
1510 }
1511 if (!GetScrollBar() && !GetScrollBarProxy()) {
1512 return;
1513 }
1514 if (!IsOutOfBoundary(false) || !scrollable_) {
1515 ScrollablePattern::HandleScrollBarOutBoundary(0);
1516 return;
1517 }
1518 float overScroll = 0.0f;
1519 if (!IsScrollSnapAlignCenter()) {
1520 if ((itemPosition_.begin()->first == 0) && GreatNotEqual(startMainPos_, contentStartOffset_)) {
1521 overScroll = startMainPos_ - contentStartOffset_;
1522 } else {
1523 overScroll = contentMainSize_ - contentEndOffset_ - endMainPos_;
1524 }
1525 } else {
1526 float itemHeight = itemPosition_[centerIndex_].endPos - itemPosition_[centerIndex_].startPos;
1527 if (startIndex_ == 0 && Positive(startMainPos_ + itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
1528 overScroll = startMainPos_ + itemHeight / 2.0f - contentMainSize_ / 2.0f;
1529 } else if ((endIndex_ == maxListItemIndex_) &&
1530 LessNotEqual(endMainPos_ - itemHeight / 2.0f, contentMainSize_ / 2.0f)) {
1531 overScroll = endMainPos_ - itemHeight / 2.0f - contentMainSize_ / 2.0f;
1532 }
1533 }
1534 ScrollablePattern::HandleScrollBarOutBoundary(overScroll);
1535 }
1536
GetItemRect(int32_t index) const1537 Rect ListPattern::GetItemRect(int32_t index) const
1538 {
1539 if (index < 0 || index < startIndex_ || index > endIndex_) {
1540 return Rect();
1541 }
1542 auto host = GetHost();
1543 CHECK_NULL_RETURN(host, Rect());
1544 auto item = host->GetChildByIndex(index);
1545 CHECK_NULL_RETURN(item, Rect());
1546 auto itemGeometry = item->GetGeometryNode();
1547 CHECK_NULL_RETURN(itemGeometry, Rect());
1548 return Rect(itemGeometry->GetFrameRect().GetX(), itemGeometry->GetFrameRect().GetY(),
1549 itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height());
1550 }
1551
GetItemRectInGroup(int32_t index,int32_t indexInGroup) const1552 Rect ListPattern::GetItemRectInGroup(int32_t index, int32_t indexInGroup) const
1553 {
1554 if (index < 0 || indexInGroup < 0 || index < startIndex_ || index > endIndex_) {
1555 return Rect();
1556 }
1557 auto host = GetHost();
1558 CHECK_NULL_RETURN(host, Rect());
1559 auto itemGroupWrapper = host->GetChildByIndex(index);
1560 CHECK_NULL_RETURN(itemGroupWrapper, Rect());
1561 auto itemGroup = itemGroupWrapper->GetHostNode();
1562 CHECK_NULL_RETURN(itemGroup, Rect());
1563 if (!(itemGroup->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG)) {
1564 return Rect();
1565 }
1566 auto itemGroupGeometry = itemGroup->GetGeometryNode();
1567 CHECK_NULL_RETURN(itemGroupGeometry, Rect());
1568 auto groupPattern = itemGroup->GetPattern<ListItemGroupPattern>();
1569 CHECK_NULL_RETURN(groupPattern, Rect());
1570 if (indexInGroup < groupPattern->GetDisplayStartIndexInGroup() ||
1571 indexInGroup > groupPattern->GetDisplayEndIndexInGroup()) {
1572 return Rect();
1573 }
1574 auto groupItem = itemGroup->GetChildByIndex(indexInGroup + groupPattern->GetItemStartIndex());
1575 CHECK_NULL_RETURN(groupItem, Rect());
1576 auto groupItemGeometry = groupItem->GetGeometryNode();
1577 CHECK_NULL_RETURN(groupItemGeometry, Rect());
1578 return Rect(itemGroupGeometry->GetFrameRect().GetX() + groupItemGeometry->GetFrameRect().GetX(),
1579 itemGroupGeometry->GetFrameRect().GetY() + groupItemGeometry->GetFrameRect().GetY(),
1580 groupItemGeometry->GetFrameRect().Width(), groupItemGeometry->GetFrameRect().Height());
1581 }
1582
UpdateScrollBarOffset()1583 void ListPattern::UpdateScrollBarOffset()
1584 {
1585 if (itemPosition_.empty()) {
1586 return;
1587 }
1588 if (!GetScrollBar() && !GetScrollBarProxy()) {
1589 return;
1590 }
1591 auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis());
1592 calculate.GetEstimateHeightAndOffset(GetHost());
1593 float currentOffset = calculate.GetEstimateOffset();
1594 float estimatedHeight = calculate.GetEstimateHeight();
1595 if (GetAlwaysEnabled()) {
1596 estimatedHeight = estimatedHeight - spaceWidth_;
1597 }
1598 if (!IsScrollSnapAlignCenter()) {
1599 currentOffset += contentStartOffset_;
1600 estimatedHeight += contentStartOffset_ + contentEndOffset_;
1601 }
1602
1603 // calculate padding offset of list
1604 auto host = GetHost();
1605 CHECK_NULL_VOID(host);
1606 auto layoutPriority = host->GetLayoutProperty();
1607 CHECK_NULL_VOID(layoutPriority);
1608 auto padding = layoutPriority->CreatePaddingAndBorder();
1609 auto paddingMain = GetAxis() == Axis::VERTICAL ? padding.Height() : padding.Width();
1610 const auto& geometryNode = host->GetGeometryNode();
1611 auto frameSize = geometryNode->GetFrameSize();
1612 Size size(frameSize.Width(), frameSize.Height());
1613 UpdateScrollBarRegion(currentOffset, estimatedHeight + paddingMain, size, Offset(0.0f, 0.0f));
1614 }
1615
GetTotalHeight() const1616 float ListPattern::GetTotalHeight() const
1617 {
1618 auto currentOffset = GetTotalOffset();
1619 if (endIndex_ >= maxListItemIndex_) {
1620 return currentOffset + endMainPos_ + contentEndOffset_;
1621 }
1622 if (itemPosition_.empty()) {
1623 return 0.0f;
1624 }
1625 int32_t remainCount = maxListItemIndex_ - endIndex_;
1626 float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1627 float remainOffset = itemsSize / itemPosition_.size() * remainCount - spaceWidth_;
1628 return currentOffset + endMainPos_ + remainOffset + contentEndOffset_;
1629 }
1630
SetChainAnimation()1631 void ListPattern::SetChainAnimation()
1632 {
1633 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1634 CHECK_NULL_VOID(listLayoutProperty);
1635 auto edgeEffect = GetEdgeEffect();
1636 int32_t lanes = std::max(listLayoutProperty->GetLanes().value_or(1), 1);
1637 bool autoLanes = listLayoutProperty->HasLaneMinLength() || listLayoutProperty->HasLaneMaxLength();
1638 bool animation = listLayoutProperty->GetChainAnimation().value_or(false);
1639 bool enable = edgeEffect == EdgeEffect::SPRING && lanes == 1 && !autoLanes && animation;
1640 if (!enable) {
1641 chainAnimation_.Reset();
1642 return;
1643 }
1644 if (!chainAnimation_) {
1645 auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1646 if (Negative(space)) {
1647 space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
1648 }
1649 springProperty_ =
1650 AceType::MakeRefPtr<SpringProperty>(CHAIN_SPRING_MASS, CHAIN_SPRING_STIFFNESS, CHAIN_SPRING_DAMPING);
1651 if (chainAnimationOptions_.has_value()) {
1652 float maxSpace = chainAnimationOptions_.value().maxSpace.ConvertToPx();
1653 float minSpace = chainAnimationOptions_.value().minSpace.ConvertToPx();
1654 minSpace = Negative(minSpace) ? 0.0f : minSpace;
1655 minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
1656 maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
1657 springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1658 springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1659 chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1660 auto conductivity = chainAnimationOptions_.value().conductivity;
1661 if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1662 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1663 }
1664 chainAnimation_->SetConductivity(conductivity);
1665 auto intensity = chainAnimationOptions_.value().intensity;
1666 if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1667 intensity = ChainAnimation::DEFAULT_INTENSITY;
1668 }
1669 chainAnimation_->SetIntensity(intensity);
1670 auto effect = chainAnimationOptions_.value().edgeEffect;
1671 chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1672 } else {
1673 auto minSpace = space * DEFAULT_MIN_SPACE_SCALE;
1674 auto maxSpace = space * DEFAULT_MAX_SPACE_SCALE;
1675 chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1676 }
1677 chainAnimation_->SetAnimationCallback([weak = AceType::WeakClaim(this)]() {
1678 auto list = weak.Upgrade();
1679 CHECK_NULL_VOID(list);
1680 if (list->IsScrollableAnimationNotRunning()) {
1681 list->MarkDirtyNodeSelf();
1682 }
1683 });
1684 }
1685 }
1686
SetChainAnimationOptions(const ChainAnimationOptions & options)1687 void ListPattern::SetChainAnimationOptions(const ChainAnimationOptions& options)
1688 {
1689 chainAnimationOptions_ = options;
1690 if (chainAnimation_) {
1691 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1692 CHECK_NULL_VOID(listLayoutProperty);
1693 auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1694 if (Negative(space)) {
1695 space = CHAIN_INTERVAL_DEFAULT.ConvertToPx();
1696 }
1697 float maxSpace = options.maxSpace.ConvertToPx();
1698 float minSpace = options.minSpace.ConvertToPx();
1699 minSpace = Negative(minSpace) ? 0.0f : minSpace;
1700 minSpace = GreatNotEqual(minSpace, space) ? space : minSpace;
1701 maxSpace = LessNotEqual(maxSpace, space) ? space : maxSpace;
1702 chainAnimation_->SetSpace(space, maxSpace, minSpace);
1703 auto conductivity = chainAnimationOptions_.value().conductivity;
1704 if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1705 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1706 }
1707 chainAnimation_->SetConductivity(conductivity);
1708 auto intensity = chainAnimationOptions_.value().intensity;
1709 if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1710 intensity = ChainAnimation::DEFAULT_INTENSITY;
1711 }
1712 chainAnimation_->SetIntensity(intensity);
1713 auto effect = options.edgeEffect;
1714 chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1715 }
1716 if (springProperty_) {
1717 springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1718 springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1719 }
1720 }
1721
ProcessDragStart(float startPosition)1722 void ListPattern::ProcessDragStart(float startPosition)
1723 {
1724 CHECK_NULL_VOID(chainAnimation_);
1725 auto host = GetHost();
1726 CHECK_NULL_VOID(host);
1727 auto globalOffset = host->GetTransformRelativeOffset();
1728 int32_t index = -1;
1729 auto offset = startPosition - GetMainAxisOffset(globalOffset, GetAxis());
1730 auto it = std::find_if(
1731 itemPosition_.begin(), itemPosition_.end(), [offset](auto pos) { return offset <= pos.second.endPos; });
1732 if (it != itemPosition_.end()) {
1733 index = it->first;
1734 } else if (!itemPosition_.empty()) {
1735 index = itemPosition_.rbegin()->first + 1;
1736 }
1737 dragFromSpring_ = false;
1738 chainAnimation_->SetControlIndex(index);
1739 chainAnimation_->SetMaxIndex(maxListItemIndex_);
1740 }
1741
ProcessDragUpdate(float dragOffset,int32_t source)1742 void ListPattern::ProcessDragUpdate(float dragOffset, int32_t source)
1743 {
1744 CHECK_NULL_VOID(chainAnimation_);
1745 if (NearZero(dragOffset) || source == SCROLL_FROM_BAR || source == SCROLL_FROM_AXIS ||
1746 source == SCROLL_FROM_BAR_FLING) {
1747 return;
1748 }
1749 if (NeedScrollSnapAlignEffect()) {
1750 auto delta = 0.0f;
1751 if (chainAnimation_->GetControlIndex() < startIndex_ - 1) {
1752 delta = chainAnimation_->SetControlIndex(std::max(startIndex_ - 1, 0));
1753 }
1754 if (chainAnimation_->GetControlIndex() > endIndex_ + 1) {
1755 delta = chainAnimation_->SetControlIndex(std::min(endIndex_ + 1, maxListItemIndex_));
1756 }
1757 if (!NearZero(delta)) {
1758 auto scrollableEvent = GetScrollableEvent();
1759 CHECK_NULL_VOID(scrollableEvent);
1760 auto scrollable = scrollableEvent->GetScrollable();
1761 CHECK_NULL_VOID(scrollable);
1762 scrollable->UpdateScrollSnapStartOffset(delta);
1763 currentDelta_ -= delta;
1764 }
1765 }
1766 bool overDrag = (source == SCROLL_FROM_UPDATE) && (IsAtTop() || IsAtBottom());
1767 chainAnimation_->SetDelta(-dragOffset, overDrag);
1768 }
1769
GetChainDelta(int32_t index) const1770 float ListPattern::GetChainDelta(int32_t index) const
1771 {
1772 CHECK_NULL_RETURN(chainAnimation_, 0.0f);
1773 return chainAnimation_->GetValue(index);
1774 }
1775
MultiSelectWithoutKeyboard(const RectF & selectedZone)1776 void ListPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
1777 {
1778 auto host = GetHost();
1779 CHECK_NULL_VOID(host);
1780 std::list<RefPtr<FrameNode>> childrens;
1781 host->GenerateOneDepthVisibleFrame(childrens);
1782 for (const auto& item : childrens) {
1783 if (item->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
1784 auto itemGroupPattern = item->GetPattern<ListItemGroupPattern>();
1785 CHECK_NULL_VOID(itemGroupPattern);
1786 auto itemGroupGeometry = item->GetGeometryNode();
1787 CHECK_NULL_VOID(itemGroupGeometry);
1788 auto itemGroupRect = itemGroupGeometry->GetFrameRect();
1789 if (!selectedZone.IsIntersectWith(itemGroupRect)) {
1790 continue;
1791 }
1792 HandleCardModeSelectedEvent(selectedZone, item, itemGroupRect.Top());
1793 continue;
1794 }
1795 auto itemPattern = item->GetPattern<ListItemPattern>();
1796 CHECK_NULL_VOID(itemPattern);
1797 if (!itemPattern->Selectable()) {
1798 continue;
1799 }
1800
1801 auto itemGeometry = item->GetGeometryNode();
1802 CHECK_NULL_VOID(itemGeometry);
1803
1804 auto itemRect = itemGeometry->GetFrameRect();
1805 if (!selectedZone.IsIntersectWith(itemRect)) {
1806 itemPattern->MarkIsSelected(false);
1807 } else {
1808 itemPattern->MarkIsSelected(true);
1809 }
1810 }
1811
1812 DrawSelectedZone(selectedZone);
1813 }
1814
HandleCardModeSelectedEvent(const RectF & selectedZone,const RefPtr<FrameNode> & itemGroupNode,float itemGroupTop)1815 void ListPattern::HandleCardModeSelectedEvent(
1816 const RectF& selectedZone, const RefPtr<FrameNode>& itemGroupNode, float itemGroupTop)
1817 {
1818 CHECK_NULL_VOID(itemGroupNode);
1819 std::list<RefPtr<FrameNode>> childrens;
1820 itemGroupNode->GenerateOneDepthVisibleFrame(childrens);
1821 for (const auto& item : childrens) {
1822 auto itemPattern = item->GetPattern<ListItemPattern>();
1823 if (!itemPattern) {
1824 continue;
1825 }
1826 if (!itemPattern->Selectable()) {
1827 continue;
1828 }
1829 auto itemGeometry = item->GetGeometryNode();
1830 CHECK_NULL_VOID(itemGeometry);
1831 auto context = item->GetRenderContext();
1832 CHECK_NULL_VOID(context);
1833 auto itemRect = itemGeometry->GetFrameRect();
1834 RectF itemRectInGroup(itemRect.GetX(), itemRect.GetY() + itemGroupTop, itemRect.Width(), itemRect.Height());
1835 if (!selectedZone.IsIntersectWith(itemRectInGroup)) {
1836 itemPattern->MarkIsSelected(false);
1837 } else {
1838 itemPattern->MarkIsSelected(true);
1839 }
1840 }
1841 }
1842
ClearMultiSelect()1843 void ListPattern::ClearMultiSelect()
1844 {
1845 auto host = GetHost();
1846 CHECK_NULL_VOID(host);
1847 std::list<RefPtr<FrameNode>> children;
1848 host->GenerateOneDepthAllFrame(children);
1849 for (const auto& child : children) {
1850 if (!child) {
1851 continue;
1852 }
1853 auto itemPattern = child->GetPattern<ListItemPattern>();
1854 if (itemPattern) {
1855 itemPattern->MarkIsSelected(false);
1856 continue;
1857 }
1858 auto itemGroupPattern = child->GetPattern<ListItemGroupPattern>();
1859 if (itemGroupPattern) {
1860 std::list<RefPtr<FrameNode>> itemChildren;
1861 child->GenerateOneDepthAllFrame(itemChildren);
1862 for (const auto& item : itemChildren) {
1863 if (!item) {
1864 continue;
1865 }
1866 itemPattern = item->GetPattern<ListItemPattern>();
1867 if (itemPattern) {
1868 itemPattern->MarkIsSelected(false);
1869 }
1870 }
1871 }
1872 }
1873
1874 ClearSelectedZone();
1875 }
1876
IsItemSelected(const GestureEvent & info)1877 bool ListPattern::IsItemSelected(const GestureEvent& info)
1878 {
1879 auto host = GetHost();
1880 CHECK_NULL_RETURN(host, false);
1881 auto node = host->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
1882 CHECK_NULL_RETURN(node, false);
1883 auto itemPattern = node->GetPattern<ListItemPattern>();
1884 if (itemPattern) {
1885 return itemPattern->IsSelected();
1886 }
1887 auto itemGroupPattern = node->GetPattern<ListItemGroupPattern>();
1888 if (itemGroupPattern) {
1889 auto itemNode = node->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
1890 CHECK_NULL_RETURN(itemNode, false);
1891 itemPattern = itemNode->GetPattern<ListItemPattern>();
1892 CHECK_NULL_RETURN(itemPattern, false);
1893 return itemPattern->IsSelected();
1894 }
1895 return false;
1896 }
1897
SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)1898 void ListPattern::SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)
1899 {
1900 // swiper item only can be replaced when no other items be dragged
1901 if (canReplaceSwiperItem_) {
1902 if (swiperItem != swiperItem_) {
1903 auto item = swiperItem_.Upgrade();
1904 if (item) {
1905 item->SwiperReset();
1906 }
1907 swiperItem_ = std::move(swiperItem);
1908 }
1909 canReplaceSwiperItem_ = false;
1910 }
1911 FireAndCleanScrollingListener();
1912 }
1913
GetItemIndexByPosition(float xOffset,float yOffset)1914 int32_t ListPattern::GetItemIndexByPosition(float xOffset, float yOffset)
1915 {
1916 auto host = GetHost();
1917 auto globalOffset = host->GetTransformRelativeOffset();
1918 float relativeX = xOffset - globalOffset.GetX();
1919 float relativeY = yOffset - globalOffset.GetY();
1920 float mainOffset = GetAxis() == Axis::VERTICAL ? relativeY : relativeX;
1921 float crossOffset = GetAxis() == Axis::VERTICAL ? relativeX : relativeY;
1922 float crossSize = GetCrossAxisSize(GetContentSize(), GetAxis());
1923 int32_t lanesOffset = 0;
1924 if (lanes_ > 1) {
1925 lanesOffset = static_cast<int32_t>(crossOffset / (crossSize / lanes_));
1926 }
1927 for (auto& pos : itemPosition_) {
1928 if (mainOffset <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1929 return std::min(pos.first + lanesOffset, maxListItemIndex_ + 1);
1930 }
1931 }
1932 if (!itemPosition_.empty()) {
1933 return itemPosition_.rbegin()->first + 1;
1934 }
1935 return 0;
1936 }
1937
ToJsonValue(std::unique_ptr<JsonValue> & json) const1938 void ListPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
1939 {
1940 ScrollablePattern::ToJsonValue(json);
1941 json->Put("multiSelectable", multiSelectable_);
1942 json->Put("startIndex", startIndex_);
1943 if (!itemPosition_.empty()) {
1944 json->Put("itemStartPos", itemPosition_.begin()->second.startPos);
1945 }
1946 }
1947
FromJson(const std::unique_ptr<JsonValue> & json)1948 void ListPattern::FromJson(const std::unique_ptr<JsonValue>& json)
1949 {
1950 ScrollToIndex(json->GetInt("startIndex"));
1951 if (json->Contains("itemStartPos")) {
1952 ScrollBy(-json->GetDouble("itemStartPos"));
1953 }
1954 auto host = GetHost();
1955 CHECK_NULL_VOID(host);
1956 host->GetRenderContext()->UpdateClipEdge(true);
1957 ScrollablePattern::FromJson(json);
1958 }
1959
SetAccessibilityAction()1960 void ListPattern::SetAccessibilityAction()
1961 {
1962 auto host = GetHost();
1963 CHECK_NULL_VOID(host);
1964 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1965 CHECK_NULL_VOID(accessibilityProperty);
1966 accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1967 const auto& pattern = weakPtr.Upgrade();
1968 CHECK_NULL_VOID(pattern);
1969 if (!pattern->IsScrollable()) {
1970 return;
1971 }
1972 pattern->ScrollPage(false);
1973 });
1974
1975 accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1976 const auto& pattern = weakPtr.Upgrade();
1977 CHECK_NULL_VOID(pattern);
1978 if (!pattern->IsScrollable()) {
1979 return;
1980 }
1981 pattern->ScrollPage(true);
1982 });
1983 }
1984
GetListItemGroupParameter(const RefPtr<FrameNode> & node)1985 ListItemGroupPara ListPattern::GetListItemGroupParameter(const RefPtr<FrameNode>& node)
1986 {
1987 ListItemGroupPara listItemGroupPara = { -1, -1, -1, -1 };
1988 auto curFrameParent = node->GetParent();
1989 auto curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
1990 while (curFrameParent && (!curFrameParentNode)) {
1991 curFrameParent = curFrameParent->GetParent();
1992 curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
1993 }
1994 CHECK_NULL_RETURN(curFrameParentNode, listItemGroupPara);
1995 if (curFrameParent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
1996 auto itemGroupPattern = curFrameParentNode->GetPattern<ListItemGroupPattern>();
1997 CHECK_NULL_RETURN(itemGroupPattern, listItemGroupPara);
1998 listItemGroupPara.displayEndIndex = itemGroupPattern->GetDisplayEndIndexInGroup();
1999 listItemGroupPara.displayStartIndex = itemGroupPattern->GetDisplayStartIndexInGroup();
2000 listItemGroupPara.lanes = itemGroupPattern->GetLanesInGroup();
2001 listItemGroupPara.itemEndIndex = itemGroupPattern->GetEndIndexInGroup();
2002 }
2003 return listItemGroupPara;
2004 }
2005
IsListItemGroup(int32_t listIndex,RefPtr<FrameNode> & node)2006 bool ListPattern::IsListItemGroup(int32_t listIndex, RefPtr<FrameNode>& node)
2007 {
2008 auto listFrame = GetHost();
2009 CHECK_NULL_RETURN(listFrame, false);
2010 auto listFocus = listFrame->GetFocusHub();
2011 CHECK_NULL_RETURN(listFocus, false);
2012 for (const auto& childFocus : listFocus->GetChildren()) {
2013 if (!childFocus->IsFocusable()) {
2014 continue;
2015 }
2016 if (auto childFrame = childFocus->GetFrameNode()) {
2017 if (auto childPattern = AceType::DynamicCast<ListItemPattern>(childFrame->GetPattern())) {
2018 auto curIndex = childPattern->GetIndexInList();
2019 auto curIndexInGroup = childPattern->GetIndexInListItemGroup();
2020 if (curIndex == listIndex) {
2021 node = childFrame;
2022 return curIndexInGroup > -1;
2023 }
2024 }
2025 }
2026 }
2027 return false;
2028 }
2029
RefreshLanesItemRange()2030 void ListPattern::RefreshLanesItemRange()
2031 {
2032 auto host = GetHost();
2033 CHECK_NULL_VOID(host);
2034 auto updatePos = host->GetChildrenUpdated();
2035 if (updatePos == -1) {
2036 return;
2037 }
2038 if (updatePos == 0) {
2039 lanesItemRange_.clear();
2040 return;
2041 }
2042 for (auto it = lanesItemRange_.begin(); it != lanesItemRange_.end();) {
2043 if (it->second < updatePos) {
2044 it++;
2045 } else if (it->first >= updatePos) {
2046 lanesItemRange_.erase(it++);
2047 } else {
2048 it->second = updatePos - 1;
2049 it++;
2050 }
2051 }
2052 }
2053
ProvideRestoreInfo()2054 std::string ListPattern::ProvideRestoreInfo()
2055 {
2056 return std::to_string(startIndex_);
2057 }
2058
CloseAllSwipeActions(OnFinishFunc && onFinishCallback)2059 void ListPattern::CloseAllSwipeActions(OnFinishFunc&& onFinishCallback)
2060 {
2061 auto item = swiperItem_.Upgrade();
2062 if (item) {
2063 return item->CloseSwipeAction(std::move(onFinishCallback));
2064 }
2065 }
2066
OnRestoreInfo(const std::string & restoreInfo)2067 void ListPattern::OnRestoreInfo(const std::string& restoreInfo)
2068 {
2069 jumpIndex_ = StringUtils::StringToInt(restoreInfo);
2070 }
2071
DumpAdvanceInfo()2072 void ListPattern::DumpAdvanceInfo()
2073 {
2074 DumpLog::GetInstance().AddDesc("maxListItemIndex:" + std::to_string(maxListItemIndex_));
2075 DumpLog::GetInstance().AddDesc("startIndex:" + std::to_string(startIndex_));
2076 DumpLog::GetInstance().AddDesc("endIndex:" + std::to_string(endIndex_));
2077 DumpLog::GetInstance().AddDesc("centerIndex:" + std::to_string(centerIndex_));
2078 DumpLog::GetInstance().AddDesc("startMainPos:" + std::to_string(startMainPos_));
2079 DumpLog::GetInstance().AddDesc("endMainPos:" + std::to_string(endMainPos_));
2080 DumpLog::GetInstance().AddDesc("currentOffset:" + std::to_string(currentOffset_));
2081 DumpLog::GetInstance().AddDesc("contentMainSize:" + std::to_string(contentMainSize_));
2082 DumpLog::GetInstance().AddDesc("contentStartOffset:" + std::to_string(contentStartOffset_));
2083 DumpLog::GetInstance().AddDesc("contentEndOffset:" + std::to_string(contentEndOffset_));
2084 DumpLog::GetInstance().AddDesc("currentDelta:" + std::to_string(currentDelta_));
2085 crossMatchChild_ ? DumpLog::GetInstance().AddDesc("crossMatchChild:true")
2086 : DumpLog::GetInstance().AddDesc("crossMatchChild:false");
2087 smooth_ ? DumpLog::GetInstance().AddDesc("smooth:true") : DumpLog::GetInstance().AddDesc("smooth:false");
2088 if (jumpIndex_.has_value()) {
2089 DumpLog::GetInstance().AddDesc("jumpIndex:" + std::to_string(jumpIndex_.value()));
2090 } else {
2091 DumpLog::GetInstance().AddDesc("jumpIndex:null");
2092 }
2093 if (jumpIndexInGroup_.has_value()) {
2094 DumpLog::GetInstance().AddDesc("jumpIndexInGroup:" + std::to_string(jumpIndexInGroup_.value()));
2095 } else {
2096 DumpLog::GetInstance().AddDesc("jumpIndexInGroup:null");
2097 }
2098 if (targetIndex_.has_value()) {
2099 DumpLog::GetInstance().AddDesc("targetIndex:" + std::to_string(targetIndex_.value()));
2100 } else {
2101 DumpLog::GetInstance().AddDesc("targetIndex:null");
2102 }
2103 if (predictSnapOffset_.has_value()) {
2104 DumpLog::GetInstance().AddDesc("predictSnapOffset:" + std::to_string(predictSnapOffset_.value()));
2105 } else {
2106 DumpLog::GetInstance().AddDesc("predictSnapOffset:null");
2107 }
2108 if (predictSnapEndPos_.has_value()) {
2109 DumpLog::GetInstance().AddDesc("predictSnapEndPos:" + std::to_string(predictSnapEndPos_.value()));
2110 } else {
2111 DumpLog::GetInstance().AddDesc("predictSnapEndPos:null");
2112 }
2113 // DumpLog::GetInstance().AddDesc("scrollAlign:%{public}d", scrollAlign_);
2114 scrollable_ ? DumpLog::GetInstance().AddDesc("scrollable:true")
2115 : DumpLog::GetInstance().AddDesc("scrollable:false");
2116 paintStateFlag_ ? DumpLog::GetInstance().AddDesc("paintStateFlag:true")
2117 : DumpLog::GetInstance().AddDesc("paintStateFlag:false");
2118 isFramePaintStateValid_ ? DumpLog::GetInstance().AddDesc("isFramePaintStateValid:true")
2119 : DumpLog::GetInstance().AddDesc("isFramePaintStateValid:false");
2120 for (auto item : itemPosition_) {
2121 DumpLog::GetInstance().AddDesc("------------------------------------------");
2122 DumpLog::GetInstance().AddDesc("itemPosition.first:" + std::to_string(item.first));
2123 DumpLog::GetInstance().AddDesc("startPos:" + std::to_string(item.second.startPos));
2124 DumpLog::GetInstance().AddDesc("endPos:" + std::to_string(item.second.endPos));
2125 DumpLog::GetInstance().AddDesc("isGroup:" + std::to_string(item.second.isGroup));
2126 }
2127 DumpLog::GetInstance().AddDesc("------------------------------------------");
2128 scrollStop_ ? DumpLog::GetInstance().AddDesc("scrollStop:true")
2129 : DumpLog::GetInstance().AddDesc("scrollStop:false");
2130 for (auto item : lanesItemRange_) {
2131 DumpLog::GetInstance().AddDesc("------------------------------------------");
2132 DumpLog::GetInstance().AddDesc("lanesItemRange.first:" + std::to_string(item.first));
2133 DumpLog::GetInstance().AddDesc("lanesItemRange.second:" + std::to_string(item.second));
2134 }
2135 DumpLog::GetInstance().AddDesc("------------------------------------------");
2136 DumpLog::GetInstance().AddDesc("lanes:" + std::to_string(lanes_));
2137 DumpLog::GetInstance().AddDesc("laneGutter:" + std::to_string(laneGutter_));
2138 dragFromSpring_ ? DumpLog::GetInstance().AddDesc("dragFromSpring:true")
2139 : DumpLog::GetInstance().AddDesc("dragFromSpring:false");
2140 isScrollEnd_ ? DumpLog::GetInstance().AddDesc("isScrollEnd:true")
2141 : DumpLog::GetInstance().AddDesc("isScrollEnd:false");
2142 IsAtTop() ? DumpLog::GetInstance().AddDesc("IsAtTop:true") : DumpLog::GetInstance().AddDesc("IsAtTop:false");
2143 IsAtBottom() ? DumpLog::GetInstance().AddDesc("IsAtBottom:true")
2144 : DumpLog::GetInstance().AddDesc("IsAtBottom:false");
2145 }
2146
GetDefaultScrollBarDisplayMode() const2147 DisplayMode ListPattern::GetDefaultScrollBarDisplayMode() const
2148 {
2149 auto defaultDisplayMode = DisplayMode::OFF;
2150 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
2151 defaultDisplayMode = DisplayMode::AUTO;
2152 }
2153 return defaultDisplayMode;
2154 }
2155 } // namespace OHOS::Ace::NG
2156