• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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_v2/list/render_list.h"
17 
18 #include "base/geometry/axis.h"
19 #include "base/log/ace_trace.h"
20 #include "base/log/log.h"
21 #include "base/memory/ace_type.h"
22 #include "base/utils/string_utils.h"
23 #include "base/utils/utils.h"
24 #include "core/animation/bilateral_spring_node.h"
25 #include "core/common/text_field_manager.h"
26 #include "core/components/box/drag_drop_event.h"
27 #include "core/components/scroll/render_scroll.h"
28 #include "core/components/scroll/render_single_child_scroll.h"
29 #include "core/components/scroll/scroll_spring_effect.h"
30 #include "core/components/scroll/scrollable.h"
31 #include "core/components/stack/stack_element.h"
32 #include "core/components_v2/list/list_component.h"
33 #include "core/components_v2/list/list_scroll_bar_controller.h"
34 #include "core/components_v2/list/render_list_item_group.h"
35 #include "core/event/axis_event.h"
36 #include "core/gestures/long_press_recognizer.h"
37 #include "core/gestures/pan_recognizer.h"
38 #include "core/gestures/sequenced_recognizer.h"
39 
40 namespace OHOS::Ace::V2 {
41 namespace {
42 
43 constexpr double VIEW_PORT_SCALE = 1.2;
44 constexpr int32_t CHAIN_ANIMATION_NODE_COUNT = 30;
45 constexpr int32_t DEFAULT_SOURCE = 3;
46 constexpr float SCROLL_MAX_TIME = 300.0f; // Scroll Animate max time 0.3 second
47 constexpr int32_t SCROLL_FROM_JUMP = 3;
48 constexpr int32_t DEFAULT_FINGERS = 1;
49 constexpr int32_t DEFAULT_DURATION = 200;
50 constexpr int32_t DEFAULT_DISTANCE = 0;
51 
52 constexpr bool DIR_HORIZONTAL = false;
53 constexpr bool DIR_VERTICAL = true;
54 constexpr bool DIR_FORWARD = false;
55 constexpr bool DIR_REVERSE = true;
56 constexpr int32_t STEP_FORWARD = 1;
57 constexpr int32_t STEP_BACK = -1;
58 constexpr int32_t STEP_INVALID = 10;
59 constexpr int32_t CENTER_ALIGN_DIVIDER = 2;
60 
61 // IsRightToLeft | IsListVertical | IsDirectionVertical | IsDirectionReverse
62 const std::map<bool, std::map<bool, std::map<bool, std::map<bool, int32_t>>>> DIRECTION_MAP = {
63     { false, // RTL is false
64         { { false, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_FORWARD }, { DIR_REVERSE, STEP_BACK } } },
65                        { DIR_VERTICAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } } } },
66             { true, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } },
67                         { DIR_VERTICAL, { { DIR_FORWARD, STEP_FORWARD }, { DIR_REVERSE, STEP_BACK } } } } } } },
68     { true, // RTL is true
69         { { false, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_BACK }, { DIR_REVERSE, STEP_FORWARD } } },
70                        { DIR_VERTICAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } } } },
71             { true, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } },
72                         { DIR_VERTICAL, { { DIR_FORWARD, STEP_BACK }, { DIR_REVERSE, STEP_FORWARD } } } } } } }
73 };
74 } // namespace
75 
~RenderList()76 RenderList::~RenderList()
77 {
78     if (scrollBarProxy_) {
79         scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
80     }
81 }
82 
Update(const RefPtr<Component> & component)83 void RenderList::Update(const RefPtr<Component>& component)
84 {
85     component_ = AceType::DynamicCast<ListComponent>(component);
86     ACE_DCHECK(component_);
87 
88     isRightToLeft_ = component_->GetTextDirection() == TextDirection::RTL ? true : false;
89     RemoveAllItems();
90 
91     auto axis = component_->GetDirection();
92     vertical_ = axis == Axis::VERTICAL;
93 
94     InitScrollBar();
95 
96     // Start index should be updated only for the first time
97     if (startIndex_ == INITIAL_CHILD_INDEX) {
98         initialIndex_ = static_cast<size_t>(component_->GetInitialIndex());
99         startIndex_ = initialIndex_ > 0 ? initialIndex_ : 0;
100         useEstimateCurrentOffset_ = true;
101     }
102     // maybe change startIndex
103     ApplyRestoreInfo();
104 
105     const auto& divider = component_->GetItemDivider();
106     listSpace_ = component_->GetSpace();
107     cachedCount_ = static_cast<size_t>(component_->GetCachedCount());
108 
109     LOGI("cached count: %{public}zu", cachedCount_);
110     spaceWidth_ = std::max(NormalizePercentToPx(component_->GetSpace(), vertical_),
111         divider ? NormalizePercentToPx(divider->strokeWidth, vertical_) : 0.0);
112     InitScrollable(axis);
113     // now only support spring
114     if (component_->GetEdgeEffect() == EdgeEffect::SPRING) {
115         if (!scrollEffect_ || scrollEffect_->GetEdgeEffect() != EdgeEffect::SPRING) {
116             scrollEffect_ = AceType::MakeRefPtr<ScrollSpringEffect>();
117             ResetEdgeEffect();
118         }
119     } else if (component_->GetEdgeEffect() == EdgeEffect::FADE) {
120         if (!scrollEffect_ || scrollEffect_->GetEdgeEffect() != EdgeEffect::FADE) {
121             scrollEffect_ = AceType::MakeRefPtr<ScrollFadeEffect>();
122             ResetEdgeEffect();
123         }
124     } else {
125         scrollEffect_ = nullptr;
126     }
127 
128     auto controller = component_->GetScrollController();
129     if (controller) {
130         controller->SetScrollNode(AceType::WeakClaim(this));
131     }
132     if (!animator_) {
133         animator_ = CREATE_ANIMATOR(GetContext());
134     }
135 
136     // chainAnimation
137     if (chainAnimation_ != component_->GetChainAnimation()) {
138         chainAnimation_ = component_->GetChainAnimation();
139         if (chainAnimation_) {
140             InitChainAnimation(CHAIN_ANIMATION_NODE_COUNT);
141             overSpringProperty_ = SpringChainProperty::GetDefaultOverSpringProperty();
142         } else {
143             overSpringProperty_ = nullptr;
144             chain_ = nullptr;
145             chainAdapter_ = nullptr;
146         }
147     }
148 
149     if (chainAnimation_) {
150         // add chain interval length
151         spaceWidth_ += NormalizeToPx(chainProperty_.Interval());
152     }
153 
154     scrollBarProxy_ = component_->GetScrollBarProxy();
155     InitScrollBarProxy();
156 
157     onItemDragStart_ = component_->GetOnItemDragStartId();
158     onItemDragEnter_ = component_->GetOnItemDragEnterId();
159     onItemDragMove_ = component_->GetOnItemDragMoveId();
160     onItemDragLeave_ = component_->GetOnItemDragLeaveId();
161     onItemDrop_ = component_->GetOnItemDropId();
162 
163     if (onItemDragStart_) {
164         CreateDragDropRecognizer();
165     }
166     FindRefreshParent(AceType::WeakClaim(this));
167 
168     isMultiSelectable_ = component_->GetMultiSelectable();
169     hasHeight_ = component_->GetHasHeight();
170     hasWidth_ = component_->GetHasWidth();
171     isLaneList_ = (component_->GetLanes() != -1) || (component_->GetLaneConstrain() != std::nullopt);
172     sticky_ = component_->GetSticky();
173     if (!vertical_ || scrollBar_->GetDisplayMode() != DisplayMode::OFF || chainAnimation_ ||
174         sticky_ != StickyStyle::NONE || isLaneList_ || !scrollable_->Available()) {
175         drivenRender_ = false;
176     } else {
177         drivenRender_ = true;
178     }
179     MarkNeedLayout();
180 }
181 
InitScrollable(Axis axis)182 void RenderList::InitScrollable(Axis axis)
183 {
184     if (scrollable_) {
185         scrollable_->SetAxis(axis);
186         scrollable_->SetOnScrollBegin(component_->GetOnScrollBegin());
187         return;
188     }
189 
190     auto callback = [weak = AceType::WeakClaim(this)](double offset, int32_t source) {
191         auto renderList = weak.Upgrade();
192 
193         if (!renderList) {
194             return false;
195         }
196 
197         if (source == SCROLL_FROM_START) {
198             renderList->ProcessDragStart(offset);
199             return true;
200         }
201 
202         Offset delta;
203         if (renderList->vertical_) {
204             delta.SetX(0.0);
205             delta.SetY(offset);
206         } else {
207             delta.SetX(offset);
208             delta.SetY(0.0);
209         }
210         renderList->AdjustOffset(delta, source);
211         if ((source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION_SPRING) &&
212             renderList->currentOffset_ >= 0.0) {
213             if (renderList->scrollable_->RelatedScrollEventDoing(Offset(0.0, -offset))) {
214                 return false;
215             }
216         }
217         renderList->ProcessDragUpdate(renderList->GetMainAxis(delta));
218 
219         // Stop animator of scroll bar.
220         auto scrollBarProxy = renderList->scrollBarProxy_;
221         if (scrollBarProxy) {
222             scrollBarProxy->StopScrollBarAnimator();
223         }
224         return renderList->UpdateScrollPosition(renderList->GetMainAxis(delta), source);
225     };
226     scrollable_ = AceType::MakeRefPtr<Scrollable>(callback, axis);
227     scrollable_->SetNotifyScrollOverCallBack([weak = AceType::WeakClaim(this)](double velocity) {
228         auto list = weak.Upgrade();
229         if (!list) {
230             return;
231         }
232         list->ProcessScrollOverCallback(velocity);
233     });
234     scrollable_->SetScrollEndCallback([weak = AceType::WeakClaim(this)]() {
235         auto list = weak.Upgrade();
236         if (!list) {
237             LOGW("render list Upgrade fail in scroll end callback");
238             return;
239         }
240         auto proxy = list->scrollBarProxy_;
241         if (proxy) {
242             proxy->StartScrollBarAnimator();
243         }
244         auto scrollBar = list->scrollBar_;
245         if (scrollBar) {
246             scrollBar->HandleScrollBarEnd();
247         }
248         list->listEventFlags_[ListEvents::SCROLL_STOP] = true;
249         list->HandleListEvent();
250     });
251     InitializeScrollable(scrollable_);
252     scrollable_->SetOnScrollBegin(component_->GetOnScrollBegin());
253     if (vertical_) {
254         scrollable_->InitRelatedParent(GetParent());
255     }
256     scrollable_->Initialize(context_);
257     scrollable_->SetNodeId(GetAccessibilityNodeId());
258 }
259 
InitScrollBarProxy()260 void RenderList::InitScrollBarProxy()
261 {
262     if (!scrollBarProxy_) {
263         return;
264     }
265     auto callback = [weak = AceType::WeakClaim(this)](double value, int32_t source) {
266         auto renderList = weak.Upgrade();
267         if (!renderList) {
268             LOGE("render list is released");
269             return false;
270         }
271         return renderList->UpdateScrollPosition(value, source);
272     };
273     scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
274     scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), callback });
275 }
276 
IsReachStart()277 bool RenderList::IsReachStart()
278 {
279     bool scrollUpToReachStart = GreatNotEqual(prevOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
280     bool scrollDownToReachStart = LessNotEqual(prevOffset_, 0.0) && GreatOrEqual(currentOffset_, 0.0);
281     return scrollUpToReachStart || scrollDownToReachStart;
282 }
283 
InitScrollBar()284 void RenderList::InitScrollBar()
285 {
286     if (!component_) {
287         LOGE("InitScrollBar failed, component_ is null.");
288         return;
289     }
290     if (scrollBar_) {
291         scrollBar_->SetDisplayMode(component_->GetScrollBar());
292         scrollBar_->Reset();
293         return;
294     }
295     const RefPtr<ScrollBarTheme> theme = GetTheme<ScrollBarTheme>();
296     if (!theme) {
297         return;
298     }
299 
300     scrollBar_ = AceType::MakeRefPtr<ScrollBar>(component_->GetScrollBar(), theme->GetShapeMode());
301     RefPtr<ListScrollBarController> controller = AceType::MakeRefPtr<ListScrollBarController>();
302     scrollBar_->SetScrollBarController(controller);
303 
304     // set the scroll bar style
305     scrollBar_->SetReservedHeight(theme->GetReservedHeight());
306     scrollBar_->SetMinHeight(theme->GetMinHeight());
307     scrollBar_->SetMinDynamicHeight(theme->GetMinDynamicHeight());
308     scrollBar_->SetForegroundColor(theme->GetForegroundColor());
309     scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
310     scrollBar_->SetPadding(theme->GetPadding());
311     scrollBar_->SetScrollable(true);
312     scrollBar_->SetInactiveWidth(theme->GetNormalWidth());
313     scrollBar_->SetNormalWidth(theme->GetNormalWidth());
314     scrollBar_->SetActiveWidth(theme->GetActiveWidth());
315     scrollBar_->SetTouchWidth(theme->GetTouchWidth());
316     if (!vertical_) {
317         scrollBar_->SetPositionMode(PositionMode::BOTTOM);
318     } else {
319         if (isRightToLeft_) {
320             scrollBar_->SetPositionMode(PositionMode::LEFT);
321         }
322     }
323     scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
324     SetScrollBarCallback();
325 }
326 
SetScrollBarCallback()327 void RenderList::SetScrollBarCallback()
328 {
329     if (!scrollBar_ || !scrollBar_->NeedScrollBar()) {
330         return;
331     }
332     auto&& scrollCallback = [weakList = AceType::WeakClaim(this)](double value, int32_t source) {
333         auto list = weakList.Upgrade();
334         if (!list) {
335             LOGE("render list is released");
336             return false;
337         }
338         return list->UpdateScrollPosition(value, source);
339     };
340     auto&& barEndCallback = [weakList = AceType::WeakClaim(this)](int32_t value) {
341         auto list = weakList.Upgrade();
342         if (!list) {
343             LOGE("render list is released.");
344             return;
345         }
346         list->scrollBarOpacity_ = value;
347         list->MarkNeedRender();
348     };
349     auto&& scrollEndCallback = []() {
350         // nothing to do
351     };
352     scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
353 }
354 
GetLaneLengthInPx(const Dimension & length)355 double RenderList::GetLaneLengthInPx(const Dimension& length)
356 {
357     if (length.Unit() == DimensionUnit::PERCENT) {
358         return NormalizePercentToPx(length, !vertical_, true);
359     }
360     return NormalizeToPx(length);
361 }
362 
ModifyLaneLength(const std::optional<std::pair<Dimension,Dimension>> & laneConstrain)363 void RenderList::ModifyLaneLength(const std::optional<std::pair<Dimension, Dimension>>& laneConstrain)
364 {
365     minLaneLength_ = GetLaneLengthInPx(laneConstrain.value().first);
366     maxLaneLength_ = GetLaneLengthInPx(laneConstrain.value().second);
367     if (LessOrEqual(maxLaneLength_, 0.0)) {
368         maxLaneLength_ = GetCrossSize(GetLayoutSize());
369     }
370     if (LessOrEqual(minLaneLength_, 0.0)) {
371         minLaneLength_ = std::min(GetCrossSize(GetLayoutSize()), maxLaneLength_);
372     }
373     if (GreatNotEqual(minLaneLength_, maxLaneLength_)) {
374         LOGI("minLaneLength: %{public}f is greater than maxLaneLength: %{public}f, assign minLaneLength to"
375              " maxLaneLength",
376             minLaneLength_, maxLaneLength_);
377         maxLaneLength_ = minLaneLength_;
378     }
379 }
380 
CalculateLanes()381 void RenderList::CalculateLanes()
382 {
383     auto lanes = component_->GetLanes();
384     do {
385         auto laneConstrain = component_->GetLaneConstrain();
386         // Case 1: lane length constrain is not set
387         //      1.1: use [lanes_] set by user if [lanes_] is set
388         //      1.2: set [lanes_] to 1 if [lanes_] is not set
389         if (!laneConstrain) {
390             if (lanes <= 0) {
391                 lanes = 1;
392             }
393             maxLaneLength_ = GetCrossSize(GetLayoutParam().GetMaxSize()) / lanes;
394             minLaneLength_ = GetCrossSize(GetLayoutParam().GetMinSize()) / lanes;
395             break;
396         }
397         // Case 2: lane length constrain is set --> need to calculate [lanes_] according to contraint.
398         // We agreed on such rules (assuming we have a vertical list here):
399         // rule 1: [minLaneLength_] has a higher priority than [maxLaneLength_] when decide [lanes_], for e.g.,
400         //         if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 120,
401         //         the [lanes_] is 3 rather than 2.
402         // rule 2: after [lanes_] is determined by rule 1, the width of lane will be as large as it can be, for e.g.,
403         //         if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 132, the [lanes_] is 3
404         //         according to rule 1, then the width of lane will be 132 / 3 = 44 rather than 40,
405         //         its [minLaneLength_].
406 
407         // set layout size temporarily to calculate percent unit of constrain
408         SetLayoutSize(GetLayoutParam().GetMaxSize());
409         ModifyLaneLength(laneConstrain);
410 
411         // if minLaneLength is 40, maxLaneLength is 60
412         // when list's width is 120, lanes_ = 3
413         // when list's width is 80, lanes_ = 2
414         // when list's width is 70, lanes_ = 1
415         auto maxCrossSize = GetCrossSize(GetLayoutSize());
416         double maxLanes = maxCrossSize / minLaneLength_;
417         double minLanes = maxCrossSize / maxLaneLength_;
418         // let's considerate scenarios when maxCrossSize > 0
419         // now it's guaranteed that [minLaneLength_] <= [maxLaneLength_], i.e., maxLanes >= minLanes > 0
420         // there are 3 scenarios:
421         // 1. 1 > maxLanes >= minLanes > 0
422         // 2. maxLanes >= 1 >= minLanes > 0
423         // 3. maxLanes >= minLanes > 1
424 
425         // 1. 1 > maxLanes >= minLanes > 0 ---> maxCrossSize < minLaneLength_ =< maxLaneLength
426         if (GreatNotEqual(1, maxLanes) && GreatOrEqual(maxLanes, minLanes)) {
427             lanes = 1;
428             minLaneLength_ = maxCrossSize;
429             maxLaneLength_ = maxCrossSize;
430             break;
431         }
432         // 2. maxLanes >= 1 >= minLanes > 0 ---> minLaneLength_ = maxCrossSize < maxLaneLength
433         if (GreatOrEqual(maxLanes, 1) && LessOrEqual(minLanes, 1)) {
434             lanes = std::floor(maxLanes);
435             maxLaneLength_ = maxCrossSize;
436             break;
437         }
438         // 3. maxLanes >= minLanes > 1 ---> minLaneLength_ <= maxLaneLength < maxCrossSize
439         if (GreatOrEqual(maxLanes, minLanes) && GreatNotEqual(minLanes, 1)) {
440             lanes = std::floor(maxLanes);
441             break;
442         }
443         lanes = 1;
444         LOGE("unexpected situation, set lanes to 1, maxLanes: %{public}f, minLanes: %{public}f, minLaneLength_: "
445              "%{public}f, maxLaneLength_: %{public}f",
446             maxLanes, minLanes, minLaneLength_, maxLaneLength_);
447     } while (0);
448     if (lanes != lanes_) {  // if lanes changes, adjust startIndex_
449         lanes_ = lanes;
450         if (lanes > 1) {
451             size_t startIndex = startIndex_ - GetItemRelativeIndex(startIndex_) % lanes;
452             if (startIndex_ != startIndex) {
453                 RemoveAllItems();
454             }
455         }
456     }
457 }
458 
RequestNewItemsAtEndForLaneList(double & curMainPos,double mainSize)459 void RenderList::RequestNewItemsAtEndForLaneList(double& curMainPos, double mainSize)
460 {
461     int newItemCntInLine = 0;
462     double lineMainSize = 0;
463     for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
464         bool breakWhenRequestNewItem = false;
465         RefPtr<RenderListItemGroup> itemGroup;
466         do {
467             if (GreatOrEqual(curMainPos, endMainPos_)) {
468                 breakWhenRequestNewItem = true;
469                 break;
470             }
471             auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
472             if (!child) {
473                 startIndex_ = std::min(startIndex_, TotalCount());
474                 breakWhenRequestNewItem = true;
475                 break;
476             }
477             if (GreatOrEqual(curMainPos, mainSize)) {
478                 ++endCachedCount_;
479             }
480             itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
481             if (itemGroup) {
482                 break;
483             }
484             lineMainSize = std::max(lineMainSize, GetMainSize(child->GetLayoutSize()));
485             ++newItemCntInLine;
486         } while (0);
487         bool singleLaneDoneAddItem = (lanes_ == 1) && !breakWhenRequestNewItem;
488         bool multiLaneDoneSupplyOneLine = (lanes_ > 1) && (newItemCntInLine == lanes_);
489         bool multiLaneStartSupplyLine = (itemGroup || breakWhenRequestNewItem) && (newItemCntInLine >= 1);
490         if (singleLaneDoneAddItem || multiLaneDoneSupplyOneLine || multiLaneStartSupplyLine) {
491             curMainPos += lineMainSize + spaceWidth_;
492             newItemCntInLine = 0;
493             lineMainSize = 0;
494         }
495         if (itemGroup) {
496             double size = GetMainSize(itemGroup->GetLayoutSize());
497             curMainPos += size + spaceWidth_;
498         }
499         if (breakWhenRequestNewItem) {
500             break;
501         }
502     }
503 }
504 
RequestNewItemsAtEnd(double & curMainPos,double mainSize)505 void RenderList::RequestNewItemsAtEnd(double& curMainPos, double mainSize)
506 {
507     for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
508         if (cachedCount_ != 0) {
509             if (endCachedCount_ >= cachedCount_ && GreatOrEqual(curMainPos, mainSize)) {
510                 break;
511             }
512         } else {
513             if (GreatOrEqual(curMainPos, endMainPos_)) {
514                 break;
515             }
516         }
517         auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
518         if (!child) {
519             startIndex_ = std::min(startIndex_, TotalCount());
520             break;
521         }
522         if (GreatOrEqual(curMainPos, mainSize)) {
523             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
524             if (itemGroup) {
525                 endCachedCount_ += itemGroup->GetCurrEndCacheCount();
526             } else {
527                 ++endCachedCount_;
528             }
529         }
530         curMainPos += GetMainSize(child->GetLayoutSize()) + spaceWidth_;
531     }
532 
533     if (selectedItem_ && selectedItemIndex_ < startIndex_) {
534         curMainPos += GetMainSize(selectedItem_->GetLayoutSize()) + spaceWidth_;
535     }
536 }
537 
RequestNewItemsAtStartForLaneList()538 void RenderList::RequestNewItemsAtStartForLaneList()
539 {
540     int newItemCntInLine = 0;
541     double lineMainSize = 0;
542     for (; startIndex_ > 0; --startIndex_) {
543         bool breakWhenRequestNewItem = false;
544         RefPtr<RenderListItemGroup> itemGroup;
545         do {
546             if (LessOrEqual(currentOffset_, startMainPos_)) {
547                 breakWhenRequestNewItem = true;
548                 break;
549             }
550             auto child = RequestAndLayoutNewItem(startIndex_ - 1, currentOffset_ - spaceWidth_, false);
551             if (!child) {
552                 breakWhenRequestNewItem = true;
553                 break;
554             }
555             if (selectedItemIndex_ == startIndex_) {
556                 continue;
557             }
558             if (LessOrEqual(currentOffset_, 0.0)) {
559                 ++startCachedCount_;
560             }
561             itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
562             if (itemGroup) {
563                 break;
564             }
565             lineMainSize = std::max(lineMainSize, GetMainSize(child->GetLayoutSize()));
566             ++newItemCntInLine;
567         } while (0);
568         bool singleLaneDoneAddItem = (lanes_ == 1) && !breakWhenRequestNewItem;
569         bool isLaneStart = !itemGroup && (lanes_ > 1) && (GetItemRelativeIndex(startIndex_ - 1) % lanes_ == 0);
570         bool multiLaneSupplyLine = (itemGroup || breakWhenRequestNewItem || isLaneStart) && (newItemCntInLine >= 1);
571         if (singleLaneDoneAddItem || multiLaneSupplyLine) {
572             currentOffset_ -= lineMainSize + spaceWidth_;
573             startIndexOffset_ -= lineMainSize + spaceWidth_;
574             newItemCntInLine = 0;
575             lineMainSize = 0;
576         }
577         if (itemGroup) {
578             double size = GetMainSize(itemGroup->GetLayoutSize());
579             currentOffset_ -= size + spaceWidth_;
580             startIndexOffset_ -= size + spaceWidth_;
581         }
582         if (breakWhenRequestNewItem) {
583             break;
584         }
585     }
586 }
587 
RequestNewItemsAtStart()588 void RenderList::RequestNewItemsAtStart()
589 {
590     for (; startIndex_ > 0; --startIndex_) {
591         if (cachedCount_ != 0) {
592             if (startCachedCount_ >= cachedCount_ && LessOrEqual(currentOffset_, 0.0)) {
593                 break;
594             }
595         } else {
596             if (LessOrEqual(currentOffset_, startMainPos_)) {
597                 break;
598             }
599         }
600         auto child = RequestAndLayoutNewItem(startIndex_ - 1, currentOffset_ - spaceWidth_, false);
601         if (!child) {
602             break;
603         }
604         if (selectedItemIndex_ == startIndex_) {
605             continue;
606         }
607         if (LessOrEqual(currentOffset_, 0.0)) {
608             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
609             if (itemGroup) {
610                 startCachedCount_ += itemGroup->GetCurrStartCacheCount();
611             } else {
612                 ++startCachedCount_;
613             }
614         }
615         currentOffset_ -= GetMainSize(child->GetLayoutSize()) + spaceWidth_;
616         startIndexOffset_ -= GetMainSize(child->GetLayoutSize()) + spaceWidth_;
617     }
618 }
619 
PerformLayout()620 void RenderList::PerformLayout()
621 {
622     UpdateAccessibilityAttr();
623     // Check validation of layout size
624     const double mainSize = ApplyLayoutParam();
625     if (NearZero(mainSize)) {
626         LOGW("Cannot layout using invalid view port");
627         return;
628     }
629     if (isLaneList_) {
630         CalculateLanes();
631     }
632 
633     double prevTotalOffset = startIndexOffset_ - prevOffset_;
634     double curMainPos = 0.0;
635     if (isLaneList_) {
636         curMainPos = LayoutOrRecycleCurrentItemsForLaneList(mainSize);
637     } else {
638         curMainPos = LayoutOrRecycleCurrentItems(mainSize);
639     }
640 
641     // Try to request new items at end if needed
642     if (isLaneList_) {
643         RequestNewItemsAtEndForLaneList(curMainPos, mainSize);
644     } else {
645         RequestNewItemsAtEnd(curMainPos, mainSize);
646     }
647 
648     if (selectedItem_ && selectedItemIndex_ < startIndex_) {
649         curMainPos += GetMainSize(selectedItem_->GetLayoutSize()) + spaceWidth_;
650     }
651 
652     if (startIndex_ + items_.size() >= TotalCount()) {
653         curMainPos -= spaceWidth_;
654     }
655 
656     // Check if reach the end of list
657     reachEnd_ = LessOrEqual(curMainPos, mainSize);
658     bool noEdgeEffect = (scrollable_ && scrollable_->IsAnimationNotRunning()) ||
659         !(scrollEffect_ && scrollEffect_->IsSpringEffect()) || autoScrollingForItemMove_;
660     if (noEdgeEffect && reachEnd_) {
661         // Adjust end of list to match the end of layout
662         if (LessNotEqual(curMainPos, mainSize)) {
663             AdjustForReachEnd(mainSize, curMainPos);
664         }
665         curMainPos = mainSize;
666     }
667 
668     // Try to request new items at start if needed
669     if (isLaneList_) {
670         RequestNewItemsAtStartForLaneList();
671     } else {
672         RequestNewItemsAtStart();
673     }
674 
675     // Check if reach the start of list
676     reachStart_ = GreatOrEqual(currentOffset_, 0.0);
677     if (noEdgeEffect && reachStart_) {
678         if (GreatOrEqual(currentOffset_, 0.0)) {
679             AdjustForReachStart(curMainPos);
680         }
681         currentOffset_ = 0;
682         if (isLaneList_) {
683             RequestNewItemsAtEndForLaneList(curMainPos, mainSize);
684         } else {
685             RequestNewItemsAtEnd(curMainPos, mainSize);
686         }
687     }
688 
689     if (IsReachStart()) {
690         listEventFlags_[ListEvents::REACH_START] = true;
691     }
692     bool scrollDownToReachEnd = LessNotEqual(prevMainPos_, mainSize) && GreatOrEqual(curMainPos, mainSize);
693     bool scrollUpToReachEnd = GreatNotEqual(prevMainPos_, mainSize) && LessOrEqual(curMainPos, mainSize);
694     // verify layout size to avoid trigger reach_end event at first [PerformLayout] when layout size is zero
695     if ((scrollDownToReachEnd || scrollUpToReachEnd) && GetLayoutSize().IsValid()) {
696         listEventFlags_[ListEvents::REACH_END] = true;
697     }
698     if (!fixedMainSize_) {
699         fixedMainSize_ = !(reachStart_ && reachEnd_);
700     }
701     // Check if disable or enable scrollable
702     CalculateMainScrollExtent(curMainPos, mainSize);
703 
704     // Set position for each child
705     Size layoutSize;
706     if (isLaneList_) {
707         layoutSize = SetItemsPositionForLaneList(mainSize);
708     } else {
709         layoutSize = SetItemsPosition(mainSize);
710     }
711 
712     // Set layout size of list component itself
713     if ((hasHeight_ && vertical_) || (hasWidth_ && !vertical_)) {
714         SetLayoutSize(GetLayoutParam().GetMaxSize());
715     } else {
716         SetLayoutSize(GetLayoutParam().Constrain(layoutSize));
717     }
718 
719     // Clear auto scrolling flags
720     autoScrollingForItemMove_ = false;
721     double currentTotalOffset = startIndexOffset_ - currentOffset_;
722 
723     if (!NearEqual(currentTotalOffset, prevTotalOffset)) {
724         SetPaintState(true);
725     } else {
726         SetPaintState(false);
727     }
728 
729     if (!NearEqual(currentTotalOffset, prevTotalOffset)) {
730         auto offset = Dimension((currentTotalOffset - prevTotalOffset) / dipScale_, DimensionUnit::VP);
731         if (scrollable_ && scrollable_->Idle()) {
732             ResumeEventCallback(component_, &ListComponent::GetOnScroll, offset, ScrollState::IDLE);
733         } else {
734             ResumeEventCallback(component_, &ListComponent::GetOnScroll, offset, scrollState_);
735         }
736     }
737 
738     realMainSize_ = curMainPos - currentOffset_;
739     HandleListEvent();
740     prevOffset_ = currentOffset_;
741     prevMainPos_ = curMainPos;
742     UpdateAccessibilityScrollAttr();
743     UpdateAccessibilityVisible();
744 }
745 
746 #define CASE_OF_LIST_EVENT_WITH_NO_PARAM(eventNumber, callback)        \
747     case ListEvents::eventNumber:                                      \
748         if (event.second) {                                            \
749             ResumeEventCallback(component_, &ListComponent::callback); \
750             LOGD("list event %{public}s triggered.", #eventNumber);    \
751             event.second = false;                                      \
752         }                                                              \
753         break;
754 
HandleListEvent()755 void RenderList::HandleListEvent()
756 {
757     for (auto& event : listEventFlags_) {
758         switch (event.first) {
759             CASE_OF_LIST_EVENT_WITH_NO_PARAM(SCROLL_STOP, GetOnScrollStop);
760             CASE_OF_LIST_EVENT_WITH_NO_PARAM(REACH_START, GetOnReachStart);
761             CASE_OF_LIST_EVENT_WITH_NO_PARAM(REACH_END, GetOnReachEnd);
762             default:
763                 LOGW("This event does not handle in here, please check. event number: %{public}d", event.first);
764                 break;
765         }
766     }
767 }
768 
CalculateLaneCrossOffset(double crossSize,double childCrossSize)769 double RenderList::CalculateLaneCrossOffset(double crossSize, double childCrossSize)
770 {
771     double delta = crossSize - childCrossSize;
772     // TODO: modify in rtl scenario
773     switch (component_->GetAlignListItemAlign()) {
774         case ListItemAlign::START:
775             return 0.0;
776         case ListItemAlign::CENTER:
777             return delta / CENTER_ALIGN_DIVIDER;
778         case ListItemAlign::END:
779             return delta;
780         default:
781             LOGW("Invalid ListItemAlign: %{public}d", component_->GetAlignListItemAlign());
782             return 0.0;
783     }
784 }
785 
SetItemsPositionForLaneList(double mainSize)786 Size RenderList::SetItemsPositionForLaneList(double mainSize)
787 {
788     double crossSize = fixedCrossSize_ ? GetCrossSize(GetLayoutParam().GetMaxSize()) : 0.0;
789     if (items_.empty()) {
790         return MakeValue<Size>(fixedMainSize_ ? mainSize : 0.0, crossSize);
791     }
792 
793     double curMainPos = currentOffset_;
794     size_t index = startIndex_;
795     size_t newStickyIndex = 0;
796     RefPtr<RenderListItem> newStickyItem;
797     RefPtr<RenderListItem> nextStickyItem;
798     double nextStickyMainAxis = Size::INFINITE_SIZE;
799     size_t firstIdx = INITIAL_CHILD_INDEX;
800     size_t lastIdx = 0;
801     double selectedItemMainSize = selectedItem_ ? GetMainSize(selectedItem_->GetLayoutSize()) : 0.0;
802 
803     for (auto iter = items_.begin(); iter != items_.end();) {
804         RefPtr<RenderListItem> child;
805         double childMainSize = 0.0;
806         double childCrossSize = 0.0;
807         std::vector<RefPtr<RenderListItem>> itemSet;
808         // start set child position in a row
809         for (int32_t rowIndex = 0; rowIndex < lanes_; rowIndex++) {
810             child = *iter;
811             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
812             if (itemGroup && rowIndex > 0) {
813                 break;
814             }
815             double singleChildSize = GetMainSize(child->GetLayoutSize());
816             childCrossSize += GetCrossSize(child->GetLayoutSize());
817             childMainSize = std::max(childMainSize, singleChildSize); // get max item height in a row as row height
818             // store items in a row, set position of each item after done getting [childMainSize]
819             itemSet.emplace_back(child);
820             if ((++iter) == items_.end() || itemGroup) {
821                 break;
822             }
823         }
824         if (!fixedCrossSize_) {
825             crossSize = std::max(crossSize, childCrossSize);
826         }
827         auto offset = MakeValue<Offset>(curMainPos, 0.0);
828         if (chainAnimation_) {
829             double chainDelta = GetChainDelta(index);
830             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
831             if (itemGroup) {
832                 itemGroup->SetChainOffset(-chainDelta);
833             }
834             offset += MakeValue<Offset>(-chainDelta, 0.0);
835         }
836         // set item position for one row
837         for (size_t i = 0; i < itemSet.size(); i++) {
838             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(itemSet[i]);
839             double itemCrossSize = itemGroup ? crossSize : crossSize / lanes_;
840             auto offsetCross = CalculateLaneCrossOffset(itemCrossSize, GetCrossSize(itemSet[i]->GetLayoutSize()));
841             auto position = offset + MakeValue<Offset>(0.0, itemCrossSize * i + offsetCross);
842             if (isRightToLeft_) {
843                 if (IsVertical()) {
844                     position = MakeValue<Offset>(
845                         GetMainAxis(position), crossSize - childCrossSize / itemSet.size() - GetCrossAxis(position));
846                 } else {
847                     position =
848                         MakeValue<Offset>(mainSize - childMainSize - GetMainAxis(position), GetCrossAxis(position));
849                 }
850             }
851             SetChildPosition(itemSet[i], position);
852         }
853 
854         if (lanes_ == 1) {
855             if (selectedItem_) {
856                 double range = std::min(selectedItemMainSize, childMainSize) / 2.0;
857                 bool beforeSelectedItem = index <= selectedItemIndex_;
858                 if (beforeSelectedItem && targetIndex_ == index) {
859                     targetMainAxis_ = curMainPos;
860                     curMainPos += selectedItemMainSize + spaceWidth_;
861                 }
862 
863                 if (movingForward_) {
864                     double axis = selectedItemMainAxis_;
865                     if (GreatOrEqual(axis, curMainPos) && LessNotEqual(axis, curMainPos + range)) {
866                         targetIndex_ = beforeSelectedItem ? index : index - 1;
867                         targetMainAxis_ = curMainPos;
868                         curMainPos += selectedItemMainSize + spaceWidth_;
869                     }
870                 } else {
871                     double axis = selectedItemMainAxis_ + selectedItemMainSize;
872                     double limit = curMainPos + childMainSize;
873                     if (GreatNotEqual(axis, limit - range) && LessOrEqual(axis, limit)) {
874                         targetIndex_ = beforeSelectedItem ? index + 1 : index;
875                         targetMainAxis_ = curMainPos;
876                         curMainPos -= selectedItemMainSize + spaceWidth_;
877                     }
878                 }
879             }
880 
881             // Disable sticky mode while expand all items
882             if (fixedMainSize_ && itemSet[0]->GetSticky() != StickyMode::NONE) {
883                 if (LessOrEqual(curMainPos, 0.0)) {
884                     newStickyItem = itemSet[0];
885                     newStickyIndex = index;
886                 } else if (!nextStickyItem) {
887                     nextStickyItem = itemSet[0];
888                     nextStickyMainAxis = curMainPos;
889                 }
890             }
891         }
892         itemSet.clear();
893 
894         childMainSize += spaceWidth_;
895         if (LessNotEqual(curMainPos, mainSize) && GreatNotEqual(curMainPos + childMainSize, 0.0)) {
896             firstIdx = std::min(firstIdx, index);
897             lastIdx = std::max(lastIdx, index);
898         }
899 
900         if (child != selectedItem_) {
901             curMainPos += childMainSize;
902         }
903 
904         if (selectedItem_ && index > selectedItemIndex_ && targetIndex_ == index) {
905             targetMainAxis_ = curMainPos;
906             curMainPos += selectedItemMainSize + spaceWidth_;
907         }
908 
909         ++index;
910     }
911     if (firstIdx != firstDisplayIndex_ || lastIdx != lastDisplayIndex_) {
912         firstDisplayIndex_ = firstIdx;
913         lastDisplayIndex_ = lastIdx;
914         ResumeEventCallback(component_, &ListComponent::GetOnScrollIndex, static_cast<int32_t>(firstDisplayIndex_),
915             static_cast<int32_t>(lastDisplayIndex_), static_cast<int32_t>(midDisplayIndex_));
916     }
917 
918     // Disable sticky mode while expand all items
919     if (!fixedMainSize_) {
920         return MakeValue<Size>(curMainPos - spaceWidth_, crossSize);
921     }
922 
923     if (lanes_ == 1) {
924         UpdateStickyListItem(newStickyItem, newStickyIndex, nextStickyItem);
925         if (currentStickyItem_) {
926             const auto& stickyItemLayoutSize = currentStickyItem_->GetLayoutSize();
927             const double mainStickySize = GetMainSize(stickyItemLayoutSize) + spaceWidth_;
928             if (nextStickyItem && LessNotEqual(nextStickyMainAxis, mainStickySize)) {
929                 auto position = MakeValue<Offset>(nextStickyMainAxis - mainStickySize, 0.0);
930                 if (isRightToLeft_) {
931                     if (IsVertical()) {
932                         position = MakeValue<Offset>(GetMainAxis(position),
933                             crossSize - GetCrossSize(stickyItemLayoutSize) - GetCrossAxis(position));
934                     } else {
935                         position = MakeValue<Offset>(
936                             mainSize - mainStickySize - GetMainAxis(position), GetCrossAxis(position));
937                     }
938                 }
939                 currentStickyItem_->SetPosition(position);
940             } else {
941                 currentStickyItem_->SetPosition(MakeValue<Offset>(0.0, 0.0));
942             }
943 
944             if (!fixedCrossSize_) {
945                 crossSize = std::max(crossSize, GetCrossSize(stickyItemLayoutSize));
946             }
947         }
948     }
949 
950     return MakeValue<Size>(mainSize, crossSize);
951 }
952 
SetItemsPosition(double mainSize)953 Size RenderList::SetItemsPosition(double mainSize)
954 {
955     double crossSize = fixedCrossSize_ ? GetCrossSize(GetLayoutParam().GetMaxSize()) : 0.0;
956     if (items_.empty()) {
957         return MakeValue<Size>(fixedMainSize_ ? mainSize : 0.0, crossSize);
958     }
959 
960     double curMainPos = currentOffset_;
961     size_t index = startIndex_;
962     size_t newStickyIndex = 0;
963     RefPtr<RenderListItem> newStickyItem;
964     RefPtr<RenderListItem> nextStickyItem;
965     double nextStickyMainAxis = Size::INFINITE_SIZE;
966     size_t firstIdx = INITIAL_CHILD_INDEX;
967     size_t lastIdx = 0;
968     double selectedItemMainSize = selectedItem_ ? GetMainSize(selectedItem_->GetLayoutSize()) : 0.0;
969 
970     for (const auto& child : items_) {
971         const auto& childLayoutSize = child->GetLayoutSize();
972         double childMainSize = GetMainSize(childLayoutSize);
973 
974         if (selectedItem_) {
975             double range = std::min(selectedItemMainSize, childMainSize) / 2.0;
976             bool beforeSelectedItem = index <= selectedItemIndex_;
977             if (beforeSelectedItem && targetIndex_ == index) {
978                 targetMainAxis_ = curMainPos;
979                 curMainPos += selectedItemMainSize + spaceWidth_;
980             }
981 
982             if (movingForward_) {
983                 double axis = selectedItemMainAxis_;
984                 if (GreatOrEqual(axis, curMainPos) && LessNotEqual(axis, curMainPos + range)) {
985                     targetIndex_ = beforeSelectedItem ? index : index - 1;
986                     targetMainAxis_ = curMainPos;
987                     curMainPos += selectedItemMainSize + spaceWidth_;
988                 }
989             } else {
990                 double axis = selectedItemMainAxis_ + selectedItemMainSize;
991                 double limit = curMainPos + childMainSize;
992                 if (GreatNotEqual(axis, limit - range) && LessOrEqual(axis, limit)) {
993                     targetIndex_ = beforeSelectedItem ? index + 1 : index;
994                     targetMainAxis_ = curMainPos;
995                     curMainPos -= selectedItemMainSize + spaceWidth_;
996                 }
997             }
998         }
999 
1000         auto offsetCross = CalculateLaneCrossOffset(crossSize, GetCrossSize(childLayoutSize));
1001         auto offset = MakeValue<Offset>(curMainPos, offsetCross);
1002         if (chainAnimation_) {
1003             double chainDelta = GetChainDelta(index);
1004             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1005             if (itemGroup) {
1006                 itemGroup->SetChainOffset(-chainDelta);
1007             }
1008             offset += MakeValue<Offset>(-chainDelta, 0.0);
1009         }
1010 
1011         if (isRightToLeft_) {
1012             if (IsVertical()) {
1013                 offset = MakeValue<Offset>(
1014                     GetMainAxis(offset), crossSize - GetCrossSize(childLayoutSize) - GetCrossAxis(offset));
1015             } else {
1016                 offset = MakeValue<Offset>(
1017                     mainSize - GetMainSize(childLayoutSize) - GetMainAxis(offset), GetCrossAxis(offset));
1018             }
1019         }
1020         SetChildPosition(child, offset);
1021         // Disable sticky mode while expand all items
1022         if (fixedMainSize_ && child->GetSticky() != StickyMode::NONE) {
1023             if (LessOrEqual(curMainPos, 0.0)) {
1024                 newStickyItem = child;
1025                 newStickyIndex = index;
1026             } else if (!nextStickyItem) {
1027                 nextStickyItem = child;
1028                 nextStickyMainAxis = curMainPos;
1029             }
1030         }
1031 
1032         childMainSize += spaceWidth_;
1033         if (LessNotEqual(curMainPos, mainSize) && GreatNotEqual(curMainPos + childMainSize, 0.0)) {
1034             if (!fixedCrossSize_) {
1035                 crossSize = std::max(crossSize, GetCrossSize(childLayoutSize));
1036             }
1037             firstIdx = std::min(firstIdx, index);
1038             lastIdx = std::max(lastIdx, index);
1039         }
1040 
1041         if (child != selectedItem_) {
1042             curMainPos += childMainSize;
1043         }
1044 
1045         if (selectedItem_ && index > selectedItemIndex_ && targetIndex_ == index) {
1046             targetMainAxis_ = curMainPos;
1047             curMainPos += selectedItemMainSize + spaceWidth_;
1048         }
1049 
1050         ++index;
1051     }
1052     if (firstIdx != firstDisplayIndex_ || lastIdx != lastDisplayIndex_) {
1053         firstDisplayIndex_ = firstIdx;
1054         lastDisplayIndex_ = lastIdx;
1055         ResumeEventCallback(component_, &ListComponent::GetOnScrollIndex, static_cast<int32_t>(firstDisplayIndex_),
1056             static_cast<int32_t>(lastDisplayIndex_), static_cast<int32_t>(midDisplayIndex_));
1057     }
1058 
1059     // Disable sticky mode while expand all items
1060     if (!fixedMainSize_) {
1061         return MakeValue<Size>(curMainPos - spaceWidth_, crossSize);
1062     }
1063 
1064     UpdateStickyListItem(newStickyItem, newStickyIndex, nextStickyItem);
1065     if (currentStickyItem_) {
1066         const auto& stickyItemLayoutSize = currentStickyItem_->GetLayoutSize();
1067         const double mainStickySize = GetMainSize(stickyItemLayoutSize) + spaceWidth_;
1068         auto offsetCross = CalculateLaneCrossOffset(crossSize, GetCrossSize(currentStickyItem_->GetLayoutSize()));
1069         if (nextStickyItem && LessNotEqual(nextStickyMainAxis, mainStickySize)) {
1070             auto position = MakeValue<Offset>(nextStickyMainAxis - mainStickySize, offsetCross);
1071             if (isRightToLeft_) {
1072                 if (IsVertical()) {
1073                     position = MakeValue<Offset>(
1074                         GetMainAxis(position), crossSize - GetCrossSize(stickyItemLayoutSize) - GetCrossAxis(position));
1075                 } else {
1076                     position =
1077                         MakeValue<Offset>(mainSize - mainStickySize - GetMainAxis(position), GetCrossAxis(position));
1078                 }
1079             }
1080             currentStickyItem_->SetPosition(position);
1081         } else {
1082             currentStickyItem_->SetPosition(MakeValue<Offset>(0.0, offsetCross));
1083         }
1084 
1085         if (!fixedCrossSize_) {
1086             crossSize = std::max(crossSize, GetCrossSize(stickyItemLayoutSize));
1087         }
1088     }
1089 
1090     return MakeValue<Size>(mainSize, crossSize);
1091 }
1092 
UpdateStickyListItem(const RefPtr<RenderListItem> & newStickyItem,size_t newStickyItemIndex,const RefPtr<RenderListItem> & nextStickyItem)1093 void RenderList::UpdateStickyListItem(const RefPtr<RenderListItem>& newStickyItem, size_t newStickyItemIndex,
1094     const RefPtr<RenderListItem>& nextStickyItem)
1095 {
1096     if (newStickyItem) {
1097         if (newStickyItem == currentStickyItem_) {
1098             return;
1099         }
1100 
1101         if (currentStickyItem_ && currentStickyIndex_ < startIndex_) {
1102             RecycleListItem(currentStickyIndex_);
1103         }
1104 
1105         currentStickyItem_ = newStickyItem;
1106         currentStickyIndex_ = newStickyItemIndex;
1107         return;
1108     }
1109 
1110     if (nextStickyItem && nextStickyItem == currentStickyItem_) {
1111         ApplyPreviousStickyListItem(currentStickyIndex_ - 1, true);
1112         return;
1113     }
1114 
1115     if (currentStickyIndex_ == INITIAL_CHILD_INDEX && startIndex_ > 0) {
1116         ApplyPreviousStickyListItem(startIndex_ - 1, true);
1117     }
1118 }
1119 
MakeInnerLayout()1120 LayoutParam RenderList::MakeInnerLayout()
1121 {
1122     Size maxSize;
1123     Size minSize;
1124     if (vertical_) {
1125         maxSize = Size(GetLayoutParam().GetMaxSize().Width(), Size::INFINITE_SIZE);
1126         minSize = Size(GetLayoutParam().GetMinSize().Width(), 0.0);
1127     } else {
1128         maxSize = Size(Size::INFINITE_SIZE, GetLayoutParam().GetMaxSize().Height());
1129         minSize = Size(0.0, GetLayoutParam().GetMinSize().Height());
1130     }
1131     return LayoutParam(maxSize, minSize);
1132 }
1133 
MakeInnerLayoutForLane()1134 LayoutParam RenderList::MakeInnerLayoutForLane()
1135 {
1136     Size maxSize;
1137     Size minSize;
1138     if (vertical_) {
1139         maxSize = Size(std::min(GetLayoutParam().GetMaxSize().Width() / lanes_, maxLaneLength_), Size::INFINITE_SIZE);
1140         minSize = Size(GetLayoutParam().GetMinSize().Width(), 0.0);
1141     } else {
1142         maxSize = Size(Size::INFINITE_SIZE, std::min(GetLayoutParam().GetMaxSize().Height() / lanes_, maxLaneLength_));
1143         minSize = Size(0.0, GetLayoutParam().GetMinSize().Height());
1144     }
1145     return LayoutParam(maxSize, minSize);
1146 }
1147 
GetCurMainPosAndMainSize(double & curMainPos,double & mainSize)1148 bool RenderList::GetCurMainPosAndMainSize(double& curMainPos, double& mainSize)
1149 {
1150     // Check validation of layout size
1151     mainSize = ApplyLayoutParam();
1152     if (NearZero(mainSize)) {
1153         LOGW("Cannot layout using invalid view port");
1154         return false;
1155     }
1156     if (isLaneList_) {
1157         curMainPos = LayoutOrRecycleCurrentItemsForLaneList(mainSize);
1158     } else {
1159         curMainPos = LayoutOrRecycleCurrentItems(mainSize);
1160     }
1161     // Try to request new items at end if needed
1162     for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
1163         if (cachedCount_ != 0) {
1164             if (endCachedCount_ >= cachedCount_) {
1165                 break;
1166             }
1167         } else {
1168             if (GreatOrEqual(curMainPos, endMainPos_)) {
1169                 break;
1170             }
1171         }
1172         auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
1173         if (!child) {
1174             startIndex_ = std::min(startIndex_, TotalCount());
1175             break;
1176         }
1177         if (GreatOrEqual(curMainPos, mainSize)) {
1178             ++endCachedCount_;
1179         }
1180         curMainPos += GetMainSize(child->GetLayoutSize()) + spaceWidth_;
1181     }
1182     if (selectedItem_ && selectedItemIndex_ < startIndex_) {
1183         curMainPos += GetMainSize(selectedItem_->GetLayoutSize()) + spaceWidth_;
1184     }
1185     curMainPos -= spaceWidth_;
1186     return true;
1187 }
1188 
HandleOverScroll()1189 bool RenderList::HandleOverScroll()
1190 {
1191     if (scrollEffect_ && scrollEffect_->IsFadeEffect() && (reachStart_ || reachEnd_)) {
1192         double overScroll = scrollEffect_->CalculateOverScroll(prevOffset_, reachEnd_);
1193         if (!NearZero(overScroll)) {
1194             Axis axis = IsVertical() ? Axis::VERTICAL : Axis::HORIZONTAL;
1195             scrollEffect_->HandleOverScroll(axis, overScroll, viewPort_);
1196         }
1197         return false;
1198     }
1199     return true;
1200 }
1201 
UpdateScrollPosition(double offset,int32_t source)1202 bool RenderList::UpdateScrollPosition(double offset, int32_t source)
1203 {
1204     if (source == SCROLL_FROM_START) {
1205         return true;
1206     }
1207     if (NearZero(offset)) {
1208         return true;
1209     }
1210     if (scrollBar_ && scrollBar_->NeedScrollBar()) {
1211         scrollBar_->SetActive(SCROLL_FROM_CHILD != source);
1212     }
1213     if (reachStart_ && HandleRefreshEffect(offset, source, currentOffset_)) {
1214         return false;
1215     }
1216     if (reachStart_ && reachEnd_) {
1217         return false;
1218     }
1219     if (offset > 0.0) {
1220         if (reachStart_ && (!scrollEffect_ || source == SCROLL_FROM_AXIS)) {
1221             return false;
1222         }
1223         reachEnd_ = false;
1224     } else {
1225         if (reachEnd_ && (!scrollEffect_ || source == SCROLL_FROM_AXIS)) {
1226             return false;
1227         }
1228         reachStart_ = false;
1229     }
1230     auto context = context_.Upgrade();
1231     if (context) {
1232         dipScale_ = context->GetDipScale();
1233     }
1234     offset_ = offset;
1235     if (source == SCROLL_FROM_UPDATE) {
1236         scrollState_ = ScrollState::SCROLL;
1237     } else if (source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING) {
1238         scrollState_ = ScrollState::FLING;
1239     } else {
1240         scrollState_ = ScrollState::IDLE;
1241     }
1242     currentOffset_ += offset;
1243     bool next = HandleOverScroll();
1244     MarkNeedLayout(true);
1245     return next;
1246 }
1247 
TouchTest(const Point & globalPoint,const Point & parentLocalPoint,const TouchRestrict & touchRestrict,TouchTestResult & result)1248 bool RenderList::TouchTest(const Point& globalPoint, const Point& parentLocalPoint, const TouchRestrict& touchRestrict,
1249     TouchTestResult& result)
1250 {
1251     // when click point is in sticky item, consume the touch event to avoid clicking on the list item underneath.
1252     if (currentStickyItem_ && currentStickyItem_->GetPaintRect().IsInRegion(parentLocalPoint)) {
1253         currentStickyItem_->TouchTest(globalPoint, parentLocalPoint, touchRestrict, result);
1254         return true;
1255     }
1256 
1257     return RenderNode::TouchTest(globalPoint, parentLocalPoint, touchRestrict, result);
1258 }
1259 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)1260 void RenderList::OnTouchTestHit(
1261     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
1262 {
1263     if (!GetVisible()) {
1264         return;
1265     }
1266 
1267     if (component_->GetEditMode() && dragDropGesture_) {
1268         dragDropGesture_->SetCoordinateOffset(coordinateOffset);
1269         result.emplace_back(dragDropGesture_);
1270     }
1271 
1272     // Disable scroll while expand all items
1273     if (!fixedMainSize_) {
1274         return;
1275     }
1276     if (!scrollable_) {
1277         return;
1278     }
1279     if (scrollable_->Available() && scrollBar_ && scrollBar_->InBarRegion(globalPoint_ - coordinateOffset)) {
1280         scrollBar_->AddScrollBarController(coordinateOffset, result);
1281     } else {
1282         scrollable_->SetCoordinateOffset(coordinateOffset);
1283         result.emplace_back(scrollable_);
1284     }
1285 }
1286 
ApplyLayoutParam()1287 double RenderList::ApplyLayoutParam()
1288 {
1289     auto maxLayoutSize = GetLayoutParam().GetMaxSize();
1290     if (!maxLayoutSize.IsValid() || maxLayoutSize.IsEmpty()) {
1291         if (!GetVisible()) {
1292             SetLayoutSize(Size());
1293         }
1294         return 0.0;
1295     }
1296 
1297     auto maxMainSize = GetMainSize(maxLayoutSize);
1298 
1299     // Update layout info for list weather layout param is changed
1300     if (IsLayoutParamChanged()) {
1301         // Minimum layout param MUST NOT be INFINITE
1302         ACE_DCHECK(!GetLayoutParam().GetMinSize().IsInfinite());
1303 
1304         if (NearEqual(maxMainSize, Size::INFINITE_SIZE)) {
1305             // Clear all child items
1306             RemoveAllItems();
1307             startIndex_ = 0;
1308             startIndexOffset_ = 0.0;
1309             currentOffset_ = 0.0;
1310 
1311             startMainPos_ = 0.0;
1312             endMainPos_ = std::numeric_limits<decltype(endMainPos_)>::max();
1313             fixedMainSizeByLayoutParam_ = false;
1314         } else {
1315             startMainPos_ = (1.0 - VIEW_PORT_SCALE) / 2 * maxMainSize;
1316             endMainPos_ = startMainPos_ + (maxMainSize * VIEW_PORT_SCALE);
1317             fixedMainSizeByLayoutParam_ = NearEqual(maxMainSize, GetMainSize(GetLayoutParam().GetMinSize()));
1318             SizeChangeOffset(maxMainSize);
1319         }
1320 
1321         fixedCrossSize_ = !NearEqual(GetCrossSize(maxLayoutSize), Size::INFINITE_SIZE);
1322         TakeBoundary(fixedMainSizeByLayoutParam_ && fixedCrossSize_);
1323     }
1324 
1325     fixedMainSize_ = fixedMainSizeByLayoutParam_;
1326     mainSize_ = maxMainSize;
1327     return maxMainSize;
1328 }
1329 
GetItemPositionState(double curMainPos,double lastItemMainSize)1330 ItemPositionState RenderList::GetItemPositionState(double curMainPos, double lastItemMainSize)
1331 {
1332     // curMainPos <= startMainPos_
1333     if (LessOrEqual(curMainPos, startMainPos_)) {
1334         return ItemPositionState::AHEAD_OF_VIEWPORT;
1335     }
1336     // (curMainPos > startMainPos_) and ((curMainPos <= endMainPos_) or (curMainPos - lastItemMainSize <= endMainPos_))
1337     if (LessOrEqual(curMainPos, endMainPos_) || (curMainPos - lastItemMainSize <= endMainPos_)) {
1338         return ItemPositionState::IN_VIEWPORT;
1339     }
1340     // curMainPos_ - lastItemMainSize > endMainPos_
1341     if (GreatNotEqual(curMainPos, endMainPos_)) {
1342         return ItemPositionState::BEHIND_VIEWPORT;
1343     }
1344     LOGE("invalid place of list item, curMainPos: %{public}f", curMainPos);
1345     return ItemPositionState::IN_VIEWPORT;
1346 }
1347 
1348 #define RECYCLE_AND_ERASE_ITEMS_OUT_OF_VIEWPORT()                                              \
1349     do {                                                                                       \
1350         for (size_t i = 0; i < itemsInOneRow.size(); i++) {                                    \
1351             if (currentStickyItem_ != itemsInOneRow[i] && selectedItem_ != itemsInOneRow[i]) { \
1352                 /* Recycle list items out of view port */                                      \
1353                 RecycleListItem(curIndex - i);                                                 \
1354             }                                                                                  \
1355             it = items_.erase(--it);                                                           \
1356         }                                                                                      \
1357     } while (0);
1358 
LayoutOrRecycleCurrentItemsForLaneList(double mainSize)1359 double RenderList::LayoutOrRecycleCurrentItemsForLaneList(double mainSize)
1360 {
1361     if (currentStickyItem_) {
1362         LayoutChild(currentStickyItem_);
1363     }
1364 
1365     double curMainPos = currentOffset_;
1366     size_t curIndex = startIndex_ - 1;
1367     std::vector<RefPtr<RenderListItem>> itemsInOneRow;
1368     for (auto it = items_.begin(); it != items_.end();) {
1369         int32_t lackItemCount = 0;
1370         // 1. layout children in a row
1371         double mainSize = 0.0;
1372         itemsInOneRow.clear();
1373         for (int32_t i = 0; i < lanes_; i++) {
1374             RefPtr<RenderListItem> child = *(it);
1375             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1376             if (itemGroup && i > 0) {
1377                 break;
1378             }
1379             if (child->IsForwardLayout()) {
1380                 LayoutChild(child, curMainPos);
1381                 double childMainSize = GetMainSize(child->GetLayoutSize());
1382                 mainSize = std::max(mainSize, childMainSize);
1383             } else {
1384                 double childMainSize = GetMainSize(child->GetLayoutSize());
1385                 mainSize = std::max(mainSize, childMainSize);
1386                 LayoutChild(child, curMainPos + mainSize, false);
1387                 double newChildMainSizee = GetMainSize(child->GetLayoutSize());
1388                 currentOffset_ -= (newChildMainSizee - childMainSize);
1389                 startIndexOffset_ -= (newChildMainSizee - childMainSize);
1390             }
1391             itemsInOneRow.emplace_back(child);
1392             ++curIndex;
1393             ++it;
1394             if (itemGroup) {
1395                 break;
1396             }
1397             // reach end of [items_]
1398             if (it == items_.end()) {
1399                 lackItemCount = lanes_ - i - 1;
1400                 break;
1401             }
1402         }
1403         // 2. calculate [curMainPos] after layout current row, deciding whether or not to request new item to fill
1404         // current row or to erase and recycle items in current row
1405         curMainPos += mainSize + spaceWidth_;
1406 
1407         // 3. do different processing according to item position state
1408         auto itemPositionState = GetItemPositionState(curMainPos, mainSize + spaceWidth_);
1409         switch (itemPositionState) {
1410             // when items are ahead of viewport, do the following things:
1411             // 1. update [startIndex_] and [currentOffset_]
1412             // 2. recycle items and erase them from [items_]
1413             case ItemPositionState::AHEAD_OF_VIEWPORT: {
1414                 startIndex_ = curIndex + 1;
1415                 startIndexOffset_ += curMainPos - currentOffset_;
1416                 currentOffset_ = curMainPos;
1417                 RECYCLE_AND_ERASE_ITEMS_OUT_OF_VIEWPORT();
1418                 break;
1419             }
1420             // when items are in viewport, continue to layout next row
1421             // if current item is the last one in [items_], request new items to supply current row
1422             case ItemPositionState::IN_VIEWPORT: {
1423                 if (lanes_ == 1) {
1424                     continue; // if list only has one lane, do not need to do suppliment for current row
1425                 }
1426                 size_t target = lackItemCount + items_.size() + startIndex_;
1427                 for (size_t newIndex = startIndex_ + items_.size(); newIndex < target; newIndex++) {
1428                     auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
1429                     if (!child) {
1430                         startIndex_ = std::min(startIndex_, TotalCount());
1431                         break;
1432                     }
1433                     if (AceType::DynamicCast<RenderListItemGroup>(child)) {
1434                         break;
1435                     }
1436                 }
1437                 break;
1438             }
1439             // when items are behind viewport, recycle items and erase them from [items_]
1440             case ItemPositionState::BEHIND_VIEWPORT: {
1441                 RECYCLE_AND_ERASE_ITEMS_OUT_OF_VIEWPORT();
1442                 curMainPos -= mainSize + spaceWidth_;
1443                 break;
1444             }
1445             default:
1446                 LOGW("unexpected item position state: %{public}d", itemPositionState);
1447                 break;
1448         }
1449     }
1450     return curMainPos;
1451 }
1452 
BackwardLayoutForCache(size_t & backwardLayoutIndex,double & backwardLayoutOffset)1453 void RenderList::BackwardLayoutForCache(size_t& backwardLayoutIndex, double& backwardLayoutOffset)
1454 {
1455     auto rit = items_.rend();
1456     std::advance(rit, (startIndex_ - backwardLayoutIndex));
1457     while (backwardLayoutIndex > startIndex_ && rit != items_.rend()) {
1458         const auto& child = *(rit++);
1459         if (!child->IsForwardLayout()) {
1460             LayoutChild(child, backwardLayoutOffset - spaceWidth_, false);
1461         }
1462         if (LessOrEqual(backwardLayoutOffset, 0.0)) {
1463             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1464             if (itemGroup) {
1465                 startCachedCount_ += itemGroup->GetCurrStartCacheCount();
1466             } else {
1467                 startCachedCount_++;
1468             }
1469         }
1470         double childSize = GetMainSize(child->GetLayoutSize());
1471         backwardLayoutOffset -= childSize + spaceWidth_;
1472         backwardLayoutIndex--;
1473         if (startCachedCount_ >= cachedCount_) {
1474             break;
1475         }
1476     }
1477 }
1478 
LayoutOrRecycleCurrentItemsForCache(double mainSize)1479 double RenderList::LayoutOrRecycleCurrentItemsForCache(double mainSize)
1480 {
1481     double curMainPos = currentOffset_;
1482     size_t curIndex = startIndex_;
1483     size_t backwardLayoutIndex = startIndex_;
1484     double backwardLayoutOffset = currentOffset_;
1485     startCachedCount_ = 0;
1486     endCachedCount_ = 0;
1487     bool recycleAll = false;
1488     if (GreatOrEqual(curMainPos, mainSize)) {
1489         recycleAll = true;
1490     }
1491     for (auto it = items_.begin(); it != items_.end(); ++curIndex) {
1492         const auto& child = *(it);
1493         if (recycleAll || endCachedCount_ >= cachedCount_) {
1494             if (currentStickyItem_ != child && selectedItem_ != child) {
1495                 // Recycle list items out of view port
1496                 RecycleListItem(curIndex);
1497             }
1498             it = items_.erase(it);
1499             continue;
1500         }
1501 
1502         if (GreatOrEqual(curMainPos, mainSize)) {
1503             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1504             endCachedCount_ += itemGroup ? itemGroup->GetCurrEndCacheCount() : 1;
1505         }
1506 
1507         if (child->IsForwardLayout()) {
1508             LayoutChild(child, curMainPos);
1509         }
1510         double childSize = GetMainSize(child->GetLayoutSize());
1511         curMainPos += childSize + spaceWidth_;
1512 
1513         if (LessOrEqual(curMainPos, 0.0) || !child->IsForwardLayout()) {
1514             backwardLayoutIndex = curIndex + 1;
1515             backwardLayoutOffset = curMainPos;
1516         }
1517         ++it;
1518     }
1519 
1520     if (backwardLayoutIndex > startIndex_) {
1521         BackwardLayoutForCache(backwardLayoutIndex, backwardLayoutOffset);
1522     }
1523 
1524     curIndex = startIndex_;
1525     for (auto it = items_.begin(); it != items_.end() && curIndex < backwardLayoutIndex; ++curIndex) {
1526         const auto& child = *(it);
1527         if (currentStickyItem_ != child && selectedItem_ != child) {
1528             // Recycle list items out of view port
1529             RecycleListItem(curIndex);
1530         }
1531         it = items_.erase(it);
1532     }
1533     startIndex_ = backwardLayoutIndex;
1534     startIndexOffset_ += backwardLayoutOffset - currentOffset_;
1535     currentOffset_ = backwardLayoutOffset;
1536     return curMainPos;
1537 }
1538 
LayoutOrRecycleCurrentItems(double mainSize)1539 double RenderList::LayoutOrRecycleCurrentItems(double mainSize)
1540 {
1541     if (currentStickyItem_) {
1542         LayoutChild(currentStickyItem_);
1543     }
1544 
1545     if (cachedCount_ != 0) {
1546         return LayoutOrRecycleCurrentItemsForCache(mainSize);
1547     }
1548 
1549     double curMainPos = currentOffset_;
1550     size_t curIndex = startIndex_;
1551     for (auto it = items_.begin(); it != items_.end(); ++curIndex) {
1552         const auto& child = *(it);
1553         if (LessOrEqual(curMainPos, endMainPos_)) {
1554             if (child->IsForwardLayout()) {
1555                 LayoutChild(child, curMainPos);
1556                 double childSize = GetMainSize(child->GetLayoutSize());
1557                 curMainPos += childSize + spaceWidth_;
1558             } else {
1559                 double oldChildSize = GetMainSize(child->GetLayoutSize());
1560                 LayoutChild(child, curMainPos + oldChildSize, false);
1561                 curMainPos += oldChildSize + spaceWidth_;
1562                 double childSize = GetMainSize(child->GetLayoutSize());
1563                 currentOffset_ -= (childSize - oldChildSize);
1564                 startIndexOffset_ -= (childSize - oldChildSize);
1565             }
1566             if (GreatOrEqual(curMainPos, startMainPos_)) {
1567                 ++it;
1568                 continue;
1569             }
1570             startIndexOffset_ += curMainPos - currentOffset_;
1571             currentOffset_ = curMainPos;
1572             startIndex_ = curIndex + 1;
1573         }
1574 
1575         if (currentStickyItem_ != child && selectedItem_ != child) {
1576             // Recycle list items out of view port
1577             RecycleListItem(curIndex);
1578         }
1579         it = items_.erase(it);
1580     }
1581 
1582     return curMainPos;
1583 }
1584 
RequestAndLayoutNewItem(size_t index,double currMainPos,bool forward)1585 RefPtr<RenderListItem> RenderList::RequestAndLayoutNewItem(size_t index, double currMainPos, bool forward)
1586 {
1587     RefPtr<RenderListItem> newChild;
1588     if (index == currentStickyIndex_ && currentStickyItem_) {
1589         newChild = currentStickyItem_;
1590     } else {
1591         {
1592             ACE_SCOPED_TRACE("RenderList:BuildListItem");
1593             newChild = RequestListItem(index);
1594         }
1595         if (newChild) {
1596             ACE_SCOPED_TRACE("RenderList:MeasureListItem");
1597             AddChildItem(newChild);
1598             LayoutChild(newChild, currMainPos, forward);
1599         }
1600     }
1601 
1602     if (newChild) {
1603         if (index < startIndex_) {
1604             items_.emplace_front(newChild);
1605         } else {
1606             items_.emplace_back(newChild);
1607         }
1608     }
1609     return newChild;
1610 }
1611 
RequestListItem(size_t index)1612 RefPtr<RenderListItem> RenderList::RequestListItem(size_t index)
1613 {
1614     auto generator = itemGenerator_.Upgrade();
1615     auto newItem = generator ? generator->RequestListItem(index) : RefPtr<RenderListItem>();
1616     if (!newItem) {
1617         return newItem;
1618     }
1619 
1620     if (component_->GetEditMode()) {
1621         newItem->SetEditMode(true);
1622         newItem->SetOnDeleteClick([weak = AceType::WeakClaim(this)](RefPtr<RenderListItem> item) {
1623             auto spThis = weak.Upgrade();
1624             if (!spThis) {
1625                 return;
1626             }
1627             spThis->OnItemDelete(item);
1628         });
1629 
1630         newItem->SetOnSelect([weak = AceType::WeakClaim(this)](RefPtr<RenderListItem> item) {
1631             auto spThis = weak.Upgrade();
1632             if (!spThis) {
1633                 return;
1634             }
1635             spThis->OnItemSelect(item);
1636         });
1637     }
1638 
1639     if (!newItem->GetVisible()) {
1640         newItem->SetVisible(true);
1641     }
1642 
1643     if (newItem->GetHidden()) {
1644         newItem->SetHidden(false);
1645     }
1646 
1647     return newItem;
1648 }
1649 
RecycleListItem(size_t index)1650 void RenderList::RecycleListItem(size_t index)
1651 {
1652     auto generator = itemGenerator_.Upgrade();
1653     if (generator) {
1654         generator->RecycleListItem(index);
1655     }
1656 }
1657 
FindItemStartIndex(size_t index)1658 size_t RenderList::FindItemStartIndex(size_t index)
1659 {
1660     auto generator = itemGenerator_.Upgrade();
1661     if (generator) {
1662         return generator->FindItemStartIndex(index);
1663     }
1664     return 0;
1665 }
1666 
GetItemRelativeIndex(size_t index)1667 size_t RenderList::GetItemRelativeIndex(size_t index)
1668 {
1669     return index - FindItemStartIndex(index);
1670 }
1671 
TotalCount()1672 size_t RenderList::TotalCount()
1673 {
1674     auto generator = itemGenerator_.Upgrade();
1675     return generator ? generator->TotalCount() : 0;
1676 }
1677 
FindPreviousStickyListItem(size_t index)1678 size_t RenderList::FindPreviousStickyListItem(size_t index)
1679 {
1680     auto generator = itemGenerator_.Upgrade();
1681     return generator ? generator->FindPreviousStickyListItem(index) : ListItemGenerator::INVALID_INDEX;
1682 }
1683 
OnItemDelete(const RefPtr<RenderListItem> & item)1684 void RenderList::OnItemDelete(const RefPtr<RenderListItem>& item)
1685 {
1686     size_t index = GetIndexByListItem(item);
1687     if (!ResumeEventCallback(component_, &ListComponent::GetOnItemDelete, false, static_cast<int32_t>(index))) {
1688         LOGI("User canceled, stop deleting item");
1689         return;
1690     }
1691 
1692     if (index < startIndex_) {
1693         --startIndex_;
1694         useEstimateCurrentOffset_ = true;
1695     }
1696 }
1697 
OnItemSelect(const RefPtr<RenderListItem> & item)1698 void RenderList::OnItemSelect(const RefPtr<RenderListItem>& item)
1699 {
1700     targetIndex_ = GetIndexByListItem(item);
1701     selectedItemIndex_ = targetIndex_;
1702     selectedItem_ = item;
1703     selectedItemMainAxis_ = GetMainAxis(item->GetPosition());
1704     LOGI("Select list item %{private}zu to move", selectedItemIndex_);
1705 }
1706 
GetIndexByListItem(const RefPtr<RenderListItem> & item) const1707 size_t RenderList::GetIndexByListItem(const RefPtr<RenderListItem>& item) const
1708 {
1709     ACE_DCHECK(item);
1710 
1711     auto it = std::find(items_.begin(), items_.end(), item);
1712     if (it != items_.end()) {
1713         int32_t offset = std::distance(items_.begin(), it);
1714         ACE_DCHECK(offset >= 0);
1715         return startIndex_ + offset;
1716     }
1717 
1718     ACE_DCHECK(fixedMainSize_);
1719     ACE_DCHECK(item == currentStickyItem_);
1720     return currentStickyIndex_;
1721 }
1722 
RemoveAllItems()1723 void RenderList::RemoveAllItems()
1724 {
1725     items_.clear();
1726     ClearChildren();
1727     currentStickyItem_.Reset();
1728     currentStickyIndex_ = INITIAL_CHILD_INDEX;
1729     isActionByScroll_ = false;
1730 }
1731 
ApplyPreviousStickyListItem(size_t index,bool needLayout)1732 void RenderList::ApplyPreviousStickyListItem(size_t index, bool needLayout)
1733 {
1734     size_t newIndex = FindPreviousStickyListItem(index);
1735     if (newIndex == ListItemGenerator::INVALID_INDEX) {
1736         currentStickyItem_.Reset();
1737         currentStickyIndex_ = INVALID_CHILD_INDEX;
1738         return;
1739     }
1740 
1741     currentStickyIndex_ = newIndex;
1742     currentStickyItem_ = RequestListItem(currentStickyIndex_);
1743     if (currentStickyIndex_ < startIndex_) {
1744         AddChildItem(currentStickyItem_);
1745         if (needLayout) {
1746             LayoutChild(currentStickyItem_);
1747         }
1748     }
1749 }
1750 
JumpToIndex(int32_t idx,int32_t source)1751 void RenderList::JumpToIndex(int32_t idx, int32_t source)
1752 {
1753     RemoveAllItems();
1754     startIndex_ = static_cast<size_t>(idx);
1755     useEstimateCurrentOffset_ = true;
1756     currentOffset_ = 0.0;
1757     MarkNeedLayout(true);
1758 }
1759 
AnimateTo(const Dimension & position,float duration,const RefPtr<Curve> & curve)1760 void RenderList::AnimateTo(const Dimension& position, float duration, const RefPtr<Curve>& curve)
1761 {
1762     if (!animator_->IsStopped()) {
1763         animator_->Stop();
1764     }
1765     animator_->ClearInterpolators();
1766     auto pos = NormalizePercentToPx(position, IsVertical());
1767     double currentOffset = startIndexOffset_ - currentOffset_;
1768     auto animation = AceType::MakeRefPtr<CurveAnimation<double>>(currentOffset, pos, curve);
1769     animation->AddListener([weak = AceType::WeakClaim(this)](double pos) {
1770         auto renderList = weak.Upgrade();
1771         if (!renderList) {
1772             return;
1773         }
1774         if (renderList->scrollable_ && !renderList->scrollable_->IsSpringMotionRunning()) {
1775             double delta = (renderList->startIndexOffset_ - renderList->currentOffset_) - pos;
1776             renderList->UpdateScrollPosition(delta, SCROLL_FROM_JUMP);
1777         }
1778     });
1779     animator_->AddInterpolator(animation);
1780     animator_->SetDuration(std::min(duration, SCROLL_MAX_TIME));
1781     animator_->ClearStopListeners();
1782     animator_->Play();
1783 }
1784 
CurrentOffset()1785 Offset RenderList::CurrentOffset()
1786 {
1787     double currentOffset = startIndexOffset_ - currentOffset_;
1788     auto ctx = GetContext().Upgrade();
1789     if (!ctx) {
1790         return vertical_ ? Offset(0.0, currentOffset) : Offset(currentOffset, 0.0);
1791     }
1792     auto mainOffset = ctx->ConvertPxToVp(Dimension(currentOffset, DimensionUnit::PX));
1793     return vertical_ ? Offset(0.0, mainOffset) : Offset(mainOffset, 0.0);
1794 }
1795 
ScrollToEdge(ScrollEdgeType scrollEdgeType,bool smooth)1796 void RenderList::ScrollToEdge(ScrollEdgeType scrollEdgeType, bool smooth)
1797 {
1798     if (scrollable_ && !scrollable_->IsAnimationNotRunning()) {
1799         scrollable_->StopScrollable();
1800     }
1801     if (vertical_) {
1802         if (scrollEdgeType == ScrollEdgeType::SCROLL_TOP) {
1803             JumpToIndex(0, SCROLL_FROM_JUMP);
1804         } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1805             JumpToIndex(TotalCount(), SCROLL_FROM_JUMP);
1806         }
1807     } else {
1808         if (scrollEdgeType == ScrollEdgeType::SCROLL_LEFT) {
1809             JumpToIndex(0, SCROLL_FROM_JUMP);
1810         } else if (scrollEdgeType == ScrollEdgeType::SCROLL_RIGHT) {
1811             JumpToIndex(TotalCount(), SCROLL_FROM_JUMP);
1812         }
1813     }
1814 }
1815 
ScrollPage(bool reverse,bool smooth)1816 void RenderList::ScrollPage(bool reverse, bool smooth)
1817 {
1818     if (scrollable_ && !scrollable_->IsAnimationNotRunning()) {
1819         scrollable_->StopScrollable();
1820     }
1821     double pageSize = GetMainSize(GetLayoutSize());
1822     if (reverse) {
1823         if (!reachStart_) {
1824             currentOffset_ += pageSize;
1825             MarkNeedLayout();
1826         }
1827     } else {
1828         if (!reachEnd_) {
1829             currentOffset_ -= pageSize;
1830             MarkNeedLayout();
1831         }
1832     }
1833 }
1834 
ScrollBy(double pixelX,double pixelY)1835 void RenderList::ScrollBy(double pixelX, double pixelY)
1836 {
1837     if (IsVertical()) {
1838         currentOffset_ -= pixelY;
1839     } else {
1840         currentOffset_ -= pixelX;
1841     }
1842     MarkNeedLayout();
1843 }
1844 
AdjustOffset(Offset & delta,int32_t source)1845 void RenderList::AdjustOffset(Offset& delta, int32_t source)
1846 {
1847     // when scrollEffect equal to none, no need to adjust offset
1848     if (!scrollEffect_) {
1849         return;
1850     }
1851 
1852     if (delta.IsZero() || source == SCROLL_FROM_ANIMATION || source == SCROLL_FROM_ANIMATION_SPRING) {
1853         return;
1854     }
1855 
1856     double viewPortSize = GetMainSize(GetPaintRect().GetSize());
1857     double offset = GetMainAxis(delta);
1858     if (NearZero(viewPortSize) || NearZero(offset)) {
1859         return;
1860     }
1861 
1862     double maxScrollExtent = mainScrollExtent_ - viewPortSize;
1863     double overscrollPastStart = 0.0;
1864     double overscrollPastEnd = 0.0;
1865     double overscrollPast = 0.0;
1866     bool easing = false;
1867 
1868     overscrollPastStart = std::max(GetCurrentPosition(), 0.0);
1869     overscrollPastEnd = std::max(-GetCurrentPosition() - maxScrollExtent, 0.0);
1870     // do not adjust offset if direction opposite from the overScroll direction when out of boundary
1871     if ((overscrollPastStart > 0.0 && offset < 0.0) || (overscrollPastEnd > 0.0 && offset > 0.0)) {
1872         return;
1873     }
1874     easing = (overscrollPastStart > 0.0 && offset > 0.0) || (overscrollPastEnd > 0.0 && offset < 0.0);
1875 
1876     overscrollPast = std::max(overscrollPastStart, overscrollPastEnd);
1877     double friction = easing ? RenderScroll::CalculateFriction((overscrollPast - std::abs(offset)) / viewPortSize)
1878                              : RenderScroll::CalculateFriction(overscrollPast / viewPortSize);
1879     double direction = offset / std::abs(offset);
1880     offset = direction * RenderScroll::CalculateOffsetByFriction(overscrollPast, std::abs(offset), friction);
1881     vertical_ ? delta.SetY(offset) : delta.SetX(offset);
1882 }
1883 
GetCurrentPosition() const1884 double RenderList::GetCurrentPosition() const
1885 {
1886     return currentOffset_;
1887 }
1888 
IsOutOfBoundary() const1889 bool RenderList::IsOutOfBoundary() const
1890 {
1891     return isOutOfBoundary_;
1892 }
1893 
ResetEdgeEffect()1894 void RenderList::ResetEdgeEffect()
1895 {
1896     if (!scrollEffect_) {
1897         LOGE("ResetEdgeEffect failed, scrollEffect_ is nullptr");
1898         return;
1899     }
1900 
1901     scrollEffect_->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() {
1902         auto list = weak.Upgrade();
1903         if (list) {
1904             return list->GetCurrentPosition();
1905         }
1906         return 0.0;
1907     });
1908     scrollEffect_->SetLeadingCallback([weak = AceType::WeakClaim(this)]() {
1909         auto list = weak.Upgrade();
1910         if (list) {
1911             return list->GetMainSize(list->GetLayoutSize()) - list->mainScrollExtent_;
1912         }
1913         return 0.0;
1914     });
1915 
1916     scrollEffect_->SetTrailingCallback([weak = AceType::WeakClaim(this)]() { return 0.0; });
1917     scrollEffect_->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() {
1918         auto list = weak.Upgrade();
1919         if (list) {
1920             return list->GetMainSize(list->GetLayoutSize()) - list->mainScrollExtent_;
1921         }
1922         return 0.0;
1923     });
1924     scrollEffect_->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() { return 0.0; });
1925     scrollEffect_->SetScrollNode(AceType::WeakClaim(this));
1926     SetEdgeEffectAttribute();
1927     scrollEffect_->InitialEdgeEffect();
1928 }
1929 
SetEdgeEffectAttribute()1930 void RenderList::SetEdgeEffectAttribute()
1931 {
1932     if (scrollEffect_ && scrollable_) {
1933         scrollEffect_->SetScrollable(scrollable_);
1934         scrollEffect_->RegisterSpringCallback();
1935         if (scrollEffect_->IsSpringEffect()) {
1936             scrollable_->SetOutBoundaryCallback([weakScroll = AceType::WeakClaim(this)]() {
1937                 auto scroll = weakScroll.Upgrade();
1938                 if (scroll) {
1939                     return scroll->IsOutOfBoundary();
1940                 }
1941                 return false;
1942             });
1943         }
1944     }
1945 }
1946 
CalculateMainScrollExtent(double curMainPos,double mainSize)1947 void RenderList::CalculateMainScrollExtent(double curMainPos, double mainSize)
1948 {
1949     // check current is out of boundary
1950     isOutOfBoundary_ = LessNotEqual(curMainPos, mainSize) || GreatNotEqual(currentOffset_, 0.0);
1951     // content length
1952     mainScrollExtent_ = curMainPos - currentOffset_;
1953     if (GetChildren().empty()) {
1954         return;
1955     }
1956     Size itemSize; // Calculate all children layout size.
1957     for (const auto& child : GetChildren()) {
1958         itemSize += child->GetLayoutSize();
1959     }
1960     auto averageItemHeight = GetMainSize(itemSize) / GetChildren().size() + spaceWidth_;
1961     estimatedHeight_ = averageItemHeight * TotalCount();
1962     lastOffset_ = startIndex_ * averageItemHeight - currentOffset_;
1963     if (startIndex_ == 0) {
1964         startIndexOffset_ = 0.0;
1965     } else if (useEstimateCurrentOffset_) {
1966         useEstimateCurrentOffset_ = false;
1967         startIndexOffset_ = startIndex_ * averageItemHeight;
1968     }
1969     if (scrollBar_) {
1970         scrollBar_->SetScrollable(estimatedHeight_ > GetMainSize(GetLayoutSize()));
1971     }
1972 }
1973 
ProcessDragStart(double startPosition)1974 void RenderList::ProcessDragStart(double startPosition)
1975 {
1976     auto globalMainOffset = GetMainAxis(GetGlobalOffset());
1977     auto localOffset = startPosition - globalMainOffset;
1978     auto index = GetNearChildByPosition(localOffset);
1979     if (index == INVALID_CHILD_INDEX) {
1980         LOGE("GetNearChildByPosition failed, localOffset = %lf not in item index [ %zu, %zu )", localOffset,
1981             startIndex_, startIndex_ + items_.size());
1982         return;
1983     }
1984     dragStartIndexPending_ = index;
1985 }
1986 
ProcessDragUpdate(double dragOffset)1987 void RenderList::ProcessDragUpdate(double dragOffset)
1988 {
1989     if (!chainAnimation_) {
1990         return;
1991     }
1992 
1993     if (NearZero(dragOffset)) {
1994         return;
1995     }
1996 
1997     currentDelta_ = dragOffset;
1998     double delta = FlushChainAnimation();
1999     currentOffset_ += delta;
2000     if (!NearZero(delta)) {
2001         LOGE("ProcessDragUpdate delta = %lf currentOffset_ = %lf", delta, currentOffset_);
2002     }
2003 }
2004 
ProcessScrollOverCallback(double velocity)2005 void RenderList::ProcessScrollOverCallback(double velocity)
2006 {
2007     if (!chainAnimation_) {
2008         LOGD("chain animation is null, no need to handle it.");
2009         return;
2010     }
2011 
2012     if (NearZero(velocity)) {
2013         LOGD("velocity is zero, no need to handle in chain animation.");
2014         return;
2015     }
2016 
2017     if (reachStart_) {
2018         dragStartIndexPending_ = startIndex_;
2019     } else if (reachEnd_) {
2020         dragStartIndexPending_ = startIndex_;
2021         if (!items_.empty()) {
2022             dragStartIndexPending_ += items_.size() - 1;
2023         }
2024     }
2025 
2026     double delta = FlushChainAnimation();
2027     currentOffset_ += delta;
2028     if (!NearZero(delta)) {
2029         LOGE("ProcessScrollOverCallback delta = %lf currentOffset_ = %lf", delta, currentOffset_);
2030     }
2031 }
2032 
InitChainAnimation(int32_t nodeCount)2033 void RenderList::InitChainAnimation(int32_t nodeCount)
2034 {
2035     auto context = GetContext().Upgrade();
2036     if (!context) {
2037         LOGE("Init chain animation failed. context is null");
2038         return;
2039     }
2040 
2041     if (chainAdapter_ && chain_) {
2042         return;
2043     }
2044     chainAdapter_ = AceType::MakeRefPtr<BilateralSpringAdapter>();
2045     chain_ = AceType::MakeRefPtr<SimpleSpringChain>(chainAdapter_);
2046     const auto& property = GetChainProperty();
2047     chain_->SetFrameDelta(property.FrameDelay());
2048     if (property.StiffnessTransfer()) {
2049         chain_->SetStiffnessTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.StiffnessCoefficient()));
2050     } else {
2051         chain_->SetStiffnessTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.StiffnessCoefficient()));
2052     }
2053     if (property.DampingTransfer()) {
2054         chain_->SetDampingTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.DampingCoefficient()));
2055     } else {
2056         chain_->SetDampingTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.DampingCoefficient()));
2057     }
2058     chain_->SetControlDamping(property.ControlDamping());
2059     chain_->SetControlStiffness(property.ControlStiffness());
2060     chain_->SetDecoration(context->NormalizeToPx(property.Interval()));
2061     chain_->SetMinDecoration(context->NormalizeToPx(property.MinInterval()));
2062     chain_->SetMaxDecoration(context->NormalizeToPx(property.MaxInterval()));
2063     for (int32_t index = 0; index < nodeCount; index++) {
2064         auto node = AceType::MakeRefPtr<BilateralSpringNode>(GetContext(), index, 0.0);
2065         WeakPtr<BilateralSpringNode> nodeWeak(node);
2066         WeakPtr<SimpleSpringAdapter> adapterWeak(chainAdapter_);
2067         node->AddUpdateListener(
2068             [weak = AceType::WeakClaim(this), nodeWeak, adapterWeak](double value, double velocity) {
2069                 auto renderList = weak.Upgrade();
2070                 auto node = nodeWeak.Upgrade();
2071                 auto adapter = adapterWeak.Upgrade();
2072                 if (!renderList || !node || !adapter) {
2073                     return;
2074                 }
2075                 renderList->MarkNeedLayout();
2076             });
2077         chainAdapter_->AddNode(node);
2078     }
2079     chainAdapter_->NotifyControlIndexChange();
2080 }
2081 
GetChainDelta(int32_t index) const2082 double RenderList::GetChainDelta(int32_t index) const
2083 {
2084     if (!chainAdapter_) {
2085         return 0.0;
2086     }
2087     double value = 0.0;
2088     RefPtr<BilateralSpringNode> node;
2089     int32_t controlIndex = dragStartIndex_;
2090     int32_t baseIndex = controlIndex - chainAdapter_->GetControlIndex();
2091     auto targetIndex = std::clamp(index - baseIndex, 0, CHAIN_ANIMATION_NODE_COUNT - 1);
2092     node = AceType::DynamicCast<BilateralSpringNode>(chainAdapter_->GetNode(targetIndex));
2093     if (node) {
2094         value = node->GetValue();
2095     }
2096     LOGD("ChainDelta. controlIndex: %{public}d, index: %{public}d, value: %{public}.3lf", controlIndex, index, value);
2097     return value;
2098 }
2099 
GetNearChildByPosition(double mainOffset) const2100 size_t RenderList::GetNearChildByPosition(double mainOffset) const
2101 {
2102     size_t index = startIndex_;
2103     size_t prevIndex = INVALID_CHILD_INDEX;
2104 
2105     for (auto& child : items_) {
2106         auto childMainOffset = GetMainAxis(child->GetPosition());
2107 
2108         if (childMainOffset > mainOffset) {
2109             return prevIndex;
2110         }
2111         prevIndex = index++;
2112     }
2113     return prevIndex;
2114 }
2115 
FlushChainAnimation()2116 double RenderList::FlushChainAnimation()
2117 {
2118     if (!chainAnimation_ || !chain_ || !chainAdapter_) {
2119         return 0.0;
2120     }
2121     double deltaDistance = 0.0;
2122     bool needSetValue = false;
2123     bool overScroll = scrollable_ && scrollable_->IsSpringMotionRunning();
2124     if (chainOverScroll_ != overScroll) {
2125         if (overScroll) {
2126             const auto& springProperty = GetOverSpringProperty();
2127             if (springProperty && springProperty->IsValid()) {
2128                 chain_->SetControlStiffness(springProperty->Stiffness());
2129                 chain_->SetControlDamping(springProperty->Damping());
2130             }
2131         } else {
2132             chain_->SetControlStiffness(GetChainProperty().ControlStiffness());
2133             chain_->SetControlDamping(GetChainProperty().ControlDamping());
2134         }
2135         chain_->OnControlNodeChange();
2136         chainOverScroll_ = overScroll;
2137     }
2138     chain_->FlushAnimation();
2139     if (dragStartIndexPending_ != dragStartIndex_) {
2140         deltaDistance = chainAdapter_->ResetControl(dragStartIndexPending_ - dragStartIndex_);
2141         LOGD("Switch chain control node. %{public}zu -> %{public}zu, deltaDistance: %{public}.1f", dragStartIndex_,
2142             dragStartIndexPending_, deltaDistance);
2143         dragStartIndex_ = dragStartIndexPending_;
2144         chainAdapter_->SetDeltaValue(-deltaDistance);
2145         needSetValue = true;
2146     }
2147     if (!NearZero(currentDelta_)) {
2148         LOGD("Set delta chain value. delta: %{public}.1f", currentDelta_);
2149         chainAdapter_->SetDeltaValue(currentDelta_);
2150         currentDelta_ = 0.0;
2151         needSetValue = true;
2152     }
2153     if (needSetValue) {
2154         chain_->SetValue(0.0);
2155         LOGD("FlushChainAnimation: %{public}s", chainAdapter_->DumpNodes().c_str());
2156     }
2157     return deltaDistance;
2158 }
2159 
UpdateAccessibilityAttr()2160 void RenderList::UpdateAccessibilityAttr()
2161 {
2162     if (!component_) {
2163         LOGE("RenderList: component is null.");
2164         return;
2165     }
2166 
2167     auto accessibilityNode = GetAccessibilityNode().Upgrade();
2168     if (!accessibilityNode) {
2169         LOGD("RenderList: current accessibilityNode is null.");
2170         return;
2171     }
2172 
2173     auto collectionInfo = accessibilityNode->GetCollectionInfo();
2174     size_t count = TotalCount() > 0 ? TotalCount() : 1;
2175     if (vertical_) {
2176         collectionInfo.rows = static_cast<int32_t>(count);
2177         collectionInfo.columns = 1;
2178     } else {
2179         collectionInfo.rows = 1;
2180         collectionInfo.columns = static_cast<int32_t>(count);
2181     }
2182     accessibilityNode->SetCollectionInfo(collectionInfo);
2183     accessibilityNode->SetScrollableState(true);
2184     accessibilityNode->SetActionScrollForward([weakList = AceType::WeakClaim(this)]() {
2185         auto list = weakList.Upgrade();
2186         if (list) {
2187             LOGI("Trigger ScrollForward by Accessibility.");
2188             return list->HandleActionScroll(true);
2189         }
2190         return false;
2191     });
2192     accessibilityNode->SetActionScrollBackward([weakList = AceType::WeakClaim(this)]() {
2193         auto list = weakList.Upgrade();
2194         if (list) {
2195             LOGI("Trigger ScrollBackward by Accessibility.");
2196             return list->HandleActionScroll(false);
2197         }
2198         return false;
2199     });
2200 
2201     accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
2202     accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_BACKWARD);
2203 
2204     scrollFinishEventBack_ = [weakList = AceType::WeakClaim(this)] {
2205         auto list = weakList.Upgrade();
2206         if (list) {
2207             list->ModifyActionScroll();
2208         }
2209     };
2210 }
2211 
UpdateAccessibilityScrollAttr()2212 void RenderList::UpdateAccessibilityScrollAttr()
2213 {
2214     auto accessibilityNode = GetAccessibilityNode().Upgrade();
2215     if (accessibilityNode) {
2216         accessibilityNode->SetListBeginIndex(firstDisplayIndex_);
2217         accessibilityNode->SetListEndIndex(lastDisplayIndex_);
2218         accessibilityNode->SetListItemCounts(items_.size());
2219     }
2220 }
2221 
UpdateAccessibilityVisible()2222 void RenderList::UpdateAccessibilityVisible()
2223 {
2224     auto accessibilityNode = GetAccessibilityNode().Upgrade();
2225     if (!accessibilityNode) {
2226         return;
2227     }
2228     Offset globalOffset = GetGlobalOffset();
2229     Rect listItemRect;
2230     Rect viewPortRect = Rect(globalOffset, GetLayoutSize());
2231     for (const auto& listItem : items_) {
2232         if (!listItem || listItem->GetChildren().empty()) {
2233             continue;
2234         }
2235         // RenderListItem's accessibility node is List's in v2, see ViewStackProcessor::WrapComponents() and
2236         // RenderElement::SetAccessibilityNode
2237         auto listItemWithAccessibilityNode = listItem->GetFirstChild();
2238         auto node = listItemWithAccessibilityNode->GetAccessibilityNode().Upgrade();
2239         if (!node) {
2240             continue;
2241         }
2242         bool visible = GetVisible();
2243         if (visible) {
2244             listItemRect.SetSize(listItem->GetLayoutSize());
2245             listItemRect.SetOffset(globalOffset + listItem->GetPosition());
2246             visible = listItemRect.IsIntersectWith(viewPortRect);
2247         }
2248         listItemWithAccessibilityNode->SetAccessibilityVisible(visible);
2249         if (visible) {
2250             Rect clampRect = listItemRect.Constrain(viewPortRect);
2251             listItemWithAccessibilityNode->SetAccessibilityRect(clampRect);
2252         } else {
2253             listItem->NotifyPaintFinish();
2254         }
2255     }
2256 }
2257 
ActionByScroll(bool forward,ScrollEventBack scrollEventBack)2258 bool RenderList::ActionByScroll(bool forward, ScrollEventBack scrollEventBack)
2259 {
2260     LOGI("Handle action by Scroll.");
2261     auto node = GetParent().Upgrade();
2262     while (node) {
2263         auto scroll = AceType::DynamicCast<RenderSingleChildScroll>(node);
2264         if (!scroll) {
2265             node = node->GetParent().Upgrade();
2266             continue;
2267         }
2268 
2269         scroll->ScrollPage(!forward, true, scrollEventBack);
2270         return true;
2271     }
2272     return false;
2273 }
2274 
HandleActionScroll(bool forward)2275 bool RenderList::HandleActionScroll(bool forward)
2276 {
2277     if (isActionByScroll_) {
2278         return ActionByScroll(forward, scrollFinishEventBack_);
2279     }
2280 
2281     if (forward) {
2282         JumpToIndex(lastDisplayIndex_, DEFAULT_SOURCE);
2283     } else {
2284         JumpToIndex(startIndex_, DEFAULT_SOURCE);
2285     }
2286     if (scrollFinishEventBack_) {
2287         scrollFinishEventBack_();
2288     }
2289     return true;
2290 }
2291 
ModifyActionScroll()2292 void RenderList::ModifyActionScroll()
2293 {
2294     hasActionScroll_ = true;
2295 }
2296 
OnPaintFinish()2297 void RenderList::OnPaintFinish()
2298 {
2299     if (!hasActionScroll_) {
2300         return;
2301     }
2302 
2303     hasActionScroll_ = false;
2304     auto context = context_.Upgrade();
2305     if (!context) {
2306         LOGE("RenderList: context is null.");
2307         return;
2308     }
2309 
2310     AccessibilityEvent scrollEvent;
2311     scrollEvent.nodeId = GetAccessibilityNodeId();
2312     scrollEvent.eventType = "scrollend";
2313     context->SendEventToAccessibility(scrollEvent);
2314 }
2315 
IsUseOnly()2316 bool RenderList::IsUseOnly()
2317 {
2318     return true;
2319 }
2320 
PrepareRawRecognizer()2321 bool RenderList::PrepareRawRecognizer()
2322 {
2323     if (rawRecognizer_) {
2324         return true;
2325     }
2326 
2327     rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
2328     auto weak = AceType::WeakClaim(this);
2329     rawRecognizer_->SetOnTouchDown([weak](const TouchEventInfo& info) {
2330         if (info.GetTouches().empty()) {
2331             return;
2332         }
2333         auto spThis = weak.Upgrade();
2334         if (spThis) {
2335             spThis->lastPos_ = spThis->GetMainAxis(info.GetTouches().front().GetLocalLocation());
2336         }
2337     });
2338     rawRecognizer_->SetOnTouchMove([weak](const TouchEventInfo& info) {
2339         if (info.GetTouches().empty()) {
2340             return;
2341         }
2342 
2343         auto spThis = weak.Upgrade();
2344         if (!spThis || !spThis->selectedItem_) {
2345             return;
2346         }
2347         double currentPos = spThis->GetMainAxis(info.GetTouches().front().GetLocalLocation());
2348         spThis->OnSelectedItemMove(currentPos);
2349     });
2350     rawRecognizer_->SetOnTouchUp([weak](const TouchEventInfo& info) {
2351         auto spThis = weak.Upgrade();
2352         if (spThis) {
2353             spThis->OnSelectedItemStopMoving(false);
2354         }
2355     });
2356     rawRecognizer_->SetOnTouchCancel([weak](const TouchEventInfo& info) {
2357         auto spThis = weak.Upgrade();
2358         if (spThis) {
2359             spThis->OnSelectedItemStopMoving(true);
2360         }
2361     });
2362 
2363     return true;
2364 }
2365 
OnSelectedItemMove(double position)2366 void RenderList::OnSelectedItemMove(double position)
2367 {
2368     double deltaPos = position - lastPos_;
2369 
2370     movingForward_ = LessOrEqual(deltaPos, 0.0);
2371     selectedItemMainAxis_ += deltaPos;
2372     deltaPos = -deltaPos;
2373     if (LessOrEqual(selectedItemMainAxis_, 0.0)) {
2374         selectedItemMainAxis_ = 0.0;
2375     } else {
2376         double maxMainSize = GetMainSize(GetLayoutSize());
2377         double mainSize = GetMainSize(selectedItem_->GetLayoutSize());
2378         if (GreatOrEqual(selectedItemMainAxis_ + mainSize, maxMainSize)) {
2379             selectedItemMainAxis_ = maxMainSize - mainSize;
2380         } else {
2381             deltaPos = 0.0;
2382             lastPos_ = position;
2383         }
2384     }
2385 
2386     if (!NearZero(deltaPos)) {
2387         currentOffset_ += deltaPos;
2388         autoScrollingForItemMove_ = true;
2389     }
2390 
2391     MarkNeedLayout();
2392 }
2393 
OnSelectedItemStopMoving(bool canceled)2394 void RenderList::OnSelectedItemStopMoving(bool canceled)
2395 {
2396     if (!canceled && targetIndex_ != selectedItemIndex_) {
2397         auto from = static_cast<int32_t>(selectedItemIndex_);
2398         auto to = static_cast<int32_t>(targetIndex_);
2399         LOGI("Moving item from %{private}d to %{private}d", from, to);
2400         if (!ResumeEventCallback(component_, &ListComponent::GetOnItemMove, false, from, to)) {
2401             LOGI("User canceled, stop moving item");
2402         }
2403     }
2404 
2405     if (selectedItemIndex_ < startIndex_ || selectedItemIndex_ >= startIndex_ + items_.size()) {
2406         RecycleListItem(selectedItemIndex_);
2407     }
2408 
2409     targetIndex_ = INITIAL_CHILD_INDEX;
2410     selectedItemIndex_ = INITIAL_CHILD_INDEX;
2411     selectedItem_ = nullptr;
2412     MarkNeedLayout();
2413 }
2414 
CreateDragDropRecognizer()2415 void RenderList::CreateDragDropRecognizer()
2416 {
2417     if (dragDropGesture_) {
2418         return;
2419     }
2420 
2421     auto longPressRecognizer =
2422         AceType::MakeRefPtr<OHOS::Ace::LongPressRecognizer>(context_, DEFAULT_DURATION, DEFAULT_FINGERS, false);
2423     longPressRecognizer->SetOnAction([weakRenderList = AceType::WeakClaim(this)](const GestureEvent& info) {
2424         auto renderList = weakRenderList.Upgrade();
2425         if (!renderList) {
2426             LOGE("LongPress action RenderList is null.");
2427             return;
2428         }
2429         renderList->scrollable_->MarkAvailable(false);
2430     });
2431     PanDirection panDirection;
2432     auto panRecognizer =
2433         AceType::MakeRefPtr<OHOS::Ace::PanRecognizer>(context_, DEFAULT_FINGERS, panDirection, DEFAULT_DISTANCE);
2434     panRecognizer->SetOnActionStart([weakRenderList = AceType::WeakClaim(this), context = context_,
2435                                         onItemDragStart = onItemDragStart_](const GestureEvent& info) {
2436         if (onItemDragStart) {
2437             auto pipelineContext = context.Upgrade();
2438             if (!pipelineContext) {
2439                 LOGE("Context is null.");
2440                 return;
2441             }
2442 
2443             auto renderList = weakRenderList.Upgrade();
2444             if (!renderList) {
2445                 LOGE("RenderList is null.");
2446                 return;
2447             }
2448 
2449             ItemDragInfo dragInfo;
2450             dragInfo.SetX(info.GetGlobalPoint().GetX());
2451             dragInfo.SetY(info.GetGlobalPoint().GetY());
2452 
2453             Point point = info.GetGlobalPoint() - renderList->GetGlobalOffset();
2454             auto listItem = renderList->FindCurrentListItem(point);
2455             if (!listItem) {
2456                 LOGW("There is no listitem at the point.");
2457                 return;
2458             }
2459 
2460             if (!listItem->IsMovable()) {
2461                 LOGI("This list item is not movable.");
2462                 return;
2463             }
2464             renderList->selectedDragItem_ = listItem;
2465             renderList->selectedItemIndex_ = renderList->GetIndexByListItem(listItem);
2466             renderList->selectedDragItem_->SetHidden(true);
2467             renderList->MarkNeedLayout();
2468 
2469             auto customComponent =
2470                 DynamicCast<Component>(onItemDragStart(dragInfo, int32_t(renderList->selectedItemIndex_)));
2471             if (!customComponent) {
2472                 LOGE("Custom component is null.");
2473                 return;
2474             }
2475             auto stackElement = pipelineContext->GetLastStack();
2476             auto positionedComponent = AceType::MakeRefPtr<PositionedComponent>(customComponent);
2477             positionedComponent->SetTop(Dimension(listItem->GetGlobalOffset().GetY()));
2478             positionedComponent->SetLeft(Dimension(listItem->GetGlobalOffset().GetX()));
2479             renderList->SetBetweenItemAndBuilder(
2480                 Offset(info.GetGlobalPoint().GetX() - listItem->GetGlobalOffset().GetX(),
2481                     info.GetGlobalPoint().GetY() - listItem->GetGlobalOffset().GetY()));
2482 
2483             auto updatePosition = [weak = weakRenderList](
2484                                       const std::function<void(const Dimension&, const Dimension&)>& func) {
2485                 auto renderList = weak.Upgrade();
2486                 if (!renderList) {
2487                     return;
2488                 }
2489                 renderList->SetUpdateBuilderFuncId(func);
2490             };
2491 
2492             positionedComponent->SetUpdatePositionFuncId(updatePosition);
2493             stackElement->PushComponent(positionedComponent);
2494             renderList->hasDragItem_ = true;
2495         }
2496     });
2497     panRecognizer->SetOnActionUpdate(
2498         [weakRenderList = AceType::WeakClaim(this), context = context_](const GestureEvent& info) {
2499             auto pipelineContext = context.Upgrade();
2500             if (!pipelineContext) {
2501                 LOGE("Context is null.");
2502                 return;
2503             }
2504 
2505             auto renderList = weakRenderList.Upgrade();
2506             if (!renderList) {
2507                 LOGE("RenderList is null.");
2508                 return;
2509             }
2510 
2511             ItemDragInfo dragInfo;
2512             dragInfo.SetX(info.GetGlobalPoint().GetX());
2513             dragInfo.SetY(info.GetGlobalPoint().GetY());
2514 
2515             Point point = info.GetGlobalPoint() - renderList->GetBetweenItemAndBuilder();
2516             if (renderList->GetUpdateBuilderFuncId()) {
2517                 renderList->GetUpdateBuilderFuncId()(Dimension(point.GetX()), Dimension(point.GetY()));
2518             }
2519 
2520             auto targetRenderlist = renderList->FindTargetRenderNode<V2::RenderList>(pipelineContext, info);
2521             auto preTargetRenderlist = renderList->GetPreTargetRenderList();
2522 
2523             if (preTargetRenderlist == targetRenderlist) {
2524                 if (targetRenderlist && targetRenderlist->GetOnItemDragMove()) {
2525                     Point point = info.GetGlobalPoint() - targetRenderlist->GetGlobalOffset();
2526                     auto newListItem = targetRenderlist->FindCurrentListItem(point);
2527                     if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(newListItem)) > -1) {
2528                         renderList->insertItemIndex_ = targetRenderlist->GetIndexByListItem(newListItem);
2529                     }
2530                     if (targetRenderlist == renderList) {
2531                         (targetRenderlist->GetOnItemDragMove())(dragInfo,
2532                             static_cast<int32_t>(renderList->selectedItemIndex_), renderList->insertItemIndex_);
2533                     } else {
2534                         (targetRenderlist->GetOnItemDragMove())(dragInfo, -1, renderList->insertItemIndex_);
2535                     }
2536                 }
2537                 return;
2538             }
2539             if (preTargetRenderlist) {
2540                 if (preTargetRenderlist->GetOnItemDragLeave()) {
2541                     (preTargetRenderlist->GetOnItemDragLeave())(
2542                         dragInfo, static_cast<int32_t>(renderList->selectedItemIndex_));
2543                 }
2544             }
2545             if (targetRenderlist) {
2546                 if (targetRenderlist->GetOnItemDragEnter()) {
2547                     (targetRenderlist->GetOnItemDragEnter())(dragInfo);
2548                 }
2549             }
2550             renderList->SetPreTargetRenderList(targetRenderlist);
2551         });
2552     panRecognizer->SetOnActionEnd([weakRenderList = AceType::WeakClaim(this), context = context_](
2553                                       const GestureEvent& info) {
2554         auto pipelineContext = context.Upgrade();
2555         if (!pipelineContext) {
2556             LOGE("Context is null.");
2557             return;
2558         }
2559 
2560         auto renderList = weakRenderList.Upgrade();
2561         if (!renderList) {
2562             LOGE("RenderList is null.");
2563             return;
2564         }
2565         if (!renderList->selectedDragItem_ || !renderList->selectedDragItem_->IsMovable()) {
2566             return;
2567         }
2568 
2569         ItemDragInfo dragInfo;
2570         dragInfo.SetX(info.GetGlobalPoint().GetX());
2571         dragInfo.SetY(info.GetGlobalPoint().GetY());
2572         if (renderList->hasDragItem_) {
2573             auto stackElement = pipelineContext->GetLastStack();
2574             stackElement->PopComponent();
2575             renderList->hasDragItem_ = false;
2576         }
2577 
2578         ACE_DCHECK(renderList->GetPreTargetRenderList() ==
2579                    renderList->FindTargetRenderNode<V2::RenderList>(pipelineContext, info));
2580         auto targetRenderlist = renderList->GetPreTargetRenderList();
2581 
2582         if (!targetRenderlist) {
2583             (renderList->GetOnItemDrop())(dragInfo, static_cast<int32_t>(renderList->selectedItemIndex_), -1, true);
2584             renderList->SetPreTargetRenderList(nullptr);
2585             renderList->selectedDragItem_->SetHidden(false);
2586             renderList->MarkNeedLayout();
2587             return;
2588         }
2589 
2590         renderList->selectedDragItem_->SetHidden(false);
2591         if (targetRenderlist->GetOnItemDrop()) {
2592             Point point = info.GetGlobalPoint() - targetRenderlist->GetGlobalOffset();
2593             auto newListItem = targetRenderlist->FindCurrentListItem(point);
2594             if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(newListItem)) > -1) {
2595                 renderList->insertItemIndex_ = static_cast<size_t>(targetRenderlist->GetIndexByListItem(newListItem));
2596             }
2597             if (targetRenderlist == renderList) {
2598                 int32_t from = static_cast<int32_t>(renderList->selectedItemIndex_);
2599                 int32_t to = static_cast<int32_t>(renderList->insertItemIndex_);
2600                 auto moveRes =
2601                     ResumeEventCallback(renderList->component_, &ListComponent::GetOnItemMove, true, from, to);
2602                 (targetRenderlist->GetOnItemDrop())(dragInfo, from, to, moveRes);
2603                 renderList->MarkNeedLayout();
2604             } else {
2605                 (targetRenderlist->GetOnItemDrop())(dragInfo, -1, renderList->insertItemIndex_, true);
2606                 targetRenderlist->MarkNeedLayout();
2607             }
2608         }
2609         renderList->SetPreTargetRenderList(nullptr);
2610         renderList->scrollable_->MarkAvailable(true);
2611     });
2612     panRecognizer->SetOnActionCancel([weakRenderList = AceType::WeakClaim(this), context = context_]() {
2613         auto pipelineContext = context.Upgrade();
2614         if (!pipelineContext) {
2615             LOGE("Context is null.");
2616             return;
2617         }
2618 
2619         auto renderList = weakRenderList.Upgrade();
2620         if (!renderList) {
2621             LOGE("RenderList is null.");
2622             return;
2623         }
2624         if (!renderList->selectedDragItem_ || !renderList->selectedDragItem_->IsMovable()) {
2625             return;
2626         }
2627 
2628         if (renderList->hasDragItem_) {
2629             auto stackElement = pipelineContext->GetLastStack();
2630             stackElement->PopComponent();
2631             renderList->hasDragItem_ = false;
2632         }
2633 
2634         renderList->SetPreTargetRenderList(nullptr);
2635         renderList->selectedDragItem_->SetHidden(false);
2636         renderList->scrollable_->MarkAvailable(true);
2637         renderList->MarkNeedLayout();
2638     });
2639     std::vector<RefPtr<GestureRecognizer>> recognizers { longPressRecognizer, panRecognizer };
2640     dragDropGesture_ = AceType::MakeRefPtr<OHOS::Ace::SequencedRecognizer>(GetContext(), recognizers);
2641 }
2642 
FindCurrentListItem(const Point & point)2643 RefPtr<RenderListItem> RenderList::FindCurrentListItem(const Point& point)
2644 {
2645     const auto& children = GetChildren();
2646     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
2647         auto& child = *iter;
2648         for (auto& rect : child->GetTouchRectList()) {
2649             if (rect.IsInRegion(point)) {
2650                 return AceType::DynamicCast<RenderListItem>(child);
2651             }
2652         }
2653     }
2654     return nullptr;
2655 }
2656 
CalculateSelectedIndex(const RefPtr<RenderList> targetRenderlist,const GestureEvent & info,Size & selectedItemSize)2657 size_t RenderList::CalculateSelectedIndex(
2658     const RefPtr<RenderList> targetRenderlist, const GestureEvent& info, Size& selectedItemSize)
2659 {
2660     auto listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), info);
2661     if (listItem) {
2662         selectedItemSize = listItem->GetLayoutSize();
2663         return targetRenderlist->GetIndexByListItem(listItem);
2664     }
2665 
2666     return DEFAULT_INDEX;
2667 }
2668 
CalculateInsertIndex(const RefPtr<RenderList> targetRenderlist,const GestureEvent & info,Size selectedItemSize)2669 int32_t RenderList::CalculateInsertIndex(
2670     const RefPtr<RenderList> targetRenderlist, const GestureEvent& info, Size selectedItemSize)
2671 {
2672     if (targetRenderlist->TotalCount() == 0) {
2673         return 0;
2674     }
2675 
2676     auto listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), info);
2677     if (!listItem) {
2678         GestureEvent newEvent = info;
2679         while (!listItem) {
2680             if (FindTargetRenderNode<V2::RenderList>(context_.Upgrade(), newEvent) != targetRenderlist) {
2681                 break;
2682             }
2683             double newX = vertical_ ? newEvent.GetGlobalPoint().GetX()
2684                                     : newEvent.GetGlobalPoint().GetX() - selectedItemSize.Width();
2685             double newY = vertical_ ? newEvent.GetGlobalPoint().GetY() - selectedItemSize.Height()
2686                                     : newEvent.GetGlobalPoint().GetY();
2687             newEvent.SetGlobalPoint(Point(newX, newY));
2688             listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), newEvent);
2689         }
2690         if (!listItem) {
2691             return 0;
2692         }
2693         if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) > -1) {
2694             return static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) + 1;
2695         }
2696         return DEFAULT_INDEX_VALUE;
2697     }
2698 
2699     if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) > -1) {
2700         return static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem));
2701     }
2702 
2703     return DEFAULT_INDEX_VALUE;
2704 }
2705 
IsAxisScrollable(AxisDirection direction)2706 bool RenderList::IsAxisScrollable(AxisDirection direction)
2707 {
2708     return (((AxisEvent::IsDirectionUp(direction) || AxisEvent::IsDirectionLeft(direction)) && !reachStart_) ||
2709             ((AxisEvent::IsDirectionDown(direction) || AxisEvent::IsDirectionRight(direction)) && !reachEnd_));
2710 }
2711 
HandleAxisEvent(const AxisEvent & event)2712 void RenderList::HandleAxisEvent(const AxisEvent& event) {}
2713 
HandleMouseEvent(const MouseEvent & event)2714 bool RenderList::HandleMouseEvent(const MouseEvent& event)
2715 {
2716     if (!isMultiSelectable_) {
2717         return false;
2718     }
2719     scrollable_->MarkAvailable(false);
2720 
2721     if (event.button == MouseButton::LEFT_BUTTON) {
2722         if (event.action == MouseAction::PRESS) {
2723             Point mousePoint(event.GetOffset().GetX(), event.GetOffset().GetY());
2724             auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2725             if (listItem && listItem->IsDragStart()) {
2726                 forbidMultiSelect_ = true;
2727             }
2728         } else if (event.action == MouseAction::RELEASE) {
2729             forbidMultiSelect_ = false;
2730         }
2731     }
2732 
2733     if (forbidMultiSelect_) {
2734         return false;
2735     }
2736 
2737     if (event.action == MouseAction::HOVER_EXIT) {
2738         mouseIsHover_ = false;
2739     } else {
2740         mouseIsHover_ = true;
2741     }
2742 
2743     auto context = context_.Upgrade();
2744     if (context) {
2745         context->SubscribeCtrlA([wp = AceType::WeakClaim(this)]() {
2746             auto sp = wp.Upgrade();
2747             if (sp) {
2748                 if (sp->mouseIsHover_ == true) {
2749                     sp->MultiSelectAllWhenCtrlA();
2750                 } else {
2751                     sp->ClearMultiSelect();
2752                     sp->MarkNeedRender();
2753                 }
2754             }
2755         });
2756     } else {
2757         LOGE("context is null");
2758         return false;
2759     }
2760 
2761     if (context->IsCtrlDown()) {
2762         if (context->IsKeyboardA()) {
2763             MultiSelectAllWhenCtrlA();
2764             return true;
2765         }
2766         HandleMouseEventWhenCtrlDown(event);
2767         return true;
2768     }
2769     selectedItemsWithCtrl_.clear();
2770 
2771     if (context->IsShiftDown()) {
2772         HandleMouseEventWhenShiftDown(event);
2773         return true;
2774     }
2775     firstItemWithShift_ = nullptr;
2776 
2777     HandleMouseEventWithoutKeyboard(event);
2778     return true;
2779 }
2780 
ClearMultiSelect()2781 void RenderList::ClearMultiSelect()
2782 {
2783     for (const auto& listItem : items_) {
2784         if (!listItem) {
2785             continue;
2786         }
2787         listItem->MarkIsSelected(false);
2788     }
2789 }
2790 
MultiSelectWithoutKeyboard(const Rect & selectedZone)2791 void RenderList::MultiSelectWithoutKeyboard(const Rect& selectedZone)
2792 {
2793     if (!selectedZone.IsValid()) {
2794         Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2795         auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2796         if (!listItem) {
2797             return;
2798         }
2799         if (!listItem->GetSelectable()) {
2800             return;
2801         }
2802         listItem->MarkIsSelected(true);
2803         if (listItem->GetOnSelectId()) {
2804             (listItem->GetOnSelectId())(listItem->IsSelected());
2805         }
2806         return;
2807     }
2808 
2809     for (const auto& listItem : items_) {
2810         if (!listItem) {
2811             continue;
2812         }
2813         if (!listItem->GetSelectable()) {
2814             continue;
2815         }
2816         if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2817             listItem->MarkIsSelected(false);
2818             if (listItem->GetOnSelectId()) {
2819                 (listItem->GetOnSelectId())(listItem->IsSelected());
2820             }
2821             continue;
2822         }
2823         listItem->MarkIsSelected(true);
2824         if (listItem->GetOnSelectId()) {
2825             (listItem->GetOnSelectId())(listItem->IsSelected());
2826         }
2827     }
2828 }
2829 
HandleMouseEventWithoutKeyboard(const MouseEvent & event)2830 void RenderList::HandleMouseEventWithoutKeyboard(const MouseEvent& event)
2831 {
2832     if (event.button == MouseButton::LEFT_BUTTON) {
2833         if (event.action == MouseAction::PRESS) {
2834             ClearMultiSelect();
2835             mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2836             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2837             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2838             MultiSelectWithoutKeyboard(selectedZone);
2839             MarkNeedRender();
2840         } else if (event.action == MouseAction::MOVE) {
2841             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2842             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2843             MultiSelectWithoutKeyboard(selectedZone);
2844             MarkNeedRender();
2845         } else if (event.action == MouseAction::RELEASE) {
2846             mouseStartOffset_ = Offset(0.0, 0.0);
2847             mouseEndOffset_ = Offset(0.0, 0.0);
2848             MarkNeedRender();
2849         }
2850     }
2851 }
2852 
GetPressItemWhenShiftDown(const Rect & selectedZone)2853 RefPtr<RenderListItem> RenderList::GetPressItemWhenShiftDown(const Rect& selectedZone)
2854 {
2855     if (!selectedZone.IsValid()) {
2856         Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2857         auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2858         if (!listItem) {
2859             return nullptr;
2860         }
2861         if (!listItem->GetSelectable()) {
2862             return nullptr;
2863         }
2864         return listItem;
2865     }
2866     return nullptr;
2867 }
2868 
MultiSelectWhenShiftDown(const Rect & selectedZone)2869 void RenderList::MultiSelectWhenShiftDown(const Rect& selectedZone)
2870 {
2871     for (const auto& listItem : items_) {
2872         if (!listItem) {
2873             continue;
2874         }
2875         if (!listItem->GetSelectable()) {
2876             continue;
2877         }
2878         if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2879             continue;
2880         }
2881         listItem->MarkIsSelected(true);
2882         if (listItem->GetOnSelectId()) {
2883             (listItem->GetOnSelectId())(listItem->IsSelected());
2884         }
2885     }
2886 }
2887 
HandleMouseEventWhenShiftDown(const MouseEvent & event)2888 void RenderList::HandleMouseEventWhenShiftDown(const MouseEvent& event)
2889 {
2890     if (event.button == MouseButton::LEFT_BUTTON) {
2891         if (event.action == MouseAction::PRESS) {
2892             mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2893             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2894             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2895             if (firstItemWithShift_ == nullptr) {
2896                 firstItemWithShift_ = GetPressItemWhenShiftDown(selectedZone);
2897             }
2898             secondItemWithShift_ = GetPressItemWhenShiftDown(selectedZone);
2899             MultiSelectAllInRange(firstItemWithShift_, secondItemWithShift_);
2900             MarkNeedRender();
2901         } else if (event.action == MouseAction::MOVE) {
2902             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2903             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2904             MultiSelectWhenShiftDown(selectedZone);
2905             MarkNeedRender();
2906         } else if (event.action == MouseAction::RELEASE) {
2907             mouseStartOffset_ = Offset(0.0, 0.0);
2908             mouseEndOffset_ = Offset(0.0, 0.0);
2909             MarkNeedRender();
2910         }
2911     }
2912 }
2913 
MultiSelectAllInRange(const RefPtr<RenderListItem> & firstItem,const RefPtr<RenderListItem> & secondItem)2914 void RenderList::MultiSelectAllInRange(
2915     const RefPtr<RenderListItem>& firstItem, const RefPtr<RenderListItem>& secondItem)
2916 {
2917     ClearMultiSelect();
2918     if (!firstItem) {
2919         return;
2920     }
2921 
2922     if (!secondItem) {
2923         firstItem->MarkIsSelected(true);
2924         if (firstItem->GetOnSelectId()) {
2925             (firstItem->GetOnSelectId())(firstItem->IsSelected());
2926         }
2927         return;
2928     }
2929 
2930     auto fromItemIndex = std::min(GetIndexByListItem(firstItem), GetIndexByListItem(secondItem));
2931     auto toItemIndex = std::max(GetIndexByListItem(firstItem), GetIndexByListItem(secondItem));
2932 
2933     for (const auto& listItem : items_) {
2934         if (!listItem) {
2935             continue;
2936         }
2937         if (!listItem->GetSelectable()) {
2938             continue;
2939         }
2940 
2941         auto nowIndex = GetIndexByListItem(listItem);
2942         if (nowIndex <= toItemIndex && nowIndex >= fromItemIndex) {
2943             listItem->MarkIsSelected(true);
2944             if (listItem->GetOnSelectId()) {
2945                 (listItem->GetOnSelectId())(listItem->IsSelected());
2946             }
2947         }
2948     }
2949 }
2950 
MultiSelectWhenCtrlDown(const Rect & selectedZone)2951 void RenderList::MultiSelectWhenCtrlDown(const Rect& selectedZone)
2952 {
2953     if (!selectedZone.IsValid()) {
2954         Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2955         auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2956         if (!listItem) {
2957             return;
2958         }
2959         if (!listItem->GetSelectable()) {
2960             return;
2961         }
2962 
2963         if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2964             listItem->MarkIsSelected(false);
2965         } else {
2966             listItem->MarkIsSelected(true);
2967         }
2968 
2969         if (listItem->GetOnSelectId()) {
2970             (listItem->GetOnSelectId())(listItem->IsSelected());
2971         }
2972         return;
2973     }
2974 
2975     for (const auto& listItem : items_) {
2976         if (!listItem) {
2977             continue;
2978         }
2979         if (!listItem->GetSelectable()) {
2980             continue;
2981         }
2982         if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2983             if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2984                 listItem->MarkIsSelected(true);
2985             } else {
2986                 listItem->MarkIsSelected(false);
2987             }
2988             if (listItem->GetOnSelectId()) {
2989                 (listItem->GetOnSelectId())(listItem->IsSelected());
2990             }
2991             continue;
2992         }
2993 
2994         if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2995             listItem->MarkIsSelected(false);
2996         } else {
2997             listItem->MarkIsSelected(true);
2998         }
2999 
3000         if (listItem->GetOnSelectId()) {
3001             (listItem->GetOnSelectId())(listItem->IsSelected());
3002         }
3003     }
3004 }
3005 
HandleMouseEventWhenCtrlDown(const MouseEvent & event)3006 void RenderList::HandleMouseEventWhenCtrlDown(const MouseEvent& event)
3007 {
3008     if (event.button == MouseButton::LEFT_BUTTON) {
3009         if (event.action == MouseAction::PRESS) {
3010             mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3011             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3012             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
3013             MultiSelectWhenCtrlDown(selectedZone);
3014             MarkNeedRender();
3015         } else if (event.action == MouseAction::MOVE) {
3016             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3017             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
3018             MultiSelectWhenCtrlDown(selectedZone);
3019             MarkNeedRender();
3020         } else if (event.action == MouseAction::RELEASE) {
3021             mouseStartOffset_ = Offset(0.0, 0.0);
3022             mouseEndOffset_ = Offset(0.0, 0.0);
3023             MarkNeedRender();
3024             CollectSelectedItems();
3025         }
3026     }
3027 }
3028 
CollectSelectedItems()3029 void RenderList::CollectSelectedItems()
3030 {
3031     selectedItemsWithCtrl_.clear();
3032     for (const auto& listItem : items_) {
3033         if (!listItem) {
3034             continue;
3035         }
3036         if (!listItem->GetSelectable()) {
3037             continue;
3038         }
3039         if (listItem->IsSelected()) {
3040             selectedItemsWithCtrl_.insert(listItem);
3041         }
3042     }
3043 }
3044 
MultiSelectAllWhenCtrlA()3045 void RenderList::MultiSelectAllWhenCtrlA()
3046 {
3047     for (const auto& listItem : items_) {
3048         if (!listItem) {
3049             continue;
3050         }
3051         if (!listItem->GetSelectable()) {
3052             continue;
3053         }
3054         listItem->MarkIsSelected(true);
3055         if (listItem->GetOnSelectId()) {
3056             (listItem->GetOnSelectId())(listItem->IsSelected());
3057         }
3058     }
3059     MarkNeedRender();
3060 }
3061 
RequestNextFocus(bool vertical,bool reverse)3062 int32_t RenderList::RequestNextFocus(bool vertical, bool reverse)
3063 {
3064     bool rightToLeft_ = false;
3065     int32_t moveStep = DIRECTION_MAP.at(rightToLeft_).at(vertical_).at(vertical).at(reverse);
3066     if (moveStep == STEP_INVALID) {
3067         return -1;
3068     }
3069     focusIndex_ += moveStep;
3070     return focusIndex_;
3071 }
3072 
ProvideRestoreInfo()3073 std::string RenderList::ProvideRestoreInfo()
3074 {
3075     if (firstDisplayIndex_ > 0) {
3076         return std::to_string(firstDisplayIndex_);
3077     }
3078     return "";
3079 }
3080 
ApplyRestoreInfo()3081 void RenderList::ApplyRestoreInfo()
3082 {
3083     if (GetRestoreInfo().empty()) {
3084         return;
3085     }
3086     startIndex_ = static_cast<size_t>(StringUtils::StringToInt(GetRestoreInfo()));
3087     SetRestoreInfo("");
3088 }
3089 
LayoutChild(RefPtr<RenderNode> child,double referencePos,bool forward)3090 void RenderList::LayoutChild(RefPtr<RenderNode> child, double referencePos, bool forward)
3091 {
3092     auto innerLayout = MakeInnerLayout();
3093     auto renderNode = child;
3094     RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3095     if (listItemGroup) {
3096         renderNode = listItemGroup->GetRenderNode();
3097         ListItemLayoutParam param = {
3098             .startCacheCount = (cachedCount_ > 0 && !isLaneList_) ? cachedCount_ - startCachedCount_ : 0,
3099             .endCacheCount = (cachedCount_ > 0 && !isLaneList_) ? cachedCount_ - endCachedCount_ : 0,
3100             .startMainPos = (cachedCount_ == 0 || isLaneList_) ? startMainPos_ : 0,
3101             .endMainPos = (cachedCount_ == 0 || isLaneList_) ? endMainPos_ : mainSize_,
3102             .listMainSize = mainSize_,
3103             .referencePos = referencePos,
3104             .maxLaneLength = isLaneList_ ? maxLaneLength_ : 0.0,
3105             .forwardLayout = forward,
3106             .isVertical = vertical_,
3107             .sticky = sticky_,
3108             .lanes = isLaneList_ ? lanes_ : 1,
3109             .align = component_->GetAlignListItemAlign(),
3110         };
3111         listItemGroup->SetItemGroupLayoutParam(param);
3112         listItemGroup->SetNeedLayoutDeep();
3113     } else if (isLaneList_) {
3114         innerLayout = MakeInnerLayoutForLane();
3115     }
3116     if (renderNode) {
3117         renderNode->Layout(innerLayout);
3118     }
3119 }
3120 
PaintChild(const RefPtr<RenderNode> & child,RenderContext & context,const Offset & offset)3121 void RenderList::PaintChild(const RefPtr<RenderNode>& child, RenderContext& context, const Offset& offset)
3122 {
3123     RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3124     if (listItemGroup) {
3125         auto renderNode = listItemGroup->GetRenderNode();
3126         RenderNode::PaintChild(renderNode, context, offset);
3127     } else {
3128         RenderNode::PaintChild(child, context, offset);
3129     }
3130 }
3131 
SetChildPosition(RefPtr<RenderNode> child,const Offset & offset)3132 void RenderList::SetChildPosition(RefPtr<RenderNode> child, const Offset& offset)
3133 {
3134     auto renderNode = child;
3135     RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3136     if (listItemGroup) {
3137         renderNode = listItemGroup->GetRenderNode();
3138     }
3139     if (renderNode) {
3140         renderNode->SetPosition(offset);
3141     }
3142 }
3143 
AddChildItem(RefPtr<RenderNode> child)3144 void RenderList::AddChildItem(RefPtr<RenderNode> child)
3145 {
3146     auto renderNode = child;
3147     RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3148     if (listItemGroup) {
3149         renderNode = listItemGroup->GetRenderNode();
3150     }
3151     AddChild(renderNode);
3152 }
3153 
SizeChangeOffset(double newWindowHeight)3154 void RenderList::SizeChangeOffset(double newWindowHeight)
3155 {
3156     LOGD("list newWindowHeight = %{public}f", newWindowHeight);
3157     auto context = context_.Upgrade();
3158     if (!context) {
3159         return;
3160     }
3161     auto textFieldManager = AceType::DynamicCast<TextFieldManager>(context->GetTextFieldManager());
3162     // only need to offset vertical lists
3163     if (textFieldManager && vertical_) {
3164         // only when textField is onFocus
3165         if (!textFieldManager->GetOnFocusTextField().Upgrade()) {
3166             return;
3167         }
3168         auto position = textFieldManager->GetClickPosition().GetY();
3169         double offset = newWindowHeight - position;
3170         if (LessOrEqual(offset, 0.0)) {
3171             // negative offset to scroll down
3172             currentOffset_ += offset;
3173             startIndexOffset_ += offset;
3174         }
3175         LOGD("size change offset applied, %{public}f", offset);
3176     }
3177 }
3178 
AdjustForReachEnd(double mainSize,double curMainPos)3179 void RenderList::AdjustForReachEnd(double mainSize, double curMainPos)
3180 {
3181     double delta = mainSize - curMainPos;
3182     for (auto rit = items_.rbegin(); rit != items_.rend(); rit++) {
3183         auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(*rit);
3184         if (itemGroup) {
3185             double size = GetMainSize(itemGroup->GetLayoutSize());
3186             LayoutChild(itemGroup, itemGroup->GetReferencePos() + delta, itemGroup->IsForwardLayout());
3187             double newSize = GetMainSize(itemGroup->GetLayoutSize());
3188             delta -= (newSize - size);
3189         }
3190     }
3191     currentOffset_ += delta;
3192 }
3193 
AdjustForReachStart(double & curMainPos)3194 void RenderList::AdjustForReachStart(double& curMainPos)
3195 {
3196     double delta = currentOffset_;
3197     for (const auto& child : items_) {
3198         auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3199         if (itemGroup) {
3200             double size = GetMainSize(itemGroup->GetLayoutSize());
3201             LayoutChild(itemGroup, itemGroup->GetReferencePos() - delta, itemGroup->IsForwardLayout());
3202             double newSize = GetMainSize(itemGroup->GetLayoutSize());
3203             delta -= (newSize - size);
3204         }
3205     }
3206     curMainPos -= delta;
3207 }
3208 
3209 } // namespace OHOS::Ace::V2
3210