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 "base/geometry/axis.h"
19 #include "base/memory/referenced.h"
20 #include "base/utils/utils.h"
21 #include "base/perfmonitor/perf_monitor.h"
22 #include "base/perfmonitor/perf_constants.h"
23 #include "core/animation/bilateral_spring_node.h"
24 #include "core/animation/spring_model.h"
25 #include "core/components/common/layout/constants.h"
26 #include "core/components/scroll/scroll_bar_theme.h"
27 #include "core/components/scroll/scrollable.h"
28 #include "core/components_ng/pattern/list/list_item_pattern.h"
29 #include "core/components_ng/pattern/list/list_lanes_layout_algorithm.h"
30 #include "core/components_ng/pattern/list/list_layout_algorithm.h"
31 #include "core/components_ng/pattern/list/list_layout_property.h"
32 #include "core/components_ng/pattern/scroll/effect/scroll_fade_effect.h"
33 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
34 #include "core/components_ng/property/measure_utils.h"
35 #include "core/components_ng/property/property.h"
36 #include "core/components_v2/inspector/inspector_constants.h"
37 #include "core/components_ng/pattern/list/list_item_group_pattern.h"
38
39 namespace OHOS::Ace::NG {
40 namespace {
41 constexpr Dimension CHAIN_INTERVAL_DEFAULT = 20.0_vp;
42 constexpr double CHAIN_SPRING_MASS = 1.0;
43 constexpr double CHAIN_SPRING_DAMPING = 30.0;
44 constexpr double CHAIN_SPRING_STIFFNESS = 228;
45 constexpr float DEFAULT_MIN_SPACE_SCALE = 0.75f;
46 constexpr float DEFAULT_MAX_SPACE_SCALE = 2.0f;
47 } // namespace
48
OnModifyDone()49 void ListPattern::OnModifyDone()
50 {
51 if (!isInitialized_) {
52 jumpIndex_ = GetLayoutProperty<ListLayoutProperty>()->GetInitialIndex().value_or(0);
53 if (NeedScrollSnapAlignEffect()) {
54 scrollAlign_ = GetScrollAlignByScrollSnapAlign();
55 }
56 }
57 auto host = GetHost();
58 CHECK_NULL_VOID(host);
59 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
60 CHECK_NULL_VOID(listLayoutProperty);
61 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
62 SetAxis(axis);
63 if (!GetScrollableEvent()) {
64 InitScrollableEvent();
65 }
66 auto edgeEffect = listLayoutProperty->GetEdgeEffect().value_or(EdgeEffect::SPRING);
67 SetEdgeEffect(edgeEffect);
68
69 auto defaultDisplayMode = DisplayMode::OFF;
70 const static int32_t PLATFORM_VERSION_TEN = 10;
71 auto pipeline = PipelineContext::GetCurrentContext();
72 CHECK_NULL_VOID(pipeline);
73 if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
74 defaultDisplayMode = DisplayMode::AUTO;
75 }
76 auto listPaintProperty = host->GetPaintProperty<ListPaintProperty>();
77 SetScrollBar(listPaintProperty->GetBarDisplayMode().value_or(defaultDisplayMode));
78 SetChainAnimation();
79 if (multiSelectable_ && !isMouseEventInit_) {
80 InitMouseEvent();
81 }
82 if (!multiSelectable_ && isMouseEventInit_) {
83 UninitMouseEvent();
84 }
85 auto focusHub = host->GetFocusHub();
86 CHECK_NULL_VOID_NOLOG(focusHub);
87 InitOnKeyEvent(focusHub);
88 SetAccessibilityAction();
89 }
90
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)91 bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
92 {
93 if (config.skipMeasure && config.skipLayout) {
94 return false;
95 }
96 bool isJump = false;
97 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
98 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
99 auto listLayoutAlgorithm = DynamicCast<ListLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
100 CHECK_NULL_RETURN(listLayoutAlgorithm, false);
101 itemPosition_ = listLayoutAlgorithm->GetItemPosition();
102 maxListItemIndex_ = listLayoutAlgorithm->GetMaxListItemIndex();
103 spaceWidth_ = listLayoutAlgorithm->GetSpaceWidth();
104 float relativeOffset = listLayoutAlgorithm->GetCurrentOffset();
105 auto predictSnapOffset = listLayoutAlgorithm->GetPredictSnapOffset();
106 if (listLayoutAlgorithm->GetEstimateOffset().has_value()) {
107 float absoluteOffset = listLayoutAlgorithm->GetEstimateOffset().value_or(currentOffset_);
108 relativeOffset += absoluteOffset - currentOffset_;
109 isJump = true;
110 }
111 if (targetIndex_) {
112 auto iter = itemPosition_.find(targetIndex_.value());
113 if (iter != itemPosition_.end()) {
114 float targetPos = 0.0f;
115 switch (scrollAlign_) {
116 case ScrollAlign::START:
117 case ScrollAlign::NONE:
118 targetPos = iter->second.startPos;
119 break;
120 case ScrollAlign::CENTER:
121 targetPos = (iter->second.endPos + iter->second.startPos) / 2.0f - contentMainSize_ / 2.0f;
122 break;
123 case ScrollAlign::END:
124 targetPos = iter->second.endPos - contentMainSize_;
125 break;
126 case ScrollAlign::AUTO:
127 ScrollAutoType scrollAutoType = listLayoutAlgorithm->GetScrollAutoType();
128 targetPos = CalculateTargetPos(iter->second.startPos, iter->second.endPos, scrollAutoType);
129 break;
130 }
131 // correct the currentOffset when the startIndex is 0.
132 if (listLayoutAlgorithm->GetStartIndex() == 0) {
133 currentOffset_ = -itemPosition_.begin()->second.startPos;
134 } else {
135 currentOffset_ = currentOffset_ + relativeOffset;
136 }
137 AnimateTo(targetPos + currentOffset_, -1, nullptr, true);
138 }
139 targetIndex_.reset();
140 } else {
141 if (listLayoutAlgorithm->GetStartIndex() == 0) {
142 currentOffset_ = -itemPosition_.begin()->second.startPos;
143 } else {
144 currentOffset_ = currentOffset_ + relativeOffset;
145 }
146 }
147 if (predictSnapOffset.has_value()) {
148 if (scrollableTouchEvent_) {
149 scrollableTouchEvent_->StartScrollSnapMotion(predictSnapOffset.value(), scrollSnapVelocity_);
150 scrollSnapVelocity_ = 0.0f;
151 }
152 predictSnapOffset_.reset();
153 }
154 if (isScrollEnd_) {
155 auto host = GetHost();
156 CHECK_NULL_RETURN(host, false);
157 host->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
158 isScrollEnd_ = false;
159 }
160 currentDelta_ = 0.0f;
161 float prevStartOffset = startMainPos_;
162 float prevEndOffset = endMainPos_ - contentMainSize_;
163 contentMainSize_ = listLayoutAlgorithm->GetContentMainSize();
164 contentStartOffset_ = listLayoutAlgorithm->GetContentStartOffset();
165 contentEndOffset_ = listLayoutAlgorithm->GetContentEndOffset();
166 startMainPos_ = listLayoutAlgorithm->GetStartPosition();
167 endMainPos_ = listLayoutAlgorithm->GetEndPosition();
168 crossMatchChild_ = listLayoutAlgorithm->IsCrossMatchChild();
169 auto lanesLayoutAlgorithm = DynamicCast<ListLanesLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
170 if (lanesLayoutAlgorithm) {
171 lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
172 lanes_ = lanesLayoutAlgorithm->GetLanes();
173 laneGutter_ = lanesLayoutAlgorithm->GetLaneGutter();
174 }
175 CheckScrollable();
176
177 bool indexChanged = false;
178 const static int32_t PLATFORM_VERSION_TEN = 10;
179 auto pipeline = PipelineContext::GetCurrentContext();
180 CHECK_NULL_RETURN(pipeline, false);
181 if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN) {
182 indexChanged = (startIndex_ != listLayoutAlgorithm->GetStartIndex()) ||
183 (endIndex_ != listLayoutAlgorithm->GetEndIndex()) ||
184 (centerIndex_ != listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty)));
185 } else {
186 indexChanged =
187 (startIndex_ != listLayoutAlgorithm->GetStartIndex()) || (endIndex_ != listLayoutAlgorithm->GetEndIndex());
188 }
189 if (indexChanged) {
190 startIndex_ = listLayoutAlgorithm->GetStartIndex();
191 endIndex_ = listLayoutAlgorithm->GetEndIndex();
192 centerIndex_ = listLayoutAlgorithm->GetMidIndex(AceType::RawPtr(dirty));
193 }
194 ProcessEvent(indexChanged, relativeOffset, isJump, prevStartOffset, prevEndOffset);
195 UpdateScrollBarOffset();
196 if (config.frameSizeChange) {
197 if (GetScrollBar() != nullptr) {
198 GetScrollBar()->ScheduleDisapplearDelayTask();
199 }
200 }
201 CheckRestartSpring();
202
203 DrivenRender(dirty);
204
205 SetScrollSource(SCROLL_FROM_NONE);
206 isInitialized_ = true;
207 MarkSelectedItems();
208 return true;
209 }
210
GetScrollAlignByScrollSnapAlign() const211 ScrollAlign ListPattern::GetScrollAlignByScrollSnapAlign() const
212 {
213 auto scrollAlign = ScrollAlign::START;
214 auto host = GetHost();
215 CHECK_NULL_RETURN(host, scrollAlign);
216 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
217 CHECK_NULL_RETURN(listProperty, scrollAlign);
218 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
219 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
220 scrollAlign = ScrollAlign::CENTER;
221 }
222 return scrollAlign;
223 }
224
CalculateTargetPos(float startPos,float endPos,ScrollAutoType scrollAutoType)225 float ListPattern::CalculateTargetPos(float startPos, float endPos, ScrollAutoType scrollAutoType)
226 {
227 float targetPos = 0.0f;
228 switch (scrollAutoType) {
229 case ScrollAutoType::NOT_CHANGE:
230 LOGI("item is fully visible, no need to scroll.");
231 break;
232 case ScrollAutoType::START:
233 targetPos = startPos;
234 break;
235 case ScrollAutoType::END:
236 targetPos = endPos - contentMainSize_;
237 break;
238 }
239 return targetPos;
240 }
241
CreateNodePaintMethod()242 RefPtr<NodePaintMethod> ListPattern::CreateNodePaintMethod()
243 {
244 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
245 V2::ItemDivider divider;
246 if (!chainAnimation_ && listLayoutProperty->HasDivider()) {
247 divider = listLayoutProperty->GetDivider().value();
248 }
249 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
250 auto drawVertical = (axis == Axis::HORIZONTAL);
251 auto paint = MakeRefPtr<ListPaintMethod>(divider, drawVertical, lanes_, spaceWidth_);
252 paint->SetScrollBar(GetScrollBar());
253 CreateScrollBarOverlayModifier();
254 paint->SetScrollBarOverlayModifier(GetScrollBarOverlayModifier());
255 paint->SetTotalItemCount(maxListItemIndex_ + 1);
256 auto scrollEffect = GetScrollEdgeEffect();
257 if (scrollEffect && scrollEffect->IsFadeEffect()) {
258 paint->SetEdgeEffect(scrollEffect);
259 }
260 if (!listContentModifier_) {
261 auto host = GetHost();
262 CHECK_NULL_RETURN(host, paint);
263 const auto& geometryNode = host->GetGeometryNode();
264 auto size = geometryNode->GetPaddingSize();
265 OffsetF offset = geometryNode->GetPaddingOffset() - geometryNode->GetFrameOffset();
266 listContentModifier_ = AceType::MakeRefPtr<ListContentModifier>(offset, size);
267 }
268
269 paint->SetLaneGutter(laneGutter_);
270 listContentModifier_->SetItemsPosition(itemPosition_);
271 paint->SetContentModifier(listContentModifier_);
272 return paint;
273 }
274
ProcessEvent(bool indexChanged,float finalOffset,bool isJump,float prevStartOffset,float prevEndOffset)275 void ListPattern::ProcessEvent(
276 bool indexChanged, float finalOffset, bool isJump, float prevStartOffset, float prevEndOffset)
277 {
278 auto host = GetHost();
279 CHECK_NULL_VOID(host);
280 auto listEventHub = host->GetEventHub<ListEventHub>();
281 CHECK_NULL_VOID(listEventHub);
282
283 paintStateFlag_ = !NearZero(finalOffset) && !isJump;
284 isFramePaintStateValid_ = true;
285 const static int32_t PLATFORM_VERSION_TEN = 10;
286 auto pipeline = PipelineContext::GetCurrentContext();
287 CHECK_NULL_VOID(pipeline);
288 auto onScroll = listEventHub->GetOnScroll();
289 if (pipeline->GetMinPlatformVersion() >= PLATFORM_VERSION_TEN && scrollStop_ && !GetScrollAbort()) {
290 auto source = GetScrollSource();
291 auto offsetPX = Dimension(finalOffset);
292 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
293 if (onScroll) {
294 if (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR) {
295 onScroll(offsetVP, ScrollState::SCROLL);
296 onScroll(0.0_vp, ScrollState::IDLE);
297 } else if (source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING ||
298 source == SCROLL_FROM_ANIMATION_CONTROLLER || source == SCROLL_FROM_BAR_FLING) {
299 onScroll(offsetVP, ScrollState::FLING);
300 onScroll(0.0_vp, ScrollState::IDLE);
301 } else {
302 onScroll(offsetVP, ScrollState::IDLE);
303 }
304 }
305 } else if (onScroll && !NearZero(finalOffset)) {
306 auto source = GetScrollSource();
307 auto offsetPX = Dimension(finalOffset);
308 auto offsetVP = Dimension(offsetPX.ConvertToVp(), DimensionUnit::VP);
309 if (pipeline->GetMinPlatformVersion() < PLATFORM_VERSION_TEN &&
310 (source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR || source == SCROLL_FROM_ANIMATION_CONTROLLER)) {
311 source = SCROLL_FROM_NONE;
312 }
313 if (source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_AXIS || source == SCROLL_FROM_BAR) {
314 onScroll(offsetVP, ScrollState::SCROLL);
315 } else if (source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING ||
316 source == SCROLL_FROM_ANIMATION_CONTROLLER || source == SCROLL_FROM_BAR_FLING) {
317 onScroll(offsetVP, ScrollState::FLING);
318 } else {
319 onScroll(offsetVP, ScrollState::IDLE);
320 }
321 }
322
323 if (indexChanged) {
324 auto onScrollIndex = listEventHub->GetOnScrollIndex();
325 if (onScrollIndex) {
326 int32_t startIndex = startIndex_ == -1 ? 0 : startIndex_;
327 int32_t endIndex = endIndex_ == -1 ? 0 : endIndex_;
328 onScrollIndex(startIndex, endIndex, centerIndex_);
329 }
330 }
331
332 auto onReachStart = listEventHub->GetOnReachStart();
333 if (onReachStart && (startIndex_ == 0)) {
334 bool scrollUpToStart = Positive(prevStartOffset) && NonPositive(startMainPos_);
335 bool scrollDownToStart = (Negative(prevStartOffset) || !isInitialized_) && NonNegative(startMainPos_);
336 if (scrollUpToStart || scrollDownToStart) {
337 onReachStart();
338 }
339 }
340 auto onReachEnd = listEventHub->GetOnReachEnd();
341 if (onReachEnd && (endIndex_ == maxListItemIndex_)) {
342 float endOffset = endMainPos_ - contentMainSize_;
343 bool scrollUpToEnd = (Positive(prevEndOffset) || !isInitialized_) && NonPositive(endOffset);
344 bool scrollDownToEnd = Negative(prevEndOffset) && NonNegative(endOffset);
345 if (scrollUpToEnd || (scrollDownToEnd && GetScrollSource() != SCROLL_FROM_NONE)) {
346 onReachEnd();
347 }
348 }
349
350 if (scrollStop_) {
351 auto onScrollStop = listEventHub->GetOnScrollStop();
352 if (!GetScrollAbort()) {
353 if (onScrollStop) {
354 SetScrollSource(SCROLL_FROM_NONE);
355 onScrollStop();
356 }
357 auto scrollBar = GetScrollBar();
358 if (scrollBar) {
359 scrollBar->ScheduleDisapplearDelayTask();
360 }
361 StartScrollBarAnimatorByProxy();
362 }
363 if (!GetScrollAbort()) {
364 PerfMonitor::GetPerfMonitor()->End(PerfConstants::APP_LIST_FLING, false);
365 }
366 scrollStop_ = false;
367 SetScrollAbort(false);
368 }
369 }
370
DrivenRender(const RefPtr<LayoutWrapper> & layoutWrapper)371 void ListPattern::DrivenRender(const RefPtr<LayoutWrapper>& layoutWrapper)
372 {
373 auto host = GetHost();
374 CHECK_NULL_VOID(host);
375 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
376 auto listPaintProperty = host->GetPaintProperty<ListPaintProperty>();
377 auto axis = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL);
378 auto stickyStyle = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE);
379 bool barNeedPaint = GetScrollBar() ? GetScrollBar()->NeedPaint() : false;
380 auto chainAnimation = listLayoutProperty->GetChainAnimation().value_or(false);
381 bool drivenRender = !(axis != Axis::VERTICAL || stickyStyle != V2::StickyStyle::NONE ||
382 barNeedPaint || chainAnimation || !scrollable_);
383
384 auto renderContext = host->GetRenderContext();
385 CHECK_NULL_VOID(renderContext);
386 renderContext->MarkDrivenRender(drivenRender);
387 if (drivenRender && isFramePaintStateValid_) {
388 // Mark items
389 int32_t indexStep = 0;
390 int32_t startIndex = itemPosition_.empty() ? 0 : itemPosition_.begin()->first;
391 for (auto& pos : itemPosition_) {
392 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first);
393 CHECK_NULL_VOID(wrapper);
394 auto itemHost = wrapper->GetHostNode();
395 CHECK_NULL_VOID(itemHost);
396 auto itemRenderContext = itemHost->GetRenderContext();
397 CHECK_NULL_VOID(itemRenderContext);
398 itemRenderContext->MarkDrivenRenderItemIndex(startIndex + indexStep);
399 indexStep++;
400 }
401 renderContext->MarkDrivenRenderFramePaintState(paintStateFlag_);
402 isFramePaintStateValid_ = false;
403 }
404 }
405
CheckScrollable()406 void ListPattern::CheckScrollable()
407 {
408 auto host = GetHost();
409 CHECK_NULL_VOID(host);
410 auto hub = host->GetEventHub<EventHub>();
411 CHECK_NULL_VOID(hub);
412 auto gestureHub = hub->GetOrCreateGestureEventHub();
413 CHECK_NULL_VOID(gestureHub);
414 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
415 CHECK_NULL_VOID(listProperty);
416 if (itemPosition_.empty()) {
417 scrollable_ = false;
418 } else {
419 if ((itemPosition_.begin()->first == 0) && (itemPosition_.rbegin()->first == maxListItemIndex_) &&
420 !IsScrollSnapAlignCenter()) {
421 scrollable_ = GreatNotEqual((endMainPos_ - startMainPos_), contentMainSize_);
422 } else {
423 scrollable_ = true;
424 }
425 }
426
427 SetScrollEnable(scrollable_);
428
429 if (!listProperty->GetScrollEnabled().value_or(scrollable_)) {
430 SetScrollEnable(false);
431 }
432 }
433
CreateLayoutAlgorithm()434 RefPtr<LayoutAlgorithm> ListPattern::CreateLayoutAlgorithm()
435 {
436 auto host = GetHost();
437 CHECK_NULL_RETURN(host, nullptr);
438 auto listLayoutProperty = host->GetLayoutProperty<ListLayoutProperty>();
439 RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm;
440 if (listLayoutProperty->HasLanes() || listLayoutProperty->HasLaneMinLength() ||
441 listLayoutProperty->HasLaneMaxLength()) {
442 auto lanesLayoutAlgorithm = MakeRefPtr<ListLanesLayoutAlgorithm>();
443 RefreshLanesItemRange();
444 lanesLayoutAlgorithm->SwapLanesItemRange(lanesItemRange_);
445 lanesLayoutAlgorithm->SetLanes(lanes_);
446 listLayoutAlgorithm.Swap(lanesLayoutAlgorithm);
447 } else {
448 listLayoutAlgorithm.Swap(MakeRefPtr<ListLayoutAlgorithm>());
449 }
450 if (jumpIndex_) {
451 listLayoutAlgorithm->SetIndex(jumpIndex_.value());
452 listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
453 jumpIndex_.reset();
454 }
455 if (targetIndex_) {
456 listLayoutAlgorithm->SetTargetIndex(targetIndex_.value());
457 listLayoutAlgorithm->SetIndexAlignment(scrollAlign_);
458 }
459 if (jumpIndexInGroup_) {
460 listLayoutAlgorithm->SetIndexInGroup(jumpIndexInGroup_.value());
461 jumpIndexInGroup_.reset();
462 }
463 if (predictSnapOffset_.has_value()) {
464 listLayoutAlgorithm->SetPredictSnapOffset(predictSnapOffset_.value());
465 listLayoutAlgorithm->SetTotalOffset(GetTotalOffset());
466 }
467 listLayoutAlgorithm->SetCurrentDelta(currentDelta_);
468 listLayoutAlgorithm->SetItemsPosition(itemPosition_);
469 listLayoutAlgorithm->SetPrevContentMainSize(contentMainSize_);
470 listLayoutAlgorithm->SetContentStartOffset(contentStartOffset_);
471 listLayoutAlgorithm->SetContentEndOffset(contentEndOffset_);
472 if (IsOutOfBoundary(false) && GetScrollSource() != SCROLL_FROM_AXIS) {
473 listLayoutAlgorithm->SetOverScrollFeature();
474 }
475 listLayoutAlgorithm->SetIsSpringEffect(IsScrollableSpringEffect());
476 listLayoutAlgorithm->SetCanOverScroll(CanOverScroll(GetScrollSource()));
477 if (chainAnimation_) {
478 SetChainAnimationLayoutAlgorithm(listLayoutAlgorithm, listLayoutProperty);
479 }
480 return listLayoutAlgorithm;
481 }
482
SetChainAnimationLayoutAlgorithm(RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm,RefPtr<ListLayoutProperty> listLayoutProperty)483 void ListPattern::SetChainAnimationLayoutAlgorithm(
484 RefPtr<ListLayoutAlgorithm> listLayoutAlgorithm, RefPtr<ListLayoutProperty> listLayoutProperty)
485 {
486 CHECK_NULL_VOID(listLayoutAlgorithm);
487 CHECK_NULL_VOID(listLayoutProperty);
488 listLayoutAlgorithm->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) {
489 auto list = weak.Upgrade();
490 CHECK_NULL_RETURN(list, 0.0f);
491 return list->GetChainDelta(index);
492 });
493 if (!listLayoutProperty->GetSpace().has_value() && chainAnimation_) {
494 listLayoutAlgorithm->SetChainInterval(CHAIN_INTERVAL_DEFAULT.ConvertToPx());
495 }
496 }
497
IsScrollSnapAlignCenter() const498 bool ListPattern::IsScrollSnapAlignCenter() const
499 {
500 auto host = GetHost();
501 CHECK_NULL_RETURN(host, false);
502 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
503 CHECK_NULL_RETURN(listProperty, false);
504 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
505 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) {
506 return true;
507 }
508
509 return false;
510 }
511
UpdateScrollSnap()512 void ListPattern::UpdateScrollSnap()
513 {
514 if (!AnimateStoped()) {
515 return;
516 }
517 predictSnapOffset_ = 0.0f;
518 }
519
NeedScrollSnapAlignEffect() const520 bool ListPattern::NeedScrollSnapAlignEffect() const
521 {
522 auto host = GetHost();
523 CHECK_NULL_RETURN(host, false);
524 auto listProperty = host->GetLayoutProperty<ListLayoutProperty>();
525 CHECK_NULL_RETURN(listProperty, false);
526 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
527 if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
528 return false;
529 }
530
531 return true;
532 }
533
IsAtTop() const534 bool ListPattern::IsAtTop() const
535 {
536 if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
537 float startItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
538 return (startIndex_ == 0) && GreatOrEqual(startMainPos_ - currentDelta_ + GetChainDelta(0),
539 contentMainSize_ / 2.0f - startItemHeight / 2.0f);
540 }
541
542 return (startIndex_ == 0) && NonNegative(startMainPos_ - currentDelta_ + GetChainDelta(0));
543 }
544
IsAtBottom() const545 bool ListPattern::IsAtBottom() const
546 {
547 if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
548 float endItemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos;
549 return (endIndex_ == maxListItemIndex_) && LessOrEqual(endMainPos_ - currentDelta_ + GetChainDelta(endIndex_),
550 contentMainSize_ / 2.0f + endItemHeight / 2.0f);
551 }
552
553 return endIndex_ == maxListItemIndex_ &&
554 LessOrEqual(endMainPos_ - currentDelta_ + GetChainDelta(endIndex_), contentMainSize_);
555 }
556
OutBoundaryCallback()557 bool ListPattern::OutBoundaryCallback()
558 {
559 bool outBoundary = IsAtTop() || IsAtBottom();
560 if (!dragFromSpring_ && outBoundary && chainAnimation_) {
561 chainAnimation_->SetOverDrag(false);
562 auto delta = chainAnimation_->SetControlIndex(IsAtTop() ? 0 : maxListItemIndex_);
563 currentDelta_ -= delta;
564 dragFromSpring_ = true;
565 }
566 return outBoundary;
567 }
568
GetListItemGroupEdge(bool & groupAtStart,bool & groupAtEnd) const569 void ListPattern::GetListItemGroupEdge(bool& groupAtStart, bool& groupAtEnd) const
570 {
571 if (itemPosition_.empty()) {
572 return;
573 }
574 bool firstIsGroup = startIndex_ == 0 && itemPosition_.begin()->second.isGroup;
575 bool lastIsGroup = endIndex_ == maxListItemIndex_ && itemPosition_.rbegin()->second.isGroup;
576 if (!firstIsGroup && !lastIsGroup) {
577 return;
578 }
579 auto host = GetHost();
580 CHECK_NULL_VOID(host);
581 std::list<RefPtr<FrameNode>> childrens;
582 host->GenerateOneDepthVisibleFrame(childrens);
583 if (childrens.empty()) {
584 return;
585 }
586 if (firstIsGroup) {
587 auto itemGroup = (*childrens.begin())->GetPattern<ListItemGroupPattern>();
588 if (itemGroup) {
589 groupAtStart = itemGroup->GetDiasplayStartIndexInGroup() == 0;
590 }
591 }
592 if (lastIsGroup) {
593 auto itemGroup = (*childrens.rbegin())->GetPattern<ListItemGroupPattern>();
594 if (itemGroup) {
595 groupAtEnd = itemGroup->GetDisplayEndIndexInGroup() == itemGroup->GetEndIndexInGroup();
596 }
597 }
598 }
599
GetOverScrollOffset(double delta) const600 OverScrollOffset ListPattern::GetOverScrollOffset(double delta) const
601 {
602 OverScrollOffset offset = { 0, 0 };
603 bool groupAtStart = true;
604 bool groupAtEnd = true;
605 GetListItemGroupEdge(groupAtStart, groupAtEnd);
606 if (startIndex_ == 0 && groupAtStart) {
607 auto startPos = startMainPos_ + GetChainDelta(0);
608 auto newStartPos = startPos + delta;
609 if (startPos > 0 && newStartPos > 0) {
610 offset.start = delta;
611 }
612 if (startPos > 0 && newStartPos <= 0) {
613 offset.start = -startPos;
614 }
615 if (startPos <= 0 && newStartPos > 0) {
616 offset.start = newStartPos;
617 }
618 if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
619 float startItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
620 if (newStartPos > (contentMainSize_ / 2.0f - startItemHeight / 2.0f - spaceWidth_ / 2.0f)) {
621 offset.start = newStartPos - (contentMainSize_ / 2.0f - startItemHeight / 2.0f - spaceWidth_ / 2.0f);
622 } else {
623 offset.start = 0.0;
624 }
625 }
626 }
627 if (endIndex_ == maxListItemIndex_ && groupAtEnd) {
628 auto endPos = endMainPos_ + GetChainDelta(endIndex_);
629 auto newEndPos = endPos + delta;
630 if (endPos < contentMainSize_ && newEndPos < contentMainSize_) {
631 offset.end = delta;
632 }
633 if (endPos < contentMainSize_ && newEndPos >= contentMainSize_) {
634 offset.end = contentMainSize_ - endPos;
635 }
636 if (endPos >= contentMainSize_ && newEndPos < contentMainSize_) {
637 offset.end = newEndPos - contentMainSize_;
638 }
639 if (IsScrollSnapAlignCenter() && !itemPosition_.empty()) {
640 float endItemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
641 if (newEndPos < (contentMainSize_ / 2.0f + endItemHeight / 2.0f + spaceWidth_ / 2.0f)) {
642 offset.end = newEndPos - (contentMainSize_ / 2.0f + endItemHeight / 2.0f + spaceWidth_ / 2.0f);
643 } else {
644 offset.end = 0.0;
645 }
646 }
647 }
648 return offset;
649 }
650
UpdateCurrentOffset(float offset,int32_t source)651 bool ListPattern::UpdateCurrentOffset(float offset, int32_t source)
652 {
653 if (itemPosition_.empty()) {
654 return false;
655 }
656 // check edgeEffect is not springEffect
657 if (!jumpIndex_.has_value() && !targetIndex_.has_value() && !HandleEdgeEffect(offset, source, GetContentSize())) {
658 if (IsOutOfBoundary(false)) {
659 MarkDirtyNodeSelf();
660 }
661 return false;
662 }
663 SetScrollSource(source);
664 currentDelta_ = currentDelta_ - offset;
665 MarkDirtyNodeSelf();
666 if (!IsOutOfBoundary() || !scrollable_) {
667 return true;
668 }
669
670 // over scroll in drag update from normal to over scroll.
671 float overScroll = 0.0f;
672 // over scroll in drag update during over scroll.
673 auto startPos = startMainPos_ - currentDelta_;
674 if ((itemPosition_.begin()->first == 0) && Positive(startPos)) {
675 overScroll = startPos;
676 } else {
677 overScroll = contentMainSize_ - (endMainPos_ - currentDelta_);
678 }
679 if (IsScrollSnapAlignCenter()) {
680 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
681 auto endPos = endMainPos_ - currentDelta_;
682 if (startIndex_ == 0 && Positive(startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f)) {
683 overScroll = startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f;
684 } else if ((endIndex_ == maxListItemIndex_) &&
685 LessNotEqual(endPos - itemHeight / 2.0f, contentMainSize_ / 2.0f)) {
686 overScroll = endPos - itemHeight / 2.0f - contentMainSize_ / 2.0f;
687 }
688 }
689 HandleScrollBarOutBoundary(overScroll);
690
691 if (GetScrollSource() == SCROLL_FROM_UPDATE) {
692 // adjust offset.
693 auto friction = CalculateFriction(std::abs(overScroll) / contentMainSize_);
694 currentDelta_ = currentDelta_ * friction;
695 }
696 return true;
697 }
698
MarkDirtyNodeSelf()699 void ListPattern::MarkDirtyNodeSelf()
700 {
701 auto host = GetHost();
702 CHECK_NULL_VOID(host);
703 if (!crossMatchChild_) {
704 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
705 } else {
706 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT);
707 }
708 }
709
OnScrollEndCallback()710 void ListPattern::OnScrollEndCallback()
711 {
712 scrollStop_ = true;
713 MarkDirtyNodeSelf();
714 }
715
OnScrollStartCallback()716 void ListPattern::OnScrollStartCallback()
717 {
718 FireOnScrollStart();
719 }
720
GetContentSize() const721 SizeF ListPattern::GetContentSize() const
722 {
723 auto host = GetHost();
724 CHECK_NULL_RETURN(host, SizeF());
725 auto geometryNode = host->GetGeometryNode();
726 CHECK_NULL_RETURN(geometryNode, SizeF());
727 return geometryNode->GetPaddingSize();
728 }
729
IsOutOfBoundary(bool useCurrentDelta)730 bool ListPattern::IsOutOfBoundary(bool useCurrentDelta)
731 {
732 if (itemPosition_.empty()) {
733 return false;
734 }
735 auto startPos = useCurrentDelta ? startMainPos_ - currentDelta_ : startMainPos_;
736 auto endPos = useCurrentDelta ? endMainPos_ - currentDelta_ : endMainPos_;
737 if (startIndex_ == 0) {
738 startPos += GetChainDelta(0);
739 }
740 if (endIndex_ == maxListItemIndex_) {
741 endPos += GetChainDelta(endIndex_);
742 }
743 bool outOfStart = (startIndex_ == 0) && Positive(startPos) && GreatNotEqual(endPos, contentMainSize_);
744 bool outOfEnd = (endIndex_ == maxListItemIndex_) && LessNotEqual(endPos, contentMainSize_) && Negative(startPos);
745 if (IsScrollSnapAlignCenter()) {
746 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos;
747 outOfStart = (startIndex_ == 0) && Positive(startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f);
748 outOfEnd = (endIndex_ == maxListItemIndex_) &&
749 LessNotEqual(endPos - itemHeight / 2.0f, contentMainSize_ / 2.0f);
750 }
751 return outOfStart || outOfEnd;
752 }
753
FireOnScrollStart()754 void ListPattern::FireOnScrollStart()
755 {
756 PerfMonitor::GetPerfMonitor()->Start(PerfConstants::APP_LIST_FLING, PerfActionType::FIRST_MOVE, "");
757 if (GetScrollAbort()) {
758 return;
759 }
760 auto scrollBar = GetScrollBar();
761 if (scrollBar) {
762 scrollBar->PlayScrollBarStartAnimation();
763 }
764 StopScrollBarAnimatorByProxy();
765 auto host = GetHost();
766 CHECK_NULL_VOID(host);
767 auto hub = host->GetEventHub<ListEventHub>();
768 CHECK_NULL_VOID_NOLOG(hub);
769 auto onScrollStart = hub->GetOnScrollStart();
770 CHECK_NULL_VOID_NOLOG(onScrollStart);
771 onScrollStart();
772 }
773
OnScrollCallback(float offset,int32_t source)774 bool ListPattern::OnScrollCallback(float offset, int32_t source)
775 {
776 if (source == SCROLL_FROM_START) {
777 ProcessDragStart(offset);
778 auto item = swiperItem_.Upgrade();
779 if (item) {
780 item->SwiperReset();
781 }
782 FireOnScrollStart();
783 return true;
784 }
785 ProcessDragUpdate(offset, source);
786 return UpdateCurrentOffset(offset, source);
787 }
788
InitScrollableEvent()789 void ListPattern::InitScrollableEvent()
790 {
791 AddScrollEvent();
792 auto host = GetHost();
793 CHECK_NULL_VOID(host);
794 auto listEventHub = host->GetEventHub<ListEventHub>();
795 auto onScrollFrameBegin = listEventHub->GetOnScrollFrameBegin();
796 SetScrollFrameBeginCallback(onScrollFrameBegin);
797 auto scrollableEvent = GetScrollableEvent();
798 CHECK_NULL_VOID(scrollableEvent);
799 scrollableTouchEvent_ = scrollableEvent->GetScrollable();
800 CHECK_NULL_VOID(scrollableTouchEvent_);
801 scrollableTouchEvent_->SetOnContinuousSliding([weak = AceType::WeakClaim(this)]() -> double {
802 auto list = weak.Upgrade();
803 return list->contentMainSize_;
804 });
805 }
806
OnScrollSnapCallback(double targetOffset,double velocity)807 bool ListPattern::OnScrollSnapCallback(double targetOffset, double velocity)
808 {
809 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
810 CHECK_NULL_RETURN(listProperty, false);
811 auto scrollSnapAlign = listProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE);
812 if (scrollSnapAlign == V2::ScrollSnapAlign::NONE) {
813 return false;
814 }
815 predictSnapOffset_ = targetOffset;
816 scrollSnapVelocity_ = velocity;
817 MarkDirtyNodeSelf();
818 return true;
819 }
820
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)821 void ListPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
822 {
823 scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
824 auto list = weak.Upgrade();
825 CHECK_NULL_RETURN_NOLOG(list, 0.0);
826 return list->startMainPos_ + list->GetChainDelta(list->startIndex_) - list->currentDelta_;
827 });
828 scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
829 auto list = weak.Upgrade();
830 auto endPos = list->endMainPos_ + list->GetChainDelta(list->endIndex_);
831 auto startPos = list->startMainPos_ + list->GetChainDelta(list->startIndex_);
832 if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
833 float endItemHeight =
834 list->itemPosition_.rbegin()->second.endPos - list->itemPosition_.rbegin()->second.startPos;
835 return list->contentMainSize_ / 2.0f + endItemHeight / 2.0f - (endPos - startPos);
836 }
837 float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
838 return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
839 });
840 scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
841 auto list = weak.Upgrade();
842 CHECK_NULL_RETURN_NOLOG(list, 0.0);
843 if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
844 float startItemHeight =
845 list->itemPosition_.begin()->second.endPos - list->itemPosition_.begin()->second.startPos;
846 return list->contentMainSize_ / 2.0f - startItemHeight / 2.0f;
847 }
848
849 return list->contentStartOffset_;
850 });
851 scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
852 auto list = weak.Upgrade();
853 auto endPos = list->endMainPos_ + list->GetChainDelta(list->endIndex_);
854 auto startPos = list->startMainPos_ + list->GetChainDelta(list->startIndex_);
855 if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
856 float endItemHeight =
857 list->itemPosition_.rbegin()->second.endPos - list->itemPosition_.rbegin()->second.startPos;
858 return list->contentMainSize_ / 2.0f + endItemHeight / 2.0f - (endPos - startPos);
859 }
860 float leading = list->contentMainSize_ - (endPos - startPos) - list->contentEndOffset_;
861 return (list->startIndex_ == 0) ? std::min(leading, list->contentStartOffset_) : leading;
862 });
863 scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double {
864 auto list = weak.Upgrade();
865 CHECK_NULL_RETURN_NOLOG(list, 0.0);
866 if (list->IsScrollSnapAlignCenter() && !list->itemPosition_.empty()) {
867 float startItemHeight =
868 list->itemPosition_.begin()->second.endPos - list->itemPosition_.begin()->second.startPos;
869 return list->contentMainSize_ / 2.0f - startItemHeight / 2.0f;
870 }
871
872 return list->contentStartOffset_;
873 });
874 }
875
CheckRestartSpring()876 void ListPattern::CheckRestartSpring()
877 {
878 if (!ScrollableIdle() || !IsOutOfBoundary()) {
879 return;
880 }
881 auto edgeEffect = GetScrollEdgeEffect();
882 if (!edgeEffect || !edgeEffect->IsSpringEffect()) {
883 return;
884 }
885 if (AnimateRunning()) {
886 return;
887 }
888 FireOnScrollStart();
889 edgeEffect->ProcessScrollOver(0);
890 }
891
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)892 void ListPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
893 {
894 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
895 auto pattern = wp.Upgrade();
896 CHECK_NULL_RETURN_NOLOG(pattern, false);
897 return pattern->OnKeyEvent(event);
898 };
899 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
900 }
901
OnKeyEvent(const KeyEvent & event)902 bool ListPattern::OnKeyEvent(const KeyEvent& event)
903 {
904 if (event.action != KeyAction::DOWN) {
905 return false;
906 }
907 if (event.code == KeyCode::KEY_PAGE_DOWN) {
908 LOGD("Keycode is PgDn. Scroll to next page");
909 ScrollPage(false);
910 return true;
911 }
912 if (event.code == KeyCode::KEY_PAGE_UP) {
913 LOGD("Keycode is PgDn. Scroll to next page");
914 ScrollPage(true);
915 return true;
916 }
917 return HandleDirectionKey(event);
918 }
919
HandleDirectionKey(const KeyEvent & event)920 bool ListPattern::HandleDirectionKey(const KeyEvent& event)
921 {
922 return false;
923 }
924
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)925 WeakPtr<FocusHub> ListPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
926 {
927 auto curFocus = currentFocusNode.Upgrade();
928 CHECK_NULL_RETURN(curFocus, nullptr);
929 auto curFrame = curFocus->GetFrameNode();
930 CHECK_NULL_RETURN(curFrame, nullptr);
931 auto curPattern = curFrame->GetPattern();
932 CHECK_NULL_RETURN(curPattern, nullptr);
933 auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
934 CHECK_NULL_RETURN(curItemPattern, nullptr);
935 auto listProperty = GetLayoutProperty<ListLayoutProperty>();
936 CHECK_NULL_RETURN(listProperty, nullptr);
937
938 auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
939 auto curIndex = curItemPattern->GetIndexInList();
940 auto curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
941 auto curListItemGroupPara = GetListItemGroupParameter(curFrame);
942 if (curIndex < 0 || curIndex > maxListItemIndex_) {
943 LOGE("can't find focused child.");
944 return nullptr;
945 }
946
947 auto moveStep = 0;
948 auto nextIndex = curIndex;
949 auto nextIndexInGroup = curIndexInGroup;
950 if (lanes_ <= 1) {
951 if ((isVertical && step == FocusStep::UP_END) || (!isVertical && step == FocusStep::LEFT_END)) {
952 moveStep = 1;
953 nextIndex = 0;
954 nextIndexInGroup = -1;
955 } else if ((isVertical && step == FocusStep::DOWN_END) || (!isVertical && step == FocusStep::RIGHT_END)) {
956 moveStep = -1;
957 nextIndex = maxListItemIndex_;
958 nextIndexInGroup = -1;
959 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT) ||
960 (step == FocusStep::TAB)) {
961 moveStep = 1;
962 if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
963 nextIndex = curIndex + moveStep;
964 nextIndexInGroup = -1;
965 } else {
966 nextIndexInGroup = curIndexInGroup + moveStep;
967 }
968 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT) ||
969 (step == FocusStep::SHIFT_TAB)) {
970 moveStep = -1;
971 if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
972 nextIndex = curIndex + moveStep;
973 nextIndexInGroup = -1;
974 } else {
975 nextIndexInGroup = curIndexInGroup + moveStep;
976 }
977 }
978 } else {
979 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END)) {
980 moveStep = 1;
981 nextIndex = 0;
982 nextIndexInGroup = -1;
983 } else if ((step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
984 moveStep = -1;
985 nextIndex = maxListItemIndex_;
986 nextIndexInGroup = -1;
987 } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
988 if (curIndexInGroup == -1) {
989 moveStep = lanes_;
990 nextIndex = curIndex + moveStep;
991 nextIndexInGroup = -1;
992 } else {
993 moveStep = curListItemGroupPara.lanes;
994 nextIndexInGroup = curIndexInGroup + moveStep;
995 }
996 } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
997 if (curIndexInGroup == -1) {
998 moveStep = -lanes_;
999 nextIndex = curIndex + moveStep;
1000 nextIndexInGroup = -1;
1001 } else {
1002 moveStep = -curListItemGroupPara.lanes;
1003 nextIndexInGroup = curIndexInGroup + moveStep;
1004 }
1005 } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1006 moveStep = 1;
1007 if (((curIndexInGroup == -1) && ((curIndex - (lanes_ - 1)) % lanes_ != 0)) || ((curIndexInGroup != -1) &&
1008 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes == 0))) {
1009 nextIndex = curIndex + moveStep;
1010 nextIndexInGroup = -1;
1011 } else if ((curIndexInGroup != -1) &&
1012 ((curIndexInGroup - (curListItemGroupPara.lanes - 1)) % curListItemGroupPara.lanes != 0)) {
1013 nextIndexInGroup = curIndexInGroup + moveStep;
1014 }
1015 } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1016 moveStep = -1;
1017 if (((curIndexInGroup == -1) && (curIndex % lanes_ != 0)) || ((curIndexInGroup != -1) &&
1018 (curIndexInGroup % curListItemGroupPara.lanes == 0))) {
1019 nextIndex = curIndex + moveStep;
1020 nextIndexInGroup = -1;
1021 } else if ((curIndexInGroup != -1) && (curIndexInGroup % curListItemGroupPara.lanes != 0)) {
1022 nextIndexInGroup = curIndexInGroup + moveStep;
1023 }
1024 } else if (step == FocusStep::TAB) {
1025 moveStep = 1;
1026 if ((curIndexInGroup == -1) || (curIndexInGroup >= curListItemGroupPara.itemEndIndex)) {
1027 nextIndex = curIndex + moveStep;
1028 nextIndexInGroup = -1;
1029 } else {
1030 nextIndexInGroup = curIndexInGroup + moveStep;
1031 }
1032 } else if (step == FocusStep::SHIFT_TAB) {
1033 moveStep = -1;
1034 if ((curIndexInGroup == -1) || (curIndexInGroup <= 0)) {
1035 nextIndex = curIndex + moveStep;
1036 nextIndexInGroup = -1;
1037 } else {
1038 nextIndexInGroup = curIndexInGroup + moveStep;
1039 }
1040 }
1041 }
1042 while (nextIndex >= 0 && nextIndex <= maxListItemIndex_) {
1043 if ((nextIndex == curIndex) && (curIndexInGroup == nextIndexInGroup)) {
1044 return nullptr;
1045 }
1046 auto nextFocusNode = ScrollAndFindFocusNode(nextIndex, curIndex, nextIndexInGroup, curIndexInGroup, moveStep,
1047 step);
1048 if (nextFocusNode.Upgrade()) {
1049 return nextFocusNode;
1050 }
1051 if (nextIndexInGroup > -1) {
1052 nextIndexInGroup += moveStep;
1053 } else {
1054 nextIndex += moveStep;
1055 }
1056 }
1057 return nullptr;
1058 }
1059
GetChildFocusNodeByIndex(int32_t tarMainIndex,int32_t tarGroupIndex)1060 WeakPtr<FocusHub> ListPattern::GetChildFocusNodeByIndex(int32_t tarMainIndex, int32_t tarGroupIndex)
1061 {
1062 LOGD("Get target item location is (%{public}d,%{public}d)", tarMainIndex, tarGroupIndex);
1063 auto listFrame = GetHost();
1064 CHECK_NULL_RETURN(listFrame, nullptr);
1065 auto listFocus = listFrame->GetFocusHub();
1066 CHECK_NULL_RETURN(listFocus, nullptr);
1067 auto childFocusList = listFocus->GetChildren();
1068 for (const auto& childFocus : childFocusList) {
1069 if (!childFocus->IsFocusable()) {
1070 continue;
1071 }
1072 auto childFrame = childFocus->GetFrameNode();
1073 if (!childFrame) {
1074 continue;
1075 }
1076 auto childPattern = childFrame->GetPattern();
1077 if (!childPattern) {
1078 continue;
1079 }
1080 auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
1081 if (!childItemPattern) {
1082 continue;
1083 }
1084 auto curIndex = childItemPattern->GetIndexInList();
1085 auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
1086 if (curIndex == tarMainIndex && curIndexInGroup == tarGroupIndex) {
1087 return AceType::WeakClaim(AceType::RawPtr(childFocus));
1088 }
1089 }
1090 LOGD("The target item at location(%{public}d,%{public}d) can not found.", tarMainIndex, tarGroupIndex);
1091 return nullptr;
1092 }
1093
ScrollToNode(const RefPtr<FrameNode> & focusFrameNode)1094 bool ListPattern::ScrollToNode(const RefPtr<FrameNode>& focusFrameNode)
1095 {
1096 CHECK_NULL_RETURN_NOLOG(focusFrameNode, false);
1097 auto focusPattern = focusFrameNode->GetPattern<ListItemPattern>();
1098 CHECK_NULL_RETURN_NOLOG(focusPattern, false);
1099 auto curIndex = focusPattern->GetIndexInList();
1100 ScrollToIndex(curIndex, smooth_, ScrollAlign::AUTO);
1101 auto pipeline = PipelineContext::GetCurrentContext();
1102 if (pipeline) {
1103 pipeline->FlushUITasks();
1104 }
1105 return true;
1106 }
1107
ScrollAndFindFocusNode(int32_t nextIndex,int32_t curIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step)1108 WeakPtr<FocusHub> ListPattern::ScrollAndFindFocusNode(int32_t nextIndex, int32_t curIndex, int32_t& nextIndexInGroup,
1109 int32_t curIndexInGroup, int32_t moveStep, FocusStep step)
1110 {
1111 auto isScrollIndex = ScrollListForFocus(nextIndex, curIndex, nextIndexInGroup);
1112 auto groupIndexInGroup = ScrollListItemGroupForFocus(nextIndex, nextIndexInGroup,
1113 curIndexInGroup, moveStep, step, isScrollIndex);
1114
1115 return groupIndexInGroup ? GetChildFocusNodeByIndex(nextIndex, nextIndexInGroup) : nullptr;
1116 }
1117
ScrollListForFocus(int32_t nextIndex,int32_t curIndex,int32_t nextIndexInGroup)1118 bool ListPattern::ScrollListForFocus(int32_t nextIndex, int32_t curIndex, int32_t nextIndexInGroup)
1119 {
1120 auto isScrollIndex = false;
1121 auto pipeline = PipelineContext::GetCurrentContext();
1122 CHECK_NULL_RETURN(pipeline, isScrollIndex);
1123 if (nextIndex < startIndex_) {
1124 if (nextIndexInGroup == -1) {
1125 isScrollIndex = true;
1126 ScrollToIndex(nextIndex, smooth_, ScrollAlign::START);
1127 pipeline->FlushUITasks();
1128 } else {
1129 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1130 pipeline->FlushUITasks();
1131 }
1132 } else if (nextIndex > endIndex_) {
1133 if (nextIndexInGroup == -1) {
1134 isScrollIndex = true;
1135 ScrollToIndex(nextIndex, smooth_, ScrollAlign::END);
1136 pipeline->FlushUITasks();
1137 } else {
1138 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1139 pipeline->FlushUITasks();
1140 }
1141 }
1142 return isScrollIndex;
1143 }
1144
ScrollListItemGroupForFocus(int32_t nextIndex,int32_t & nextIndexInGroup,int32_t curIndexInGroup,int32_t moveStep,FocusStep step,bool isScrollIndex)1145 bool ListPattern::ScrollListItemGroupForFocus(int32_t nextIndex, int32_t& nextIndexInGroup, int32_t curIndexInGroup,
1146 int32_t moveStep, FocusStep step, bool isScrollIndex)
1147 {
1148 auto groupIndexInGroup = true;
1149 auto pipeline = PipelineContext::GetCurrentContext();
1150 CHECK_NULL_RETURN(pipeline, groupIndexInGroup);
1151 RefPtr<FrameNode> nextIndexNode;
1152 auto isNextInGroup = IsListItemGroup(nextIndex, nextIndexNode);
1153 CHECK_NULL_RETURN(nextIndexNode, groupIndexInGroup);
1154 if (!isNextInGroup) {
1155 nextIndexInGroup = -1;
1156 return groupIndexInGroup;
1157 }
1158 auto nextListItemGroupPara = GetListItemGroupParameter(nextIndexNode);
1159 if (nextIndexInGroup == -1) {
1160 auto scrollAlign = ScrollAlign::END;
1161 nextIndexInGroup = moveStep < 0 ? nextListItemGroupPara.itemEndIndex : 0;
1162 if ((step == FocusStep::UP_END) || (step == FocusStep::LEFT_END) ||
1163 (step == FocusStep::DOWN_END) || (step == FocusStep::RIGHT_END)) {
1164 scrollAlign = moveStep < 0 ? ScrollAlign::END : ScrollAlign::START;
1165 } else {
1166 scrollAlign = moveStep < 0 ? ScrollAlign::START : ScrollAlign::END;
1167 }
1168 if ((nextIndexInGroup < nextListItemGroupPara.displayStartIndex) ||
1169 (nextIndexInGroup > nextListItemGroupPara.displayEndIndex) || (isScrollIndex)) {
1170 ScrollToIndex(nextIndex, nextIndexInGroup, scrollAlign);
1171 pipeline->FlushUITasks();
1172 }
1173 } else if (nextIndexInGroup > nextListItemGroupPara.itemEndIndex) {
1174 nextIndexInGroup = -1;
1175 groupIndexInGroup = false;
1176 } else {
1177 if ((nextIndexInGroup < curIndexInGroup) && (nextIndexInGroup < nextListItemGroupPara.displayStartIndex)) {
1178 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::START);
1179 pipeline->FlushUITasks();
1180 } else if ((nextIndexInGroup > curIndexInGroup) &&
1181 (nextIndexInGroup > nextListItemGroupPara.displayEndIndex)) {
1182 ScrollToIndex(nextIndex, nextIndexInGroup, ScrollAlign::END);
1183 pipeline->FlushUITasks();
1184 }
1185 }
1186 return groupIndexInGroup;
1187 }
1188
OnAnimateStop()1189 void ListPattern::OnAnimateStop()
1190 {
1191 scrollStop_ = true;
1192 MarkDirtyNodeSelf();
1193 isScrollEnd_ = true;
1194 }
1195
ScrollTo(float position)1196 void ListPattern::ScrollTo(float position)
1197 {
1198 LOGI("ScrollTo:%{public}f", position);
1199 StopAnimate();
1200 jumpIndex_.reset();
1201 targetIndex_.reset();
1202 currentDelta_ = 0.0f;
1203 UpdateCurrentOffset(GetTotalOffset() - position, SCROLL_FROM_JUMP);
1204 MarkDirtyNodeSelf();
1205 isScrollEnd_ = true;
1206 }
1207
ScrollToIndex(int32_t index,bool smooth,ScrollAlign align)1208 void ListPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign align)
1209 {
1210 LOGI("ScrollToIndex:%{public}d, align:%{public}d.", index, align);
1211 SetScrollSource(SCROLL_FROM_JUMP);
1212 StopAnimate();
1213 auto host = GetHost();
1214 CHECK_NULL_VOID(host);
1215 auto totalItemCount = host->TotalChildCount();
1216 if ((index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM)) {
1217 currentDelta_ = 0.0f;
1218 smooth_ = smooth;
1219 if (smooth_) {
1220 targetIndex_ = index;
1221 if (index == ListLayoutAlgorithm::LAST_ITEM) {
1222 targetIndex_ = totalItemCount - 1;
1223 } else if ((LessNotEqual(targetIndex_.value(), 0)) ||
1224 (GreatOrEqual(targetIndex_.value(), totalItemCount))) {
1225 targetIndex_.reset();
1226 }
1227 } else {
1228 jumpIndex_ = index;
1229 }
1230 scrollAlign_ = align;
1231 MarkDirtyNodeSelf();
1232 }
1233 isScrollEnd_ = true;
1234 }
1235
ScrollToIndex(int32_t index,int32_t indexInGroup,ScrollAlign align)1236 void ListPattern::ScrollToIndex(int32_t index, int32_t indexInGroup, ScrollAlign align)
1237 {
1238 LOGI("ScrollToIndex:%{public}d, %{public}d, align:%{public}d.", index, indexInGroup, align);
1239 SetScrollSource(SCROLL_FROM_JUMP);
1240 StopAnimate();
1241 if (index >= 0 || index == ListLayoutAlgorithm::LAST_ITEM) {
1242 currentDelta_ = 0;
1243 jumpIndex_ = index;
1244 jumpIndexInGroup_ = indexInGroup;
1245 scrollAlign_ = align;
1246 MarkDirtyNodeSelf();
1247 }
1248 isScrollEnd_ = true;
1249 }
1250
ScrollToEdge(ScrollEdgeType scrollEdgeType)1251 void ListPattern::ScrollToEdge(ScrollEdgeType scrollEdgeType)
1252 {
1253 LOGI("ScrollToEdge:%{public}zu", scrollEdgeType);
1254 if (scrollEdgeType == ScrollEdgeType::SCROLL_TOP) {
1255 ScrollToIndex(0, smooth_, ScrollAlign::START);
1256 } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1257 ScrollToIndex(ListLayoutAlgorithm::LAST_ITEM, smooth_, ScrollAlign::END);
1258 }
1259 }
1260
ScrollPage(bool reverse)1261 bool ListPattern::ScrollPage(bool reverse)
1262 {
1263 LOGI("ScrollPage:%{public}d", reverse);
1264 StopAnimate();
1265 float distance = reverse ? contentMainSize_ : -contentMainSize_;
1266 UpdateCurrentOffset(distance, SCROLL_FROM_JUMP);
1267 isScrollEnd_ = true;
1268 return true;
1269 }
1270
ScrollBy(float offset)1271 void ListPattern::ScrollBy(float offset)
1272 {
1273 StopAnimate();
1274 UpdateCurrentOffset(-offset, SCROLL_FROM_JUMP);
1275 isScrollEnd_ = true;
1276 }
1277
GetCurrentOffset() const1278 Offset ListPattern::GetCurrentOffset() const
1279 {
1280 if (GetAxis() == Axis::HORIZONTAL) {
1281 return { GetTotalOffset(), 0.0 };
1282 }
1283 return { 0.0, GetTotalOffset() };
1284 }
1285
UpdateScrollBarOffset()1286 void ListPattern::UpdateScrollBarOffset()
1287 {
1288 if (itemPosition_.empty()) {
1289 return;
1290 }
1291 if (!GetScrollBar() && !GetScrollBarProxy()) {
1292 return;
1293 }
1294 float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1295 float currentOffset = itemsSize / itemPosition_.size() * itemPosition_.begin()->first - startMainPos_;
1296 Offset scrollOffset = { currentOffset, currentOffset }; // fit for w/h switched.
1297 auto estimatedHeight = itemsSize / itemPosition_.size() * (maxListItemIndex_ + 1);
1298
1299 // calculate padding offset of list
1300 auto host = GetHost();
1301 CHECK_NULL_VOID_NOLOG(host);
1302 auto layoutPriority = host->GetLayoutProperty();
1303 CHECK_NULL_VOID_NOLOG(layoutPriority);
1304 auto paddingOffset = layoutPriority->CreatePaddingAndBorder().Offset();
1305 Offset viewOffset = { paddingOffset.GetX(), paddingOffset.GetY() };
1306 const auto& geometryNode = host->GetGeometryNode();
1307 auto frameSize = geometryNode->GetFrameSize();
1308 Size size(frameSize.Width(), frameSize.Height());
1309 UpdateScrollBarRegion(currentOffset, estimatedHeight, size, Offset(0.0f, 0.0f));
1310 }
1311
GetTotalHeight() const1312 float ListPattern::GetTotalHeight() const
1313 {
1314 if (itemPosition_.empty()) {
1315 return 0.0f;
1316 }
1317
1318 float itemsSize = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_;
1319 return itemsSize / itemPosition_.size() * (maxListItemIndex_ + 1);
1320 }
1321
SetChainAnimation()1322 void ListPattern::SetChainAnimation()
1323 {
1324 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1325 CHECK_NULL_VOID(listLayoutProperty);
1326 auto edgeEffect = listLayoutProperty->GetEdgeEffect().value_or(EdgeEffect::SPRING);
1327 int32_t lanes = std::max(listLayoutProperty->GetLanes().value_or(1), 1);
1328 bool autoLanes = listLayoutProperty->HasLaneMinLength() || listLayoutProperty->HasLaneMaxLength();
1329 bool animation = listLayoutProperty->GetChainAnimation().value_or(false);
1330 bool enable = edgeEffect == EdgeEffect::SPRING && lanes == 1 && !autoLanes && animation;
1331 if (!enable) {
1332 chainAnimation_.Reset();
1333 return;
1334 }
1335 if (!chainAnimation_) {
1336 auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1337 springProperty_ =
1338 AceType::MakeRefPtr<SpringProperty>(CHAIN_SPRING_MASS, CHAIN_SPRING_STIFFNESS, CHAIN_SPRING_DAMPING);
1339 if (chainAnimationOptions_.has_value()) {
1340 float maxSpace = chainAnimationOptions_.value().maxSpace.ConvertToPx();
1341 float minSpace = chainAnimationOptions_.value().minSpace.ConvertToPx();
1342 if (GreatNotEqual(minSpace, maxSpace)) {
1343 minSpace = space;
1344 maxSpace = space;
1345 }
1346 springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1347 springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1348 chainAnimation_ =
1349 AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1350 auto conductivity = chainAnimationOptions_.value().conductivity;
1351 if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1352 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1353 }
1354 chainAnimation_->SetConductivity(conductivity);
1355 auto intensity = chainAnimationOptions_.value().intensity;
1356 if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1357 intensity = ChainAnimation::DEFAULT_INTENSITY;
1358 }
1359 chainAnimation_->SetIntensity(intensity);
1360 auto effect = chainAnimationOptions_.value().edgeEffect;
1361 chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1362 } else {
1363 auto minSpace = space * DEFAULT_MIN_SPACE_SCALE;
1364 auto maxSpace = space * DEFAULT_MAX_SPACE_SCALE;
1365 chainAnimation_ = AceType::MakeRefPtr<ChainAnimation>(space, maxSpace, minSpace, springProperty_);
1366 }
1367 chainAnimation_->SetAnimationCallback([weak = AceType::WeakClaim(this)]() {
1368 auto list = weak.Upgrade();
1369 CHECK_NULL_VOID(list);
1370 list->MarkDirtyNodeSelf();
1371 });
1372 }
1373 }
1374
SetChainAnimationOptions(const ChainAnimationOptions & options)1375 void ListPattern::SetChainAnimationOptions(const ChainAnimationOptions& options)
1376 {
1377 chainAnimationOptions_ = options;
1378 if (chainAnimation_) {
1379 auto listLayoutProperty = GetLayoutProperty<ListLayoutProperty>();
1380 CHECK_NULL_VOID(listLayoutProperty);
1381 auto space = listLayoutProperty->GetSpace().value_or(CHAIN_INTERVAL_DEFAULT).ConvertToPx();
1382 float maxSpace = options.maxSpace.ConvertToPx();
1383 float minSpace = options.minSpace.ConvertToPx();
1384 if (GreatNotEqual(minSpace, maxSpace)) {
1385 minSpace = space;
1386 maxSpace = space;
1387 }
1388 chainAnimation_->SetSpace(space, maxSpace, minSpace);
1389 auto conductivity = chainAnimationOptions_.value().conductivity;
1390 if (LessNotEqual(conductivity, 0) || GreatNotEqual(conductivity, 1)) {
1391 conductivity = ChainAnimation::DEFAULT_CONDUCTIVITY;
1392 }
1393 chainAnimation_->SetConductivity(conductivity);
1394 auto intensity = chainAnimationOptions_.value().intensity;
1395 if (LessNotEqual(intensity, 0) || GreatNotEqual(intensity, 1)) {
1396 intensity = ChainAnimation::DEFAULT_INTENSITY;
1397 }
1398 chainAnimation_->SetIntensity(intensity);
1399 auto effect = options.edgeEffect;
1400 chainAnimation_->SetEdgeEffect(effect == 1 ? ChainEdgeEffect::STRETCH : ChainEdgeEffect::DEFAULT);
1401 }
1402 if (springProperty_) {
1403 springProperty_->SetStiffness(chainAnimationOptions_.value().stiffness);
1404 springProperty_->SetDamping(chainAnimationOptions_.value().damping);
1405 }
1406 }
1407
ProcessDragStart(float startPosition)1408 void ListPattern::ProcessDragStart(float startPosition)
1409 {
1410 CHECK_NULL_VOID_NOLOG(chainAnimation_);
1411 auto host = GetHost();
1412 CHECK_NULL_VOID(host);
1413 auto globalOffset = host->GetTransformRelativeOffset();
1414 int32_t index = -1;
1415 auto offset = startPosition - GetMainAxisOffset(globalOffset, GetAxis());
1416 auto it = std::find_if(itemPosition_.begin(), itemPosition_.end(), [offset](auto pos) {
1417 return offset <= pos.second.endPos;
1418 });
1419 if (it != itemPosition_.end()) {
1420 index = it->first;
1421 } else if (!itemPosition_.empty()) {
1422 index = itemPosition_.rbegin()->first + 1;
1423 }
1424 dragFromSpring_ = false;
1425 chainAnimation_->SetControlIndex(index);
1426 chainAnimation_->SetMaxIndex(maxListItemIndex_);
1427 }
1428
ProcessDragUpdate(float dragOffset,int32_t source)1429 void ListPattern::ProcessDragUpdate(float dragOffset, int32_t source)
1430 {
1431 CHECK_NULL_VOID_NOLOG(chainAnimation_);
1432 if (NearZero(dragOffset)) {
1433 return;
1434 }
1435 if (NeedScrollSnapAlignEffect()) {
1436 auto delta = 0.0f;
1437 if (chainAnimation_->GetControlIndex() < startIndex_ - 1) {
1438 delta = chainAnimation_->SetControlIndex(std::max(startIndex_ - 1, 0));
1439 }
1440 if (chainAnimation_->GetControlIndex() > endIndex_ + 1) {
1441 delta = chainAnimation_->SetControlIndex(std::min(endIndex_ + 1, maxListItemIndex_));
1442 }
1443 if (!NearZero(delta)) {
1444 auto scrollableEvent = GetScrollableEvent();
1445 CHECK_NULL_VOID(scrollableEvent);
1446 auto scrollable = scrollableEvent->GetScrollable();
1447 CHECK_NULL_VOID(scrollable);
1448 scrollable->UpdateScrollSnapStartOffset(delta);
1449 currentDelta_ -= delta;
1450 }
1451 }
1452 bool overDrag = (source == SCROLL_FROM_UPDATE) && (IsAtTop() || IsAtBottom());
1453 chainAnimation_->SetDelta(-dragOffset, overDrag);
1454 }
1455
GetChainDelta(int32_t index) const1456 float ListPattern::GetChainDelta(int32_t index) const
1457 {
1458 CHECK_NULL_RETURN_NOLOG(chainAnimation_, 0.0f);
1459 return chainAnimation_->GetValue(index);
1460 }
1461
MultiSelectWithoutKeyboard(const RectF & selectedZone)1462 void ListPattern::MultiSelectWithoutKeyboard(const RectF& selectedZone)
1463 {
1464 auto host = GetHost();
1465 CHECK_NULL_VOID(host);
1466 std::list<RefPtr<FrameNode>> childrens;
1467 host->GenerateOneDepthVisibleFrame(childrens);
1468 for (const auto& item : childrens) {
1469 if (item->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
1470 auto itemGroupPattern = item->GetPattern<ListItemGroupPattern>();
1471 CHECK_NULL_VOID(itemGroupPattern);
1472 auto itemGroupGeometry = item->GetGeometryNode();
1473 CHECK_NULL_VOID(itemGroupGeometry);
1474 auto itemGroupRect = itemGroupGeometry->GetFrameRect();
1475 if (!selectedZone.IsIntersectWith(itemGroupRect)) {
1476 continue;
1477 }
1478 HandleCardModeSelectedEvent(selectedZone, item, itemGroupRect.Top());
1479 continue;
1480 }
1481 auto itemPattern = item->GetPattern<ListItemPattern>();
1482 CHECK_NULL_VOID(itemPattern);
1483 if (!itemPattern->Selectable()) {
1484 continue;
1485 }
1486
1487 auto itemGeometry = item->GetGeometryNode();
1488 CHECK_NULL_VOID(itemGeometry);
1489
1490 auto itemRect = itemGeometry->GetFrameRect();
1491 if (!selectedZone.IsIntersectWith(itemRect)) {
1492 itemPattern->MarkIsSelected(false);
1493 } else {
1494 itemPattern->MarkIsSelected(true);
1495 }
1496 }
1497
1498 DrawSelectedZone(selectedZone);
1499 }
1500
HandleCardModeSelectedEvent(const RectF & selectedZone,const RefPtr<FrameNode> & itemGroupNode,float itemGroupTop)1501 void ListPattern::HandleCardModeSelectedEvent(
1502 const RectF& selectedZone, const RefPtr<FrameNode>& itemGroupNode, float itemGroupTop)
1503 {
1504 CHECK_NULL_VOID(itemGroupNode);
1505 std::list<RefPtr<FrameNode>> childrens;
1506 itemGroupNode->GenerateOneDepthVisibleFrame(childrens);
1507 for (const auto& item : childrens) {
1508 auto itemPattern = item->GetPattern<ListItemPattern>();
1509 if (!itemPattern) {
1510 continue;
1511 }
1512 if (!itemPattern->Selectable()) {
1513 continue;
1514 }
1515 auto itemGeometry = item->GetGeometryNode();
1516 CHECK_NULL_VOID(itemGeometry);
1517 auto context = item->GetRenderContext();
1518 CHECK_NULL_VOID(context);
1519 auto itemRect = itemGeometry->GetFrameRect();
1520 RectF itemRectInGroup(itemRect.GetX(), itemRect.GetY() + itemGroupTop, itemRect.Width(), itemRect.Height());
1521 if (!selectedZone.IsIntersectWith(itemRectInGroup)) {
1522 itemPattern->MarkIsSelected(false);
1523 } else {
1524 itemPattern->MarkIsSelected(true);
1525 }
1526 }
1527 }
1528
ClearMultiSelect()1529 void ListPattern::ClearMultiSelect()
1530 {
1531 auto host = GetHost();
1532 CHECK_NULL_VOID(host);
1533 std::list<RefPtr<FrameNode>> children;
1534 host->GenerateOneDepthAllFrame(children);
1535 for (const auto& child : children) {
1536 if (!child) {
1537 continue;
1538 }
1539 auto itemPattern = child->GetPattern<ListItemPattern>();
1540 if (itemPattern) {
1541 itemPattern->MarkIsSelected(false);
1542 continue;
1543 }
1544 auto itemGroupPattern = child->GetPattern<ListItemGroupPattern>();
1545 if (itemGroupPattern) {
1546 std::list<RefPtr<FrameNode>> itemChildren;
1547 child->GenerateOneDepthAllFrame(itemChildren);
1548 for (const auto& item : itemChildren) {
1549 if (!item) {
1550 continue;
1551 }
1552 itemPattern = item->GetPattern<ListItemPattern>();
1553 if (itemPattern) {
1554 itemPattern->MarkIsSelected(false);
1555 }
1556 }
1557 }
1558 }
1559
1560 ClearSelectedZone();
1561 }
1562
IsItemSelected(const MouseInfo & info)1563 bool ListPattern::IsItemSelected(const MouseInfo& info)
1564 {
1565 auto host = GetHost();
1566 CHECK_NULL_RETURN(host, false);
1567 auto node = host->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
1568 CHECK_NULL_RETURN_NOLOG(node, false);
1569 auto itemPattern = node->GetPattern<ListItemPattern>();
1570 if (itemPattern) {
1571 return itemPattern->IsSelected();
1572 }
1573 auto itemGroupPattern = node->GetPattern<ListItemGroupPattern>();
1574 if (itemGroupPattern) {
1575 auto itemNode = node->FindChildByPosition(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
1576 CHECK_NULL_RETURN_NOLOG(itemNode, false);
1577 itemPattern = itemNode->GetPattern<ListItemPattern>();
1578 CHECK_NULL_RETURN_NOLOG(itemPattern, false);
1579 return itemPattern->IsSelected();
1580 }
1581 return false;
1582 }
1583
SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)1584 void ListPattern::SetSwiperItem(WeakPtr<ListItemPattern> swiperItem)
1585 {
1586 if (swiperItem_ != swiperItem) {
1587 auto item = swiperItem_.Upgrade();
1588 if (item) {
1589 item->SwiperReset();
1590 }
1591 swiperItem_ = std::move(swiperItem);
1592 }
1593 }
1594
GetItemIndexByPosition(float xOffset,float yOffset)1595 int32_t ListPattern::GetItemIndexByPosition(float xOffset, float yOffset)
1596 {
1597 auto host = GetHost();
1598 auto globalOffset = host->GetTransformRelativeOffset();
1599 float relativeX = xOffset - globalOffset.GetX();
1600 float relativeY = yOffset - globalOffset.GetY();
1601 float mainOffset = GetAxis() == Axis::VERTICAL ? relativeY : relativeX;
1602 float crossOffset = GetAxis() == Axis::VERTICAL ? relativeX : relativeY;
1603 float crossSize = GetCrossAxisSize(GetContentSize(), GetAxis());
1604 int32_t lanesOffset = 0;
1605 if (lanes_ > 1) {
1606 lanesOffset = static_cast<int32_t>(crossOffset / (crossSize / lanes_));
1607 }
1608 for (auto & pos : itemPosition_) {
1609 if (mainOffset <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */
1610 return std::min(pos.first + lanesOffset, maxListItemIndex_ + 1);
1611 }
1612 }
1613 if (!itemPosition_.empty()) {
1614 return itemPosition_.rbegin()->first + 1;
1615 }
1616 return 0;
1617 }
1618
ToJsonValue(std::unique_ptr<JsonValue> & json) const1619 void ListPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
1620 {
1621 json->Put("multiSelectable", multiSelectable_);
1622 json->Put("startIndex", startIndex_);
1623 if (!itemPosition_.empty()) {
1624 json->Put("itemStartPos", itemPosition_.begin()->second.startPos);
1625 }
1626 json->Put("friction", GetFriction());
1627 }
1628
FromJson(const std::unique_ptr<JsonValue> & json)1629 void ListPattern::FromJson(const std::unique_ptr<JsonValue>& json)
1630 {
1631 ScrollToIndex(json->GetInt("startIndex"));
1632 if (json->Contains("itemStartPos")) {
1633 ScrollBy(-json->GetDouble("itemStartPos"));
1634 }
1635 auto host = GetHost();
1636 CHECK_NULL_VOID(host);
1637 host->GetRenderContext()->UpdateClipEdge(true);
1638 ScrollablePattern::FromJson(json);
1639 }
1640
SetAccessibilityAction()1641 void ListPattern::SetAccessibilityAction()
1642 {
1643 auto host = GetHost();
1644 CHECK_NULL_VOID(host);
1645 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1646 CHECK_NULL_VOID(accessibilityProperty);
1647 accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1648 const auto& pattern = weakPtr.Upgrade();
1649 CHECK_NULL_VOID(pattern);
1650 if (!pattern->IsScrollable()) {
1651 return;
1652 }
1653 pattern->ScrollPage(false);
1654 });
1655
1656 accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1657 const auto& pattern = weakPtr.Upgrade();
1658 CHECK_NULL_VOID(pattern);
1659 if (!pattern->IsScrollable()) {
1660 return;
1661 }
1662 pattern->ScrollPage(true);
1663 });
1664 }
1665
GetListItemGroupParameter(const RefPtr<FrameNode> & node)1666 ListItemGroupPara ListPattern::GetListItemGroupParameter(const RefPtr<FrameNode>& node)
1667 {
1668 ListItemGroupPara listItemGroupPara = {-1, -1, -1, -1};
1669 auto curFrameParent = node->GetParent();
1670 auto curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
1671 while (curFrameParent && (!curFrameParentNode)) {
1672 curFrameParent = curFrameParent->GetParent();
1673 curFrameParentNode = AceType::DynamicCast<FrameNode>(curFrameParent);
1674 }
1675 CHECK_NULL_RETURN(curFrameParentNode, listItemGroupPara);
1676 if (curFrameParent->GetTag() == V2::LIST_ITEM_GROUP_ETS_TAG) {
1677 auto itemGroupPattern = curFrameParentNode->GetPattern<ListItemGroupPattern>();
1678 CHECK_NULL_RETURN(itemGroupPattern, listItemGroupPara);
1679 listItemGroupPara.displayEndIndex = itemGroupPattern->GetDisplayEndIndexInGroup();
1680 listItemGroupPara.displayStartIndex = itemGroupPattern->GetDiasplayStartIndexInGroup();
1681 listItemGroupPara.lanes = itemGroupPattern->GetLanesInGroup();
1682 listItemGroupPara.itemEndIndex = itemGroupPattern->GetEndIndexInGroup();
1683 LOGD("TListPattern::GetListItemGroupParameter(%{public}d,%{public}d,%{public}d,%{public}d).",
1684 listItemGroupPara.displayEndIndex, listItemGroupPara.displayStartIndex,
1685 listItemGroupPara.lanes, listItemGroupPara.itemEndIndex);
1686 }
1687 return listItemGroupPara;
1688 }
1689
IsListItemGroup(int32_t listIndex,RefPtr<FrameNode> & node)1690 bool ListPattern::IsListItemGroup(int32_t listIndex, RefPtr<FrameNode>& node)
1691 {
1692 auto listFrame = GetHost();
1693 CHECK_NULL_RETURN(listFrame, false);
1694 auto listFocus = listFrame->GetFocusHub();
1695 CHECK_NULL_RETURN(listFocus, false);
1696 for (const auto& childFocus : listFocus->GetChildren()) {
1697 if (!childFocus->IsFocusable()) {
1698 continue;
1699 }
1700 if (auto childFrame = childFocus->GetFrameNode()) {
1701 if (auto childPattern = AceType::DynamicCast<ListItemPattern>(childFrame->GetPattern())) {
1702 auto curIndex = childPattern->GetIndexInList();
1703 auto curIndexInGroup = childPattern->GetIndexInListItemGroup();
1704 if (curIndex == listIndex) {
1705 node = childFrame;
1706 return curIndexInGroup > -1;
1707 }
1708 }
1709 }
1710 }
1711 return false;
1712 }
1713
RefreshLanesItemRange()1714 void ListPattern::RefreshLanesItemRange()
1715 {
1716 auto host = GetHost();
1717 CHECK_NULL_VOID(host);
1718 auto updatePos = host->GetChildrenUpdated();
1719 if (updatePos == -1) {
1720 return;
1721 }
1722 if (updatePos == 0) {
1723 lanesItemRange_.clear();
1724 return;
1725 }
1726 for (auto it = lanesItemRange_.begin(); it != lanesItemRange_.end();) {
1727 if (it->second < updatePos) {
1728 it++;
1729 } else if (it->first >= updatePos) {
1730 lanesItemRange_.erase(it++);
1731 } else {
1732 it->second = updatePos - 1;
1733 it++;
1734 }
1735 }
1736 }
1737 } // namespace OHOS::Ace::NG
1738