1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_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 float SCROLL_MAX_TIME = 300.0f; // Scroll Animate max time 0.3 second
46 constexpr int32_t SCROLL_FROM_JUMP = 3;
47 constexpr int32_t DEFAULT_FINGERS = 1;
48 constexpr int32_t DEFAULT_DURATION = 200;
49 constexpr int32_t DEFAULT_DISTANCE = 0;
50 
51 constexpr bool DIR_HORIZONTAL = false;
52 constexpr bool DIR_VERTICAL = true;
53 constexpr bool DIR_FORWARD = false;
54 constexpr bool DIR_REVERSE = true;
55 constexpr int32_t STEP_FORWARD = 1;
56 constexpr int32_t STEP_BACK = -1;
57 constexpr int32_t STEP_INVALID = 10;
58 constexpr int32_t CENTER_ALIGN_DIVIDER = 2;
59 
60 // IsRightToLeft | IsListVertical | IsDirectionVertical | IsDirectionReverse
61 const std::map<bool, std::map<bool, std::map<bool, std::map<bool, int32_t>>>> DIRECTION_MAP = {
62     { false, // RTL is false
63         { { false, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_FORWARD }, { DIR_REVERSE, STEP_BACK } } },
64                        { DIR_VERTICAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } } } },
65             { true, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } },
66                         { DIR_VERTICAL, { { DIR_FORWARD, STEP_FORWARD }, { DIR_REVERSE, STEP_BACK } } } } } } },
67     { true, // RTL is true
68         { { false, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_BACK }, { DIR_REVERSE, STEP_FORWARD } } },
69                        { DIR_VERTICAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } } } },
70             { true, { { DIR_HORIZONTAL, { { DIR_FORWARD, STEP_INVALID }, { DIR_REVERSE, STEP_INVALID } } },
71                         { DIR_VERTICAL, { { DIR_FORWARD, STEP_BACK }, { DIR_REVERSE, STEP_FORWARD } } } } } } }
72 };
73 } // namespace
74 
~RenderList()75 RenderList::~RenderList()
76 {
77     if (scrollBarProxy_) {
78         scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
79     }
80 }
81 
Update(const RefPtr<Component> & component)82 void RenderList::Update(const RefPtr<Component>& component)
83 {
84     component_ = AceType::DynamicCast<ListComponent>(component);
85     ACE_DCHECK(component_);
86 
87     isRightToLeft_ = component_->GetTextDirection() == TextDirection::RTL ? true : false;
88     RemoveAllItems();
89 
90     auto axis = component_->GetDirection();
91     vertical_ = axis == Axis::VERTICAL;
92 
93     InitScrollBar();
94 
95     // Start index should be updated only for the first time
96     if (startIndex_ == INITIAL_CHILD_INDEX) {
97         initialIndex_ = static_cast<size_t>(component_->GetInitialIndex());
98         startIndex_ = initialIndex_ > 0 ? initialIndex_ : 0;
99         useEstimateCurrentOffset_ = true;
100     }
101     // maybe change startIndex
102     ApplyRestoreInfo();
103 
104     const auto& divider = component_->GetItemDivider();
105     listSpace_ = component_->GetSpace();
106     cachedCount_ = static_cast<size_t>(component_->GetCachedCount());
107 
108     LOGI("cached count: %{public}zu", cachedCount_);
109     spaceWidth_ = std::max(NormalizePercentToPx(component_->GetSpace(), vertical_),
110         divider ? NormalizePercentToPx(divider->strokeWidth, vertical_) : 0.0);
111     InitScrollable(axis);
112     // now only support spring
113     if (component_->GetEdgeEffect() == EdgeEffect::SPRING) {
114         if (!scrollEffect_ || scrollEffect_->GetEdgeEffect() != EdgeEffect::SPRING) {
115             scrollEffect_ = AceType::MakeRefPtr<ScrollSpringEffect>();
116             ResetEdgeEffect();
117         }
118     } else if (component_->GetEdgeEffect() == EdgeEffect::FADE) {
119         if (!scrollEffect_ || scrollEffect_->GetEdgeEffect() != EdgeEffect::FADE) {
120             scrollEffect_ = AceType::MakeRefPtr<ScrollFadeEffect>();
121             ResetEdgeEffect();
122         }
123     } else {
124         scrollEffect_ = nullptr;
125     }
126 
127     auto controller = component_->GetScrollController();
128     if (controller) {
129         controller->SetScrollNode(AceType::WeakClaim(this));
130     }
131     if (!animator_) {
132         animator_ = CREATE_ANIMATOR(GetContext());
133     }
134 
135     // chainAnimation
136     if (chainAnimation_ != component_->GetChainAnimation()) {
137         chainAnimation_ = component_->GetChainAnimation();
138         if (chainAnimation_) {
139             InitChainAnimation(CHAIN_ANIMATION_NODE_COUNT);
140             overSpringProperty_ = SpringChainProperty::GetDefaultOverSpringProperty();
141         } else {
142             overSpringProperty_ = nullptr;
143             chain_ = nullptr;
144             chainAdapter_ = nullptr;
145         }
146     }
147 
148     if (chainAnimation_) {
149         // add chain interval length
150         spaceWidth_ += NormalizeToPx(chainProperty_.Interval());
151     }
152 
153     scrollBarProxy_ = component_->GetScrollBarProxy();
154     InitScrollBarProxy();
155 
156     onItemDragStart_ = component_->GetOnItemDragStartId();
157     onItemDragEnter_ = component_->GetOnItemDragEnterId();
158     onItemDragMove_ = component_->GetOnItemDragMoveId();
159     onItemDragLeave_ = component_->GetOnItemDragLeaveId();
160     onItemDrop_ = component_->GetOnItemDropId();
161 
162     if (onItemDragStart_) {
163         CreateDragDropRecognizer();
164     }
165     FindRefreshParent(AceType::WeakClaim(this));
166 
167     isMultiSelectable_ = component_->GetMultiSelectable();
168     hasHeight_ = component_->GetHasHeight();
169     hasWidth_ = component_->GetHasWidth();
170     isLaneList_ = (component_->GetLanes() != -1) || (component_->GetLaneConstrain() != std::nullopt);
171     sticky_ = component_->GetSticky();
172     if (!vertical_ || scrollBar_->GetDisplayMode() != DisplayMode::OFF || chainAnimation_ ||
173         sticky_ != StickyStyle::NONE || isLaneList_ || !scrollable_->Available()) {
174         drivenRender_ = false;
175     } else {
176         drivenRender_ = true;
177     }
178     MarkNeedLayout();
179 }
180 
InitScrollable(Axis axis)181 void RenderList::InitScrollable(Axis axis)
182 {
183     if (scrollable_) {
184         scrollable_->SetAxis(axis);
185         scrollable_->SetOnScrollBegin(component_->GetOnScrollBegin());
186         return;
187     }
188 
189     auto callback = [weak = AceType::WeakClaim(this)](double offset, int32_t source) {
190         auto renderList = weak.Upgrade();
191 
192         if (!renderList) {
193             return false;
194         }
195 
196         if (source == SCROLL_FROM_START) {
197             renderList->ProcessDragStart(offset);
198             return true;
199         }
200 
201         Offset delta;
202         if (renderList->vertical_) {
203             delta.SetX(0.0);
204             delta.SetY(offset);
205         } else {
206             delta.SetX(offset);
207             delta.SetY(0.0);
208         }
209         renderList->AdjustOffset(delta, source);
210         if ((source == SCROLL_FROM_UPDATE || source == SCROLL_FROM_ANIMATION_SPRING) &&
211             renderList->currentOffset_ >= 0.0) {
212             if (renderList->scrollable_->RelatedScrollEventDoing(Offset(0.0, -offset))) {
213                 return false;
214             }
215         }
216         renderList->ProcessDragUpdate(renderList->GetMainAxis(delta));
217 
218         // Stop animator of scroll bar.
219         auto scrollBarProxy = renderList->scrollBarProxy_;
220         if (scrollBarProxy) {
221             scrollBarProxy->StopScrollBarAnimator();
222         }
223         return renderList->UpdateScrollPosition(renderList->GetMainAxis(delta), source);
224     };
225     scrollable_ = AceType::MakeRefPtr<Scrollable>(callback, axis);
226     scrollable_->SetNotifyScrollOverCallBack([weak = AceType::WeakClaim(this)](double velocity) {
227         auto list = weak.Upgrade();
228         if (!list) {
229             return;
230         }
231         list->ProcessScrollOverCallback(velocity);
232     });
233     scrollable_->SetScrollEndCallback([weak = AceType::WeakClaim(this)]() {
234         auto list = weak.Upgrade();
235         if (!list) {
236             LOGW("render list Upgrade fail in scroll end callback");
237             return;
238         }
239         auto proxy = list->scrollBarProxy_;
240         if (proxy) {
241             proxy->StartScrollBarAnimator();
242         }
243         auto scrollBar = list->scrollBar_;
244         if (scrollBar) {
245             scrollBar->HandleScrollBarEnd();
246         }
247         list->listEventFlags_[ListEvents::SCROLL_STOP] = true;
248         list->HandleListEvent();
249     });
250     InitializeScrollable(scrollable_);
251     scrollable_->SetOnScrollBegin(component_->GetOnScrollBegin());
252     if (vertical_) {
253         scrollable_->InitRelatedParent(GetParent());
254     }
255     scrollable_->Initialize(context_);
256     scrollable_->SetNodeId(GetAccessibilityNodeId());
257 }
258 
InitScrollBarProxy()259 void RenderList::InitScrollBarProxy()
260 {
261     if (!scrollBarProxy_) {
262         return;
263     }
264     auto callback = [weak = AceType::WeakClaim(this)](double value, int32_t source) {
265         auto renderList = weak.Upgrade();
266         if (!renderList) {
267             LOGE("render list is released");
268             return false;
269         }
270         return renderList->UpdateScrollPosition(value, source);
271     };
272     scrollBarProxy_->UnRegisterScrollableNode(AceType::WeakClaim(this));
273     scrollBarProxy_->RegisterScrollableNode({ AceType::WeakClaim(this), callback });
274 }
275 
IsReachStart()276 bool RenderList::IsReachStart()
277 {
278     bool scrollUpToReachStart = GreatNotEqual(prevOffset_, 0.0) && LessOrEqual(currentOffset_, 0.0);
279     bool scrollDownToReachStart = LessNotEqual(prevOffset_, 0.0) && GreatOrEqual(currentOffset_, 0.0);
280     return scrollUpToReachStart || scrollDownToReachStart;
281 }
282 
InitScrollBar()283 void RenderList::InitScrollBar()
284 {
285     if (!component_) {
286         LOGE("InitScrollBar failed, component_ is null.");
287         return;
288     }
289     if (scrollBar_) {
290         scrollBar_->SetDisplayMode(component_->GetScrollBar());
291         scrollBar_->Reset();
292         return;
293     }
294     const RefPtr<ScrollBarTheme> theme = GetTheme<ScrollBarTheme>();
295     if (!theme) {
296         return;
297     }
298 
299     scrollBar_ = AceType::MakeRefPtr<ScrollBar>(component_->GetScrollBar(), theme->GetShapeMode());
300     RefPtr<ListScrollBarController> controller = AceType::MakeRefPtr<ListScrollBarController>();
301     scrollBar_->SetScrollBarController(controller);
302 
303     // set the scroll bar style
304     scrollBar_->SetReservedHeight(theme->GetReservedHeight());
305     scrollBar_->SetMinHeight(theme->GetMinHeight());
306     scrollBar_->SetMinDynamicHeight(theme->GetMinDynamicHeight());
307     scrollBar_->SetForegroundColor(theme->GetForegroundColor());
308     scrollBar_->SetBackgroundColor(theme->GetBackgroundColor());
309     scrollBar_->SetPadding(theme->GetPadding());
310     scrollBar_->SetScrollable(true);
311     scrollBar_->SetInactiveWidth(theme->GetNormalWidth());
312     scrollBar_->SetNormalWidth(theme->GetNormalWidth());
313     scrollBar_->SetActiveWidth(theme->GetActiveWidth());
314     scrollBar_->SetTouchWidth(theme->GetTouchWidth());
315     if (!vertical_) {
316         scrollBar_->SetPositionMode(PositionMode::BOTTOM);
317     } else {
318         if (isRightToLeft_) {
319             scrollBar_->SetPositionMode(PositionMode::LEFT);
320         }
321     }
322     scrollBar_->InitScrollBar(AceType::WeakClaim(this), GetContext());
323     SetScrollBarCallback();
324 }
325 
SetScrollBarCallback()326 void RenderList::SetScrollBarCallback()
327 {
328     if (!scrollBar_ || !scrollBar_->NeedScrollBar()) {
329         return;
330     }
331     auto&& scrollCallback = [weakList = AceType::WeakClaim(this)](double value, int32_t source) {
332         auto list = weakList.Upgrade();
333         if (!list) {
334             LOGE("render list is released");
335             return false;
336         }
337         return list->UpdateScrollPosition(value, source);
338     };
339     auto&& barEndCallback = [weakList = AceType::WeakClaim(this)](int32_t value) {
340         auto list = weakList.Upgrade();
341         if (!list) {
342             LOGE("render list is released.");
343             return;
344         }
345         list->scrollBarOpacity_ = value;
346         list->MarkNeedRender();
347     };
348     auto&& scrollEndCallback = []() {
349         // nothing to do
350     };
351     scrollBar_->SetCallBack(scrollCallback, barEndCallback, scrollEndCallback);
352 }
353 
GetLaneLengthInPx(const Dimension & length)354 double RenderList::GetLaneLengthInPx(const Dimension& length)
355 {
356     if (length.Unit() == DimensionUnit::PERCENT) {
357         return NormalizePercentToPx(length, !vertical_, true);
358     }
359     return NormalizeToPx(length);
360 }
361 
ModifyLaneLength(const std::optional<std::pair<Dimension,Dimension>> & laneConstrain)362 void RenderList::ModifyLaneLength(const std::optional<std::pair<Dimension, Dimension>>& laneConstrain)
363 {
364     minLaneLength_ = GetLaneLengthInPx(laneConstrain.value().first);
365     maxLaneLength_ = GetLaneLengthInPx(laneConstrain.value().second);
366     if (LessOrEqual(maxLaneLength_, 0.0)) {
367         maxLaneLength_ = GetCrossSize(GetLayoutSize());
368     }
369     if (LessOrEqual(minLaneLength_, 0.0)) {
370         minLaneLength_ = std::min(GetCrossSize(GetLayoutSize()), maxLaneLength_);
371     }
372     if (GreatNotEqual(minLaneLength_, maxLaneLength_)) {
373         LOGI("minLaneLength: %{public}f is greater than maxLaneLength: %{public}f, assign minLaneLength to"
374              " maxLaneLength",
375             minLaneLength_, maxLaneLength_);
376         maxLaneLength_ = minLaneLength_;
377     }
378 }
379 
CalculateLanes()380 void RenderList::CalculateLanes()
381 {
382     auto lanes = component_->GetLanes();
383     do {
384         auto laneConstrain = component_->GetLaneConstrain();
385         // Case 1: lane length constrain is not set
386         //      1.1: use [lanes_] set by user if [lanes_] is set
387         //      1.2: set [lanes_] to 1 if [lanes_] is not set
388         if (!laneConstrain) {
389             if (lanes <= 0) {
390                 lanes = 1;
391             }
392             maxLaneLength_ = GetCrossSize(GetLayoutParam().GetMaxSize()) / lanes;
393             minLaneLength_ = GetCrossSize(GetLayoutParam().GetMinSize()) / lanes;
394             break;
395         }
396         // Case 2: lane length constrain is set --> need to calculate [lanes_] according to contraint.
397         // We agreed on such rules (assuming we have a vertical list here):
398         // rule 1: [minLaneLength_] has a higher priority than [maxLaneLength_] when decide [lanes_], for e.g.,
399         //         if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 120,
400         //         the [lanes_] is 3 rather than 2.
401         // rule 2: after [lanes_] is determined by rule 1, the width of lane will be as large as it can be, for e.g.,
402         //         if [minLaneLength_] is 40, [maxLaneLength_] is 60, list's width is 132, the [lanes_] is 3
403         //         according to rule 1, then the width of lane will be 132 / 3 = 44 rather than 40,
404         //         its [minLaneLength_].
405 
406         // set layout size temporarily to calculate percent unit of constrain
407         SetLayoutSize(GetLayoutParam().GetMaxSize());
408         ModifyLaneLength(laneConstrain);
409 
410         // if minLaneLength is 40, maxLaneLength is 60
411         // when list's width is 120, lanes_ = 3
412         // when list's width is 80, lanes_ = 2
413         // when list's width is 70, lanes_ = 1
414         auto maxCrossSize = GetCrossSize(GetLayoutSize());
415         double maxLanes = maxCrossSize / minLaneLength_;
416         double minLanes = maxCrossSize / maxLaneLength_;
417         // let's considerate scenarios when maxCrossSize > 0
418         // now it's guaranteed that [minLaneLength_] <= [maxLaneLength_], i.e., maxLanes >= minLanes > 0
419         // there are 3 scenarios:
420         // 1. 1 > maxLanes >= minLanes > 0
421         // 2. maxLanes >= 1 >= minLanes > 0
422         // 3. maxLanes >= minLanes > 1
423 
424         // 1. 1 > maxLanes >= minLanes > 0 ---> maxCrossSize < minLaneLength_ =< maxLaneLength
425         if (GreatNotEqual(1, maxLanes) && GreatOrEqual(maxLanes, minLanes)) {
426             lanes = 1;
427             minLaneLength_ = maxCrossSize;
428             maxLaneLength_ = maxCrossSize;
429             break;
430         }
431         // 2. maxLanes >= 1 >= minLanes > 0 ---> minLaneLength_ = maxCrossSize < maxLaneLength
432         if (GreatOrEqual(maxLanes, 1) && LessOrEqual(minLanes, 1)) {
433             lanes = std::floor(maxLanes);
434             maxLaneLength_ = maxCrossSize;
435             break;
436         }
437         // 3. maxLanes >= minLanes > 1 ---> minLaneLength_ <= maxLaneLength < maxCrossSize
438         if (GreatOrEqual(maxLanes, minLanes) && GreatNotEqual(minLanes, 1)) {
439             lanes = std::floor(maxLanes);
440             break;
441         }
442         lanes = 1;
443         LOGE("unexpected situation, set lanes to 1, maxLanes: %{public}f, minLanes: %{public}f, minLaneLength_: "
444              "%{public}f, maxLaneLength_: %{public}f",
445             maxLanes, minLanes, minLaneLength_, maxLaneLength_);
446     } while (0);
447     if (lanes != lanes_) {  // if lanes changes, adjust startIndex_
448         lanes_ = lanes;
449         if (lanes > 1) {
450             size_t startIndex = startIndex_ - GetItemRelativeIndex(startIndex_) % static_cast<size_t>(lanes);
451             if (startIndex_ != startIndex) {
452                 RemoveAllItems();
453             }
454         }
455     }
456 }
457 
RequestNewItemsAtEndForLaneList(double & curMainPos,double mainSize)458 void RenderList::RequestNewItemsAtEndForLaneList(double& curMainPos, double mainSize)
459 {
460     int newItemCntInLine = 0;
461     double lineMainSize = 0;
462     for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
463         bool breakWhenRequestNewItem = false;
464         RefPtr<RenderListItemGroup> itemGroup;
465         do {
466             if (GreatOrEqual(curMainPos, endMainPos_)) {
467                 breakWhenRequestNewItem = true;
468                 break;
469             }
470             auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
471             if (!child) {
472                 startIndex_ = std::min(startIndex_, TotalCount());
473                 breakWhenRequestNewItem = true;
474                 break;
475             }
476             if (GreatOrEqual(curMainPos, mainSize)) {
477                 ++endCachedCount_;
478             }
479             itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
480             if (itemGroup) {
481                 break;
482             }
483             lineMainSize = std::max(lineMainSize, GetMainSize(child->GetLayoutSize()));
484             ++newItemCntInLine;
485         } while (0);
486         bool singleLaneDoneAddItem = (lanes_ == 1) && !breakWhenRequestNewItem;
487         bool multiLaneDoneSupplyOneLine = (lanes_ > 1) && (newItemCntInLine == lanes_);
488         bool multiLaneStartSupplyLine = (itemGroup || breakWhenRequestNewItem) && (newItemCntInLine >= 1);
489         if (singleLaneDoneAddItem || multiLaneDoneSupplyOneLine || multiLaneStartSupplyLine) {
490             curMainPos += lineMainSize + spaceWidth_;
491             newItemCntInLine = 0;
492             lineMainSize = 0;
493         }
494         if (itemGroup) {
495             double size = GetMainSize(itemGroup->GetLayoutSize());
496             curMainPos += size + spaceWidth_;
497         }
498         if (breakWhenRequestNewItem) {
499             break;
500         }
501     }
502 }
503 
RequestNewItemsAtEnd(double & curMainPos,double mainSize)504 void RenderList::RequestNewItemsAtEnd(double& curMainPos, double mainSize)
505 {
506     for (size_t newIndex = startIndex_ + items_.size();; ++newIndex) {
507         if (cachedCount_ != 0) {
508             if (endCachedCount_ >= cachedCount_ && GreatOrEqual(curMainPos, mainSize)) {
509                 break;
510             }
511         } else {
512             if (GreatOrEqual(curMainPos, endMainPos_)) {
513                 break;
514             }
515         }
516         auto child = RequestAndLayoutNewItem(newIndex, curMainPos);
517         if (!child) {
518             startIndex_ = std::min(startIndex_, TotalCount());
519             break;
520         }
521         if (GreatOrEqual(curMainPos, mainSize)) {
522             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
523             if (itemGroup) {
524                 endCachedCount_ += itemGroup->GetCurrEndCacheCount();
525             } else {
526                 ++endCachedCount_;
527             }
528         }
529         curMainPos += GetMainSize(child->GetLayoutSize()) + spaceWidth_;
530     }
531 
532     if (selectedItem_ && selectedItemIndex_ < startIndex_) {
533         curMainPos += GetMainSize(selectedItem_->GetLayoutSize()) + spaceWidth_;
534     }
535 }
536 
RequestNewItemsAtStartForLaneList()537 void RenderList::RequestNewItemsAtStartForLaneList()
538 {
539     int newItemCntInLine = 0;
540     double lineMainSize = 0;
541     for (; startIndex_ > 0; --startIndex_) {
542         bool breakWhenRequestNewItem = false;
543         RefPtr<RenderListItemGroup> itemGroup;
544         do {
545             if (LessOrEqual(currentOffset_, startMainPos_)) {
546                 breakWhenRequestNewItem = true;
547                 break;
548             }
549             auto child = RequestAndLayoutNewItem(startIndex_ - 1, currentOffset_ - spaceWidth_, false);
550             if (!child) {
551                 breakWhenRequestNewItem = true;
552                 break;
553             }
554             if (selectedItemIndex_ == startIndex_) {
555                 continue;
556             }
557             if (LessOrEqual(currentOffset_, 0.0)) {
558                 ++startCachedCount_;
559             }
560             itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
561             if (itemGroup) {
562                 break;
563             }
564             lineMainSize = std::max(lineMainSize, GetMainSize(child->GetLayoutSize()));
565             ++newItemCntInLine;
566         } while (0);
567         bool singleLaneDoneAddItem = (lanes_ == 1) && !breakWhenRequestNewItem;
568         bool isLaneStart =
569             !itemGroup && (lanes_ > 1) && (GetItemRelativeIndex(startIndex_ - 1) % static_cast<size_t>(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             event.second = false;                                      \
751         }                                                              \
752         break;
753 
HandleListEvent()754 void RenderList::HandleListEvent()
755 {
756     for (auto& event : listEventFlags_) {
757         switch (event.first) {
758             CASE_OF_LIST_EVENT_WITH_NO_PARAM(SCROLL_STOP, GetOnScrollStop);
759             CASE_OF_LIST_EVENT_WITH_NO_PARAM(REACH_START, GetOnReachStart);
760             CASE_OF_LIST_EVENT_WITH_NO_PARAM(REACH_END, GetOnReachEnd);
761             default:
762                 LOGW("This event does not handle in here, please check. event number: %{public}d", event.first);
763                 break;
764         }
765     }
766 }
767 
CalculateLaneCrossOffset(double crossSize,double childCrossSize)768 double RenderList::CalculateLaneCrossOffset(double crossSize, double childCrossSize)
769 {
770     double delta = crossSize - childCrossSize;
771     // TODO: modify in rtl scenario
772     switch (component_->GetAlignListItemAlign()) {
773         case ListItemAlign::START:
774             return 0.0;
775         case ListItemAlign::CENTER:
776             return delta / CENTER_ALIGN_DIVIDER;
777         case ListItemAlign::END:
778             return delta;
779         default:
780             LOGW("Invalid ListItemAlign: %{public}d", component_->GetAlignListItemAlign());
781             return 0.0;
782     }
783 }
784 
SetItemsPositionForLaneList(double mainSize)785 Size RenderList::SetItemsPositionForLaneList(double mainSize)
786 {
787     double crossSize = fixedCrossSize_ ? GetCrossSize(GetLayoutParam().GetMaxSize()) : 0.0;
788     if (items_.empty()) {
789         return MakeValue<Size>(fixedMainSize_ ? mainSize : 0.0, crossSize);
790     }
791 
792     double curMainPos = currentOffset_;
793     size_t index = startIndex_;
794     size_t newStickyIndex = 0;
795     RefPtr<RenderListItem> newStickyItem;
796     RefPtr<RenderListItem> nextStickyItem;
797     double nextStickyMainAxis = Size::INFINITE_SIZE;
798     size_t firstIdx = INITIAL_CHILD_INDEX;
799     size_t lastIdx = 0;
800     double selectedItemMainSize = selectedItem_ ? GetMainSize(selectedItem_->GetLayoutSize()) : 0.0;
801 
802     for (auto iter = items_.begin(); iter != items_.end();) {
803         RefPtr<RenderListItem> child;
804         double childMainSize = 0.0;
805         double childCrossSize = 0.0;
806         std::vector<RefPtr<RenderListItem>> itemSet;
807         // start set child position in a row
808         for (int32_t rowIndex = 0; rowIndex < lanes_; rowIndex++) {
809             child = *iter;
810             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
811             if (itemGroup && rowIndex > 0) {
812                 break;
813             }
814             double singleChildSize = GetMainSize(child->GetLayoutSize());
815             childCrossSize += GetCrossSize(child->GetLayoutSize());
816             childMainSize = std::max(childMainSize, singleChildSize); // get max item height in a row as row height
817             // store items in a row, set position of each item after done getting [childMainSize]
818             itemSet.emplace_back(child);
819             if ((++iter) == items_.end() || itemGroup) {
820                 break;
821             }
822         }
823         if (!fixedCrossSize_) {
824             crossSize = std::max(crossSize, childCrossSize);
825         }
826         auto offset = MakeValue<Offset>(curMainPos, 0.0);
827         if (chainAnimation_) {
828             double chainDelta = GetChainDelta(index);
829             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
830             if (itemGroup) {
831                 itemGroup->SetChainOffset(-chainDelta);
832             }
833             offset += MakeValue<Offset>(-chainDelta, 0.0);
834         }
835         // set item position for one row
836         for (size_t i = 0; i < itemSet.size(); i++) {
837             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(itemSet[i]);
838             double itemCrossSize = itemGroup ? crossSize : crossSize / lanes_;
839             auto offsetCross = CalculateLaneCrossOffset(itemCrossSize, GetCrossSize(itemSet[i]->GetLayoutSize()));
840             auto position = offset + MakeValue<Offset>(0.0, itemCrossSize * i + offsetCross);
841             if (isRightToLeft_) {
842                 if (IsVertical()) {
843                     position = MakeValue<Offset>(
844                         GetMainAxis(position), crossSize - childCrossSize / itemSet.size() - GetCrossAxis(position));
845                 } else {
846                     position =
847                         MakeValue<Offset>(mainSize - childMainSize - GetMainAxis(position), GetCrossAxis(position));
848                 }
849             }
850             SetChildPosition(itemSet[i], position);
851         }
852 
853         if (lanes_ == 1) {
854             if (selectedItem_) {
855                 double range = std::min(selectedItemMainSize, childMainSize) / 2.0;
856                 bool beforeSelectedItem = index <= selectedItemIndex_;
857                 if (beforeSelectedItem && targetIndex_ == index) {
858                     targetMainAxis_ = curMainPos;
859                     curMainPos += selectedItemMainSize + spaceWidth_;
860                 }
861 
862                 if (movingForward_) {
863                     double axis = selectedItemMainAxis_;
864                     if (GreatOrEqual(axis, curMainPos) && LessNotEqual(axis, curMainPos + range)) {
865                         targetIndex_ = beforeSelectedItem ? index : index - 1;
866                         targetMainAxis_ = curMainPos;
867                         curMainPos += selectedItemMainSize + spaceWidth_;
868                     }
869                 } else {
870                     double axis = selectedItemMainAxis_ + selectedItemMainSize;
871                     double limit = curMainPos + childMainSize;
872                     if (GreatNotEqual(axis, limit - range) && LessOrEqual(axis, limit)) {
873                         targetIndex_ = beforeSelectedItem ? index + 1 : index;
874                         targetMainAxis_ = curMainPos;
875                         curMainPos -= selectedItemMainSize + spaceWidth_;
876                     }
877                 }
878             }
879 
880             // Disable sticky mode while expand all items
881             if (fixedMainSize_ && itemSet[0]->GetSticky() != StickyMode::NONE) {
882                 if (LessOrEqual(curMainPos, 0.0)) {
883                     newStickyItem = itemSet[0];
884                     newStickyIndex = index;
885                 } else if (!nextStickyItem) {
886                     nextStickyItem = itemSet[0];
887                     nextStickyMainAxis = curMainPos;
888                 }
889             }
890         }
891         itemSet.clear();
892 
893         childMainSize += spaceWidth_;
894         if (LessNotEqual(curMainPos, mainSize) && GreatNotEqual(curMainPos + childMainSize, 0.0)) {
895             firstIdx = std::min(firstIdx, index);
896             lastIdx = std::max(lastIdx, index);
897         }
898 
899         if (child != selectedItem_) {
900             curMainPos += childMainSize;
901         }
902 
903         if (selectedItem_ && index > selectedItemIndex_ && targetIndex_ == index) {
904             targetMainAxis_ = curMainPos;
905             curMainPos += selectedItemMainSize + spaceWidth_;
906         }
907 
908         ++index;
909     }
910     if (firstIdx != firstDisplayIndex_ || lastIdx != lastDisplayIndex_) {
911         firstDisplayIndex_ = firstIdx;
912         lastDisplayIndex_ = lastIdx;
913         ResumeEventCallback(component_, &ListComponent::GetOnScrollIndex, static_cast<int32_t>(firstDisplayIndex_),
914             static_cast<int32_t>(lastDisplayIndex_), static_cast<int32_t>(midDisplayIndex_));
915     }
916 
917     // Disable sticky mode while expand all items
918     if (!fixedMainSize_) {
919         return MakeValue<Size>(curMainPos - spaceWidth_, crossSize);
920     }
921 
922     if (lanes_ == 1) {
923         UpdateStickyListItem(newStickyItem, newStickyIndex, nextStickyItem);
924         if (currentStickyItem_) {
925             const auto& stickyItemLayoutSize = currentStickyItem_->GetLayoutSize();
926             const double mainStickySize = GetMainSize(stickyItemLayoutSize) + spaceWidth_;
927             if (nextStickyItem && LessNotEqual(nextStickyMainAxis, mainStickySize)) {
928                 auto position = MakeValue<Offset>(nextStickyMainAxis - mainStickySize, 0.0);
929                 if (isRightToLeft_) {
930                     if (IsVertical()) {
931                         position = MakeValue<Offset>(GetMainAxis(position),
932                             crossSize - GetCrossSize(stickyItemLayoutSize) - GetCrossAxis(position));
933                     } else {
934                         position = MakeValue<Offset>(
935                             mainSize - mainStickySize - GetMainAxis(position), GetCrossAxis(position));
936                     }
937                 }
938                 currentStickyItem_->SetPosition(position);
939             } else {
940                 currentStickyItem_->SetPosition(MakeValue<Offset>(0.0, 0.0));
941             }
942 
943             if (!fixedCrossSize_) {
944                 crossSize = std::max(crossSize, GetCrossSize(stickyItemLayoutSize));
945             }
946         }
947     }
948 
949     return MakeValue<Size>(mainSize, crossSize);
950 }
951 
SetItemsPosition(double mainSize)952 Size RenderList::SetItemsPosition(double mainSize)
953 {
954     double crossSize = fixedCrossSize_ ? GetCrossSize(GetLayoutParam().GetMaxSize()) : 0.0;
955     if (items_.empty()) {
956         return MakeValue<Size>(fixedMainSize_ ? mainSize : 0.0, crossSize);
957     }
958 
959     double curMainPos = currentOffset_;
960     size_t index = startIndex_;
961     size_t newStickyIndex = 0;
962     RefPtr<RenderListItem> newStickyItem;
963     RefPtr<RenderListItem> nextStickyItem;
964     double nextStickyMainAxis = Size::INFINITE_SIZE;
965     size_t firstIdx = INITIAL_CHILD_INDEX;
966     size_t lastIdx = 0;
967     double selectedItemMainSize = selectedItem_ ? GetMainSize(selectedItem_->GetLayoutSize()) : 0.0;
968 
969     for (const auto& child : items_) {
970         const auto& childLayoutSize = child->GetLayoutSize();
971         double childMainSize = GetMainSize(childLayoutSize);
972 
973         if (selectedItem_) {
974             double range = std::min(selectedItemMainSize, childMainSize) / 2.0;
975             bool beforeSelectedItem = index <= selectedItemIndex_;
976             if (beforeSelectedItem && targetIndex_ == index) {
977                 targetMainAxis_ = curMainPos;
978                 curMainPos += selectedItemMainSize + spaceWidth_;
979             }
980 
981             if (movingForward_) {
982                 double axis = selectedItemMainAxis_;
983                 if (GreatOrEqual(axis, curMainPos) && LessNotEqual(axis, curMainPos + range)) {
984                     targetIndex_ = beforeSelectedItem ? index : index - 1;
985                     targetMainAxis_ = curMainPos;
986                     curMainPos += selectedItemMainSize + spaceWidth_;
987                 }
988             } else {
989                 double axis = selectedItemMainAxis_ + selectedItemMainSize;
990                 double limit = curMainPos + childMainSize;
991                 if (GreatNotEqual(axis, limit - range) && LessOrEqual(axis, limit)) {
992                     targetIndex_ = beforeSelectedItem ? index + 1 : index;
993                     targetMainAxis_ = curMainPos;
994                     curMainPos -= selectedItemMainSize + spaceWidth_;
995                 }
996             }
997         }
998 
999         auto offsetCross = CalculateLaneCrossOffset(crossSize, GetCrossSize(childLayoutSize));
1000         auto offset = MakeValue<Offset>(curMainPos, offsetCross);
1001         if (chainAnimation_) {
1002             double chainDelta = GetChainDelta(index);
1003             auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
1004             if (itemGroup) {
1005                 itemGroup->SetChainOffset(-chainDelta);
1006             }
1007             offset += MakeValue<Offset>(-chainDelta, 0.0);
1008         }
1009 
1010         if (isRightToLeft_) {
1011             if (IsVertical()) {
1012                 offset = MakeValue<Offset>(
1013                     GetMainAxis(offset), crossSize - GetCrossSize(childLayoutSize) - GetCrossAxis(offset));
1014             } else {
1015                 offset = MakeValue<Offset>(
1016                     mainSize - GetMainSize(childLayoutSize) - GetMainAxis(offset), GetCrossAxis(offset));
1017             }
1018         }
1019         SetChildPosition(child, offset);
1020         // Disable sticky mode while expand all items
1021         if (fixedMainSize_ && child->GetSticky() != StickyMode::NONE) {
1022             if (LessOrEqual(curMainPos, 0.0)) {
1023                 newStickyItem = child;
1024                 newStickyIndex = index;
1025             } else if (!nextStickyItem) {
1026                 nextStickyItem = child;
1027                 nextStickyMainAxis = curMainPos;
1028             }
1029         }
1030 
1031         childMainSize += spaceWidth_;
1032         if (LessNotEqual(curMainPos, mainSize) && GreatNotEqual(curMainPos + childMainSize, 0.0)) {
1033             if (!fixedCrossSize_) {
1034                 crossSize = std::max(crossSize, GetCrossSize(childLayoutSize));
1035             }
1036             firstIdx = std::min(firstIdx, index);
1037             lastIdx = std::max(lastIdx, index);
1038         }
1039 
1040         if (child != selectedItem_) {
1041             curMainPos += childMainSize;
1042         }
1043 
1044         if (selectedItem_ && index > selectedItemIndex_ && targetIndex_ == index) {
1045             targetMainAxis_ = curMainPos;
1046             curMainPos += selectedItemMainSize + spaceWidth_;
1047         }
1048 
1049         ++index;
1050     }
1051     if (firstIdx != firstDisplayIndex_ || lastIdx != lastDisplayIndex_) {
1052         firstDisplayIndex_ = firstIdx;
1053         lastDisplayIndex_ = lastIdx;
1054         ResumeEventCallback(component_, &ListComponent::GetOnScrollIndex, static_cast<int32_t>(firstDisplayIndex_),
1055             static_cast<int32_t>(lastDisplayIndex_), static_cast<int32_t>(midDisplayIndex_));
1056     }
1057 
1058     // Disable sticky mode while expand all items
1059     if (!fixedMainSize_) {
1060         return MakeValue<Size>(curMainPos - spaceWidth_, crossSize);
1061     }
1062 
1063     UpdateStickyListItem(newStickyItem, newStickyIndex, nextStickyItem);
1064     if (currentStickyItem_) {
1065         const auto& stickyItemLayoutSize = currentStickyItem_->GetLayoutSize();
1066         const double mainStickySize = GetMainSize(stickyItemLayoutSize) + spaceWidth_;
1067         auto offsetCross = CalculateLaneCrossOffset(crossSize, GetCrossSize(currentStickyItem_->GetLayoutSize()));
1068         if (nextStickyItem && LessNotEqual(nextStickyMainAxis, mainStickySize)) {
1069             auto position = MakeValue<Offset>(nextStickyMainAxis - mainStickySize, offsetCross);
1070             if (isRightToLeft_) {
1071                 if (IsVertical()) {
1072                     position = MakeValue<Offset>(
1073                         GetMainAxis(position), crossSize - GetCrossSize(stickyItemLayoutSize) - GetCrossAxis(position));
1074                 } else {
1075                     position =
1076                         MakeValue<Offset>(mainSize - mainStickySize - GetMainAxis(position), GetCrossAxis(position));
1077                 }
1078             }
1079             currentStickyItem_->SetPosition(position);
1080         } else {
1081             currentStickyItem_->SetPosition(MakeValue<Offset>(0.0, offsetCross));
1082         }
1083 
1084         if (!fixedCrossSize_) {
1085             crossSize = std::max(crossSize, GetCrossSize(stickyItemLayoutSize));
1086         }
1087     }
1088 
1089     return MakeValue<Size>(mainSize, crossSize);
1090 }
1091 
UpdateStickyListItem(const RefPtr<RenderListItem> & newStickyItem,size_t newStickyItemIndex,const RefPtr<RenderListItem> & nextStickyItem)1092 void RenderList::UpdateStickyListItem(const RefPtr<RenderListItem>& newStickyItem, size_t newStickyItemIndex,
1093     const RefPtr<RenderListItem>& nextStickyItem)
1094 {
1095     if (newStickyItem) {
1096         if (newStickyItem == currentStickyItem_) {
1097             return;
1098         }
1099 
1100         if (currentStickyItem_ && currentStickyIndex_ < startIndex_) {
1101             RecycleListItem(currentStickyIndex_);
1102         }
1103 
1104         currentStickyItem_ = newStickyItem;
1105         currentStickyIndex_ = newStickyItemIndex;
1106         return;
1107     }
1108 
1109     if (nextStickyItem && nextStickyItem == currentStickyItem_) {
1110         size_t index = currentStickyIndex_ != 0 ? currentStickyIndex_ - 1 : INVALID_CHILD_INDEX;
1111         ApplyPreviousStickyListItem(index, 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_ != 0 ? startIndex_ - 1 : INVALID_CHILD_INDEX;
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 = static_cast<size_t>(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)1751 void RenderList::JumpToIndex(int32_t idx)
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);
1804         } else if (scrollEdgeType == ScrollEdgeType::SCROLL_BOTTOM) {
1805             JumpToIndex(TotalCount());
1806         }
1807     } else {
1808         if (scrollEdgeType == ScrollEdgeType::SCROLL_LEFT) {
1809             JumpToIndex(0);
1810         } else if (scrollEdgeType == ScrollEdgeType::SCROLL_RIGHT) {
1811             JumpToIndex(TotalCount());
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         return;
2009     }
2010 
2011     if (NearZero(velocity)) {
2012         return;
2013     }
2014 
2015     if (reachStart_) {
2016         dragStartIndexPending_ = startIndex_;
2017     } else if (reachEnd_) {
2018         dragStartIndexPending_ = startIndex_;
2019         if (!items_.empty()) {
2020             dragStartIndexPending_ += items_.size() - 1;
2021         }
2022     }
2023 
2024     double delta = FlushChainAnimation();
2025     currentOffset_ += delta;
2026     if (!NearZero(delta)) {
2027         LOGE("ProcessScrollOverCallback delta = %lf currentOffset_ = %lf", delta, currentOffset_);
2028     }
2029 }
2030 
InitChainAnimation(int32_t nodeCount)2031 void RenderList::InitChainAnimation(int32_t nodeCount)
2032 {
2033     auto context = GetContext().Upgrade();
2034     if (!context) {
2035         LOGE("Init chain animation failed. context is null");
2036         return;
2037     }
2038 
2039     if (chainAdapter_ && chain_) {
2040         return;
2041     }
2042     chainAdapter_ = AceType::MakeRefPtr<BilateralSpringAdapter>();
2043     chain_ = AceType::MakeRefPtr<SimpleSpringChain>(chainAdapter_);
2044     const auto& property = GetChainProperty();
2045     chain_->SetFrameDelta(property.FrameDelay());
2046     if (property.StiffnessTransfer()) {
2047         chain_->SetStiffnessTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.StiffnessCoefficient()));
2048     } else {
2049         chain_->SetStiffnessTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.StiffnessCoefficient()));
2050     }
2051     if (property.DampingTransfer()) {
2052         chain_->SetDampingTransfer(AceType::MakeRefPtr<ExpParamTransfer>(property.DampingCoefficient()));
2053     } else {
2054         chain_->SetDampingTransfer(AceType::MakeRefPtr<LinearParamTransfer>(property.DampingCoefficient()));
2055     }
2056     chain_->SetControlDamping(property.ControlDamping());
2057     chain_->SetControlStiffness(property.ControlStiffness());
2058     chain_->SetDecoration(context->NormalizeToPx(property.Interval()));
2059     chain_->SetMinDecoration(context->NormalizeToPx(property.MinInterval()));
2060     chain_->SetMaxDecoration(context->NormalizeToPx(property.MaxInterval()));
2061     for (int32_t index = 0; index < nodeCount; index++) {
2062         auto node = AceType::MakeRefPtr<BilateralSpringNode>(GetContext(), index, 0.0);
2063         WeakPtr<BilateralSpringNode> nodeWeak(node);
2064         WeakPtr<SimpleSpringAdapter> adapterWeak(chainAdapter_);
2065         node->AddUpdateListener(
2066             [weak = AceType::WeakClaim(this), nodeWeak, adapterWeak](double value, double velocity) {
2067                 auto renderList = weak.Upgrade();
2068                 auto node = nodeWeak.Upgrade();
2069                 auto adapter = adapterWeak.Upgrade();
2070                 if (!renderList || !node || !adapter) {
2071                     return;
2072                 }
2073                 renderList->MarkNeedLayout();
2074             });
2075         chainAdapter_->AddNode(node);
2076     }
2077     chainAdapter_->NotifyControlIndexChange();
2078 }
2079 
GetChainDelta(int32_t index) const2080 double RenderList::GetChainDelta(int32_t index) const
2081 {
2082     if (!chainAdapter_) {
2083         return 0.0;
2084     }
2085     double value = 0.0;
2086     RefPtr<BilateralSpringNode> node;
2087     int32_t controlIndex = dragStartIndex_;
2088     int32_t baseIndex = controlIndex - chainAdapter_->GetControlIndex();
2089     auto targetIndex = std::clamp(index - baseIndex, 0, CHAIN_ANIMATION_NODE_COUNT - 1);
2090     node = AceType::DynamicCast<BilateralSpringNode>(chainAdapter_->GetNode(targetIndex));
2091     if (node) {
2092         value = node->GetValue();
2093     }
2094     return value;
2095 }
2096 
GetNearChildByPosition(double mainOffset) const2097 size_t RenderList::GetNearChildByPosition(double mainOffset) const
2098 {
2099     size_t index = startIndex_;
2100     size_t prevIndex = INVALID_CHILD_INDEX;
2101 
2102     for (auto& child : items_) {
2103         auto childMainOffset = GetMainAxis(child->GetPosition());
2104         if (childMainOffset > mainOffset) {
2105             return prevIndex;
2106         }
2107         prevIndex = index++;
2108     }
2109     return prevIndex;
2110 }
2111 
FlushChainAnimation()2112 double RenderList::FlushChainAnimation()
2113 {
2114     if (!chainAnimation_ || !chain_ || !chainAdapter_) {
2115         return 0.0;
2116     }
2117     double deltaDistance = 0.0;
2118     bool needSetValue = false;
2119     bool overScroll = scrollable_ && scrollable_->IsSpringMotionRunning();
2120     if (chainOverScroll_ != overScroll) {
2121         if (overScroll) {
2122             const auto& springProperty = GetOverSpringProperty();
2123             if (springProperty && springProperty->IsValid()) {
2124                 chain_->SetControlStiffness(springProperty->Stiffness());
2125                 chain_->SetControlDamping(springProperty->Damping());
2126             }
2127         } else {
2128             chain_->SetControlStiffness(GetChainProperty().ControlStiffness());
2129             chain_->SetControlDamping(GetChainProperty().ControlDamping());
2130         }
2131         chain_->OnControlNodeChange();
2132         chainOverScroll_ = overScroll;
2133     }
2134     chain_->FlushAnimation();
2135     if (dragStartIndexPending_ != dragStartIndex_) {
2136         deltaDistance = chainAdapter_->ResetControl(dragStartIndexPending_ - dragStartIndex_);
2137         dragStartIndex_ = dragStartIndexPending_;
2138         chainAdapter_->SetDeltaValue(-deltaDistance);
2139         needSetValue = true;
2140     }
2141     if (!NearZero(currentDelta_)) {
2142         chainAdapter_->SetDeltaValue(currentDelta_);
2143         currentDelta_ = 0.0;
2144         needSetValue = true;
2145     }
2146     if (needSetValue) {
2147         chain_->SetValue(0.0);
2148     }
2149     return deltaDistance;
2150 }
2151 
UpdateAccessibilityAttr()2152 void RenderList::UpdateAccessibilityAttr()
2153 {
2154     if (!component_) {
2155         LOGE("RenderList: component is null.");
2156         return;
2157     }
2158 
2159     auto accessibilityNode = GetAccessibilityNode().Upgrade();
2160     if (!accessibilityNode) {
2161         return;
2162     }
2163 
2164     auto collectionInfo = accessibilityNode->GetCollectionInfo();
2165     size_t count = TotalCount() > 0 ? TotalCount() : 1;
2166     if (vertical_) {
2167         collectionInfo.rows = static_cast<int32_t>(count);
2168         collectionInfo.columns = 1;
2169     } else {
2170         collectionInfo.rows = 1;
2171         collectionInfo.columns = static_cast<int32_t>(count);
2172     }
2173     accessibilityNode->SetCollectionInfo(collectionInfo);
2174     accessibilityNode->SetScrollableState(true);
2175     accessibilityNode->SetActionScrollForward([weakList = AceType::WeakClaim(this)]() {
2176         auto list = weakList.Upgrade();
2177         if (list) {
2178             LOGI("Trigger ScrollForward by Accessibility.");
2179             return list->HandleActionScroll(true);
2180         }
2181         return false;
2182     });
2183     accessibilityNode->SetActionScrollBackward([weakList = AceType::WeakClaim(this)]() {
2184         auto list = weakList.Upgrade();
2185         if (list) {
2186             LOGI("Trigger ScrollBackward by Accessibility.");
2187             return list->HandleActionScroll(false);
2188         }
2189         return false;
2190     });
2191 
2192     accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
2193     accessibilityNode->AddSupportAction(AceAction::ACTION_SCROLL_BACKWARD);
2194 
2195     scrollFinishEventBack_ = [weakList = AceType::WeakClaim(this)] {
2196         auto list = weakList.Upgrade();
2197         if (list) {
2198             list->ModifyActionScroll();
2199         }
2200     };
2201 }
2202 
UpdateAccessibilityScrollAttr()2203 void RenderList::UpdateAccessibilityScrollAttr()
2204 {
2205     auto accessibilityNode = GetAccessibilityNode().Upgrade();
2206     if (accessibilityNode) {
2207         accessibilityNode->SetListBeginIndex(firstDisplayIndex_);
2208         accessibilityNode->SetListEndIndex(lastDisplayIndex_);
2209         accessibilityNode->SetListItemCounts(items_.size());
2210     }
2211 }
2212 
UpdateAccessibilityVisible()2213 void RenderList::UpdateAccessibilityVisible()
2214 {
2215     auto accessibilityNode = GetAccessibilityNode().Upgrade();
2216     if (!accessibilityNode) {
2217         return;
2218     }
2219     Offset globalOffset = GetGlobalOffset();
2220     Rect listItemRect;
2221     Rect viewPortRect = Rect(globalOffset, GetLayoutSize());
2222     for (const auto& listItem : items_) {
2223         if (!listItem || listItem->GetChildren().empty()) {
2224             continue;
2225         }
2226         // RenderListItem's accessibility node is List's in v2, see ViewStackProcessor::WrapComponents() and
2227         // RenderElement::SetAccessibilityNode
2228         auto listItemWithAccessibilityNode = listItem->GetFirstChild();
2229         auto node = listItemWithAccessibilityNode->GetAccessibilityNode().Upgrade();
2230         if (!node) {
2231             continue;
2232         }
2233         bool visible = GetVisible();
2234         if (visible) {
2235             listItemRect.SetSize(listItem->GetLayoutSize());
2236             listItemRect.SetOffset(globalOffset + listItem->GetPosition());
2237             visible = listItemRect.IsIntersectWith(viewPortRect);
2238         }
2239         listItemWithAccessibilityNode->SetAccessibilityVisible(visible);
2240         if (visible) {
2241             Rect clampRect = listItemRect.Constrain(viewPortRect);
2242             listItemWithAccessibilityNode->SetAccessibilityRect(clampRect);
2243         } else {
2244             listItem->NotifyPaintFinish();
2245         }
2246     }
2247 }
2248 
ActionByScroll(bool forward,ScrollEventBack scrollEventBack)2249 bool RenderList::ActionByScroll(bool forward, ScrollEventBack scrollEventBack)
2250 {
2251     LOGI("Handle action by Scroll.");
2252     auto node = GetParent().Upgrade();
2253     while (node) {
2254         auto scroll = AceType::DynamicCast<RenderSingleChildScroll>(node);
2255         if (!scroll) {
2256             node = node->GetParent().Upgrade();
2257             continue;
2258         }
2259 
2260         scroll->ScrollPage(!forward, true, scrollEventBack);
2261         return true;
2262     }
2263     return false;
2264 }
2265 
HandleActionScroll(bool forward)2266 bool RenderList::HandleActionScroll(bool forward)
2267 {
2268     if (isActionByScroll_) {
2269         return ActionByScroll(forward, scrollFinishEventBack_);
2270     }
2271 
2272     if (forward) {
2273         JumpToIndex(lastDisplayIndex_);
2274     } else {
2275         JumpToIndex(startIndex_);
2276     }
2277     if (scrollFinishEventBack_) {
2278         scrollFinishEventBack_();
2279     }
2280     return true;
2281 }
2282 
ModifyActionScroll()2283 void RenderList::ModifyActionScroll()
2284 {
2285     hasActionScroll_ = true;
2286 }
2287 
OnPaintFinish()2288 void RenderList::OnPaintFinish()
2289 {
2290     if (!hasActionScroll_) {
2291         return;
2292     }
2293 
2294     hasActionScroll_ = false;
2295     auto context = context_.Upgrade();
2296     if (!context) {
2297         LOGE("RenderList: context is null.");
2298         return;
2299     }
2300 
2301     AccessibilityEvent scrollEvent;
2302     scrollEvent.nodeId = GetAccessibilityNodeId();
2303     scrollEvent.eventType = "scrollend";
2304     context->SendEventToAccessibility(scrollEvent);
2305 }
2306 
IsUseOnly()2307 bool RenderList::IsUseOnly()
2308 {
2309     return true;
2310 }
2311 
PrepareRawRecognizer()2312 bool RenderList::PrepareRawRecognizer()
2313 {
2314     if (rawRecognizer_) {
2315         return true;
2316     }
2317 
2318     rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
2319     auto weak = AceType::WeakClaim(this);
2320     rawRecognizer_->SetOnTouchDown([weak](const TouchEventInfo& info) {
2321         if (info.GetTouches().empty()) {
2322             return;
2323         }
2324         auto spThis = weak.Upgrade();
2325         if (spThis) {
2326             spThis->lastPos_ = spThis->GetMainAxis(info.GetTouches().front().GetLocalLocation());
2327         }
2328     });
2329     rawRecognizer_->SetOnTouchMove([weak](const TouchEventInfo& info) {
2330         if (info.GetTouches().empty()) {
2331             return;
2332         }
2333 
2334         auto spThis = weak.Upgrade();
2335         if (!spThis || !spThis->selectedItem_) {
2336             return;
2337         }
2338         double currentPos = spThis->GetMainAxis(info.GetTouches().front().GetLocalLocation());
2339         spThis->OnSelectedItemMove(currentPos);
2340     });
2341     rawRecognizer_->SetOnTouchUp([weak](const TouchEventInfo& info) {
2342         auto spThis = weak.Upgrade();
2343         if (spThis) {
2344             spThis->OnSelectedItemStopMoving(false);
2345         }
2346     });
2347     rawRecognizer_->SetOnTouchCancel([weak](const TouchEventInfo& info) {
2348         auto spThis = weak.Upgrade();
2349         if (spThis) {
2350             spThis->OnSelectedItemStopMoving(true);
2351         }
2352     });
2353 
2354     return true;
2355 }
2356 
OnSelectedItemMove(double position)2357 void RenderList::OnSelectedItemMove(double position)
2358 {
2359     double deltaPos = position - lastPos_;
2360 
2361     movingForward_ = LessOrEqual(deltaPos, 0.0);
2362     selectedItemMainAxis_ += deltaPos;
2363     deltaPos = -deltaPos;
2364     if (LessOrEqual(selectedItemMainAxis_, 0.0)) {
2365         selectedItemMainAxis_ = 0.0;
2366     } else {
2367         double maxMainSize = GetMainSize(GetLayoutSize());
2368         double mainSize = GetMainSize(selectedItem_->GetLayoutSize());
2369         if (GreatOrEqual(selectedItemMainAxis_ + mainSize, maxMainSize)) {
2370             selectedItemMainAxis_ = maxMainSize - mainSize;
2371         } else {
2372             deltaPos = 0.0;
2373             lastPos_ = position;
2374         }
2375     }
2376 
2377     if (!NearZero(deltaPos)) {
2378         currentOffset_ += deltaPos;
2379         autoScrollingForItemMove_ = true;
2380     }
2381 
2382     MarkNeedLayout();
2383 }
2384 
OnSelectedItemStopMoving(bool canceled)2385 void RenderList::OnSelectedItemStopMoving(bool canceled)
2386 {
2387     if (!canceled && targetIndex_ != selectedItemIndex_) {
2388         auto from = static_cast<int32_t>(selectedItemIndex_);
2389         auto to = static_cast<int32_t>(targetIndex_);
2390         LOGI("Moving item from %{private}d to %{private}d", from, to);
2391         if (!ResumeEventCallback(component_, &ListComponent::GetOnItemMove, false, from, to)) {
2392             LOGI("User canceled, stop moving item");
2393         }
2394     }
2395 
2396     if (selectedItemIndex_ < startIndex_ || selectedItemIndex_ >= startIndex_ + items_.size()) {
2397         RecycleListItem(selectedItemIndex_);
2398     }
2399 
2400     targetIndex_ = INITIAL_CHILD_INDEX;
2401     selectedItemIndex_ = INITIAL_CHILD_INDEX;
2402     selectedItem_ = nullptr;
2403     MarkNeedLayout();
2404 }
2405 
CreateDragDropRecognizer()2406 void RenderList::CreateDragDropRecognizer()
2407 {
2408     if (dragDropGesture_) {
2409         return;
2410     }
2411 
2412     auto longPressRecognizer =
2413         AceType::MakeRefPtr<OHOS::Ace::LongPressRecognizer>(context_, DEFAULT_DURATION, DEFAULT_FINGERS, false);
2414     longPressRecognizer->SetOnAction([weakRenderList = AceType::WeakClaim(this)](const GestureEvent& info) {
2415         auto renderList = weakRenderList.Upgrade();
2416         if (!renderList) {
2417             LOGE("LongPress action RenderList is null.");
2418             return;
2419         }
2420         renderList->scrollable_->MarkAvailable(false);
2421     });
2422     PanDirection panDirection;
2423     auto panRecognizer =
2424         AceType::MakeRefPtr<OHOS::Ace::PanRecognizer>(context_, DEFAULT_FINGERS, panDirection, DEFAULT_DISTANCE);
2425     panRecognizer->SetOnActionStart([weakRenderList = AceType::WeakClaim(this), context = context_,
2426                                         onItemDragStart = onItemDragStart_](const GestureEvent& info) {
2427         if (onItemDragStart) {
2428             auto pipelineContext = context.Upgrade();
2429             if (!pipelineContext) {
2430                 LOGE("Context is null.");
2431                 return;
2432             }
2433 
2434             auto renderList = weakRenderList.Upgrade();
2435             if (!renderList) {
2436                 LOGE("RenderList is null.");
2437                 return;
2438             }
2439 
2440             ItemDragInfo dragInfo;
2441             dragInfo.SetX(info.GetGlobalPoint().GetX());
2442             dragInfo.SetY(info.GetGlobalPoint().GetY());
2443 
2444             Point point = info.GetGlobalPoint() - renderList->GetGlobalOffset();
2445             auto listItem = renderList->FindCurrentListItem(point);
2446             if (!listItem) {
2447                 LOGW("There is no listitem at the point.");
2448                 return;
2449             }
2450 
2451             if (!listItem->IsMovable()) {
2452                 LOGI("This list item is not movable.");
2453                 return;
2454             }
2455             renderList->selectedDragItem_ = listItem;
2456             renderList->selectedItemIndex_ = renderList->GetIndexByListItem(listItem);
2457             renderList->selectedDragItem_->SetHidden(true);
2458             renderList->MarkNeedLayout();
2459 
2460             auto customComponent =
2461                 DynamicCast<Component>(onItemDragStart(dragInfo, int32_t(renderList->selectedItemIndex_)));
2462             if (!customComponent) {
2463                 LOGE("Custom component is null.");
2464                 return;
2465             }
2466             auto stackElement = pipelineContext->GetLastStack();
2467             auto positionedComponent = AceType::MakeRefPtr<PositionedComponent>(customComponent);
2468             positionedComponent->SetTop(Dimension(listItem->GetGlobalOffset().GetY()));
2469             positionedComponent->SetLeft(Dimension(listItem->GetGlobalOffset().GetX()));
2470             renderList->SetBetweenItemAndBuilder(
2471                 Offset(info.GetGlobalPoint().GetX() - listItem->GetGlobalOffset().GetX(),
2472                     info.GetGlobalPoint().GetY() - listItem->GetGlobalOffset().GetY()));
2473 
2474             auto updatePosition = [weak = weakRenderList](
2475                                       const std::function<void(const Dimension&, const Dimension&)>& func) {
2476                 auto renderList = weak.Upgrade();
2477                 if (!renderList) {
2478                     return;
2479                 }
2480                 renderList->SetUpdateBuilderFuncId(func);
2481             };
2482 
2483             positionedComponent->SetUpdatePositionFuncId(updatePosition);
2484             stackElement->PushComponent(positionedComponent);
2485             renderList->hasDragItem_ = true;
2486         }
2487     });
2488     panRecognizer->SetOnActionUpdate(
2489         [weakRenderList = AceType::WeakClaim(this), context = context_](const GestureEvent& info) {
2490             auto pipelineContext = context.Upgrade();
2491             if (!pipelineContext) {
2492                 LOGE("Context is null.");
2493                 return;
2494             }
2495 
2496             auto renderList = weakRenderList.Upgrade();
2497             if (!renderList) {
2498                 LOGE("RenderList is null.");
2499                 return;
2500             }
2501 
2502             ItemDragInfo dragInfo;
2503             dragInfo.SetX(info.GetGlobalPoint().GetX());
2504             dragInfo.SetY(info.GetGlobalPoint().GetY());
2505 
2506             Point point = info.GetGlobalPoint() - renderList->GetBetweenItemAndBuilder();
2507             if (renderList->GetUpdateBuilderFuncId()) {
2508                 renderList->GetUpdateBuilderFuncId()(Dimension(point.GetX()), Dimension(point.GetY()));
2509             }
2510 
2511             auto targetRenderlist = renderList->FindTargetRenderNode<V2::RenderList>(pipelineContext, info);
2512             auto preTargetRenderlist = renderList->GetPreTargetRenderList();
2513             if (preTargetRenderlist == targetRenderlist) {
2514                 if (targetRenderlist && targetRenderlist->GetOnItemDragMove()) {
2515                     Point point = info.GetGlobalPoint() - targetRenderlist->GetGlobalOffset();
2516                     auto newListItem = targetRenderlist->FindCurrentListItem(point);
2517                     if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(newListItem)) > -1) {
2518                         renderList->insertItemIndex_ = targetRenderlist->GetIndexByListItem(newListItem);
2519                     }
2520                     if (targetRenderlist == renderList) {
2521                         (targetRenderlist->GetOnItemDragMove())(dragInfo,
2522                             static_cast<int32_t>(renderList->selectedItemIndex_), renderList->insertItemIndex_);
2523                     } else {
2524                         (targetRenderlist->GetOnItemDragMove())(dragInfo, -1, renderList->insertItemIndex_);
2525                     }
2526                 }
2527                 return;
2528             }
2529             if (preTargetRenderlist) {
2530                 if (preTargetRenderlist->GetOnItemDragLeave()) {
2531                     (preTargetRenderlist->GetOnItemDragLeave())(
2532                         dragInfo, static_cast<int32_t>(renderList->selectedItemIndex_));
2533                 }
2534             }
2535             if (targetRenderlist) {
2536                 if (targetRenderlist->GetOnItemDragEnter()) {
2537                     (targetRenderlist->GetOnItemDragEnter())(dragInfo);
2538                 }
2539             }
2540             renderList->SetPreTargetRenderList(targetRenderlist);
2541         });
2542     panRecognizer->SetOnActionEnd([weakRenderList = AceType::WeakClaim(this), context = context_](
2543                                       const GestureEvent& info) {
2544         auto pipelineContext = context.Upgrade();
2545         if (!pipelineContext) {
2546             LOGE("Context is null.");
2547             return;
2548         }
2549 
2550         auto renderList = weakRenderList.Upgrade();
2551         if (!renderList) {
2552             LOGE("RenderList is null.");
2553             return;
2554         }
2555         if (!renderList->selectedDragItem_ || !renderList->selectedDragItem_->IsMovable()) {
2556             return;
2557         }
2558 
2559         ItemDragInfo dragInfo;
2560         dragInfo.SetX(info.GetGlobalPoint().GetX());
2561         dragInfo.SetY(info.GetGlobalPoint().GetY());
2562         if (renderList->hasDragItem_) {
2563             auto stackElement = pipelineContext->GetLastStack();
2564             stackElement->PopComponent();
2565             renderList->hasDragItem_ = false;
2566         }
2567 
2568         ACE_DCHECK(renderList->GetPreTargetRenderList() ==
2569                    renderList->FindTargetRenderNode<V2::RenderList>(pipelineContext, info));
2570         auto targetRenderlist = renderList->GetPreTargetRenderList();
2571         if (!targetRenderlist) {
2572             (renderList->GetOnItemDrop())(dragInfo, static_cast<int32_t>(renderList->selectedItemIndex_), -1, true);
2573             renderList->SetPreTargetRenderList(nullptr);
2574             renderList->selectedDragItem_->SetHidden(false);
2575             renderList->MarkNeedLayout();
2576             return;
2577         }
2578 
2579         renderList->selectedDragItem_->SetHidden(false);
2580         if (targetRenderlist->GetOnItemDrop()) {
2581             Point point = info.GetGlobalPoint() - targetRenderlist->GetGlobalOffset();
2582             auto newListItem = targetRenderlist->FindCurrentListItem(point);
2583             if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(newListItem)) > -1) {
2584                 renderList->insertItemIndex_ = static_cast<size_t>(targetRenderlist->GetIndexByListItem(newListItem));
2585             }
2586             if (targetRenderlist == renderList) {
2587                 int32_t from = static_cast<int32_t>(renderList->selectedItemIndex_);
2588                 int32_t to = static_cast<int32_t>(renderList->insertItemIndex_);
2589                 auto moveRes =
2590                     ResumeEventCallback(renderList->component_, &ListComponent::GetOnItemMove, true, from, to);
2591                 (targetRenderlist->GetOnItemDrop())(dragInfo, from, to, moveRes);
2592                 renderList->MarkNeedLayout();
2593             } else {
2594                 (targetRenderlist->GetOnItemDrop())(dragInfo, -1, renderList->insertItemIndex_, true);
2595                 targetRenderlist->MarkNeedLayout();
2596             }
2597         }
2598         renderList->SetPreTargetRenderList(nullptr);
2599         renderList->scrollable_->MarkAvailable(true);
2600     });
2601     panRecognizer->SetOnActionCancel([weakRenderList = AceType::WeakClaim(this), context = context_]() {
2602         auto pipelineContext = context.Upgrade();
2603         if (!pipelineContext) {
2604             LOGE("Context is null.");
2605             return;
2606         }
2607 
2608         auto renderList = weakRenderList.Upgrade();
2609         if (!renderList) {
2610             LOGE("RenderList is null.");
2611             return;
2612         }
2613         if (!renderList->selectedDragItem_ || !renderList->selectedDragItem_->IsMovable()) {
2614             return;
2615         }
2616 
2617         if (renderList->hasDragItem_) {
2618             auto stackElement = pipelineContext->GetLastStack();
2619             stackElement->PopComponent();
2620             renderList->hasDragItem_ = false;
2621         }
2622 
2623         renderList->SetPreTargetRenderList(nullptr);
2624         renderList->selectedDragItem_->SetHidden(false);
2625         renderList->scrollable_->MarkAvailable(true);
2626         renderList->MarkNeedLayout();
2627     });
2628     std::vector<RefPtr<GestureRecognizer>> recognizers { longPressRecognizer, panRecognizer };
2629     dragDropGesture_ = AceType::MakeRefPtr<OHOS::Ace::SequencedRecognizer>(GetContext(), recognizers);
2630 }
2631 
FindCurrentListItem(const Point & point)2632 RefPtr<RenderListItem> RenderList::FindCurrentListItem(const Point& point)
2633 {
2634     const auto& children = GetChildren();
2635     for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
2636         auto& child = *iter;
2637         for (auto& rect : child->GetTouchRectList()) {
2638             if (rect.IsInRegion(point)) {
2639                 return AceType::DynamicCast<RenderListItem>(child);
2640             }
2641         }
2642     }
2643     return nullptr;
2644 }
2645 
CalculateSelectedIndex(const RefPtr<RenderList> targetRenderlist,const GestureEvent & info,Size & selectedItemSize)2646 size_t RenderList::CalculateSelectedIndex(
2647     const RefPtr<RenderList> targetRenderlist, const GestureEvent& info, Size& selectedItemSize)
2648 {
2649     auto listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), info);
2650     if (listItem) {
2651         selectedItemSize = listItem->GetLayoutSize();
2652         return targetRenderlist->GetIndexByListItem(listItem);
2653     }
2654 
2655     return DEFAULT_INDEX;
2656 }
2657 
CalculateInsertIndex(const RefPtr<RenderList> targetRenderlist,const GestureEvent & info,Size selectedItemSize)2658 int32_t RenderList::CalculateInsertIndex(
2659     const RefPtr<RenderList> targetRenderlist, const GestureEvent& info, Size selectedItemSize)
2660 {
2661     if (targetRenderlist->TotalCount() == 0) {
2662         return 0;
2663     }
2664 
2665     auto listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), info);
2666     if (!listItem) {
2667         GestureEvent newEvent = info;
2668         while (!listItem) {
2669             if (FindTargetRenderNode<V2::RenderList>(context_.Upgrade(), newEvent) != targetRenderlist) {
2670                 break;
2671             }
2672             double newX = vertical_ ? newEvent.GetGlobalPoint().GetX()
2673                                     : newEvent.GetGlobalPoint().GetX() - selectedItemSize.Width();
2674             double newY = vertical_ ? newEvent.GetGlobalPoint().GetY() - selectedItemSize.Height()
2675                                     : newEvent.GetGlobalPoint().GetY();
2676             newEvent.SetGlobalPoint(Point(newX, newY));
2677             listItem = targetRenderlist->FindTargetRenderNode<RenderListItem>(context_.Upgrade(), newEvent);
2678         }
2679         if (!listItem) {
2680             return 0;
2681         }
2682         if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) > -1) {
2683             return static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) + 1;
2684         }
2685         return DEFAULT_INDEX_VALUE;
2686     }
2687 
2688     if (static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem)) > -1) {
2689         return static_cast<int32_t>(targetRenderlist->GetIndexByListItem(listItem));
2690     }
2691 
2692     return DEFAULT_INDEX_VALUE;
2693 }
2694 
IsAxisScrollable(AxisDirection direction)2695 bool RenderList::IsAxisScrollable(AxisDirection direction)
2696 {
2697     return (((AxisEvent::IsDirectionUp(direction) || AxisEvent::IsDirectionLeft(direction)) && !reachStart_) ||
2698             ((AxisEvent::IsDirectionDown(direction) || AxisEvent::IsDirectionRight(direction)) && !reachEnd_));
2699 }
2700 
HandleAxisEvent(const AxisEvent & event)2701 void RenderList::HandleAxisEvent(const AxisEvent& event) {}
2702 
HandleMouseEvent(const MouseEvent & event)2703 bool RenderList::HandleMouseEvent(const MouseEvent& event)
2704 {
2705     if (!isMultiSelectable_) {
2706         return false;
2707     }
2708     scrollable_->MarkAvailable(false);
2709 
2710     if (event.button == MouseButton::LEFT_BUTTON) {
2711         if (event.action == MouseAction::PRESS) {
2712             Point mousePoint(event.GetOffset().GetX(), event.GetOffset().GetY());
2713             auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2714             if (listItem && listItem->IsDragStart()) {
2715                 forbidMultiSelect_ = true;
2716             }
2717         } else if (event.action == MouseAction::RELEASE) {
2718             forbidMultiSelect_ = false;
2719         }
2720     }
2721 
2722     if (forbidMultiSelect_) {
2723         return false;
2724     }
2725 
2726     if (event.action == MouseAction::HOVER_EXIT) {
2727         mouseIsHover_ = false;
2728     } else {
2729         mouseIsHover_ = true;
2730     }
2731 
2732     auto context = context_.Upgrade();
2733     if (context) {
2734         context->SubscribeCtrlA([wp = AceType::WeakClaim(this)]() {
2735             auto sp = wp.Upgrade();
2736             if (sp) {
2737                 if (sp->mouseIsHover_ == true) {
2738                     sp->MultiSelectAllWhenCtrlA();
2739                 } else {
2740                     sp->ClearMultiSelect();
2741                     sp->MarkNeedRender();
2742                 }
2743             }
2744         });
2745     } else {
2746         LOGE("context is null");
2747         return false;
2748     }
2749 
2750     if (context->IsCtrlDown()) {
2751         if (context->IsKeyboardA()) {
2752             MultiSelectAllWhenCtrlA();
2753             return true;
2754         }
2755         HandleMouseEventWhenCtrlDown(event);
2756         return true;
2757     }
2758     selectedItemsWithCtrl_.clear();
2759 
2760     if (context->IsShiftDown()) {
2761         HandleMouseEventWhenShiftDown(event);
2762         return true;
2763     }
2764     firstItemWithShift_ = nullptr;
2765 
2766     HandleMouseEventWithoutKeyboard(event);
2767     return true;
2768 }
2769 
ClearMultiSelect()2770 void RenderList::ClearMultiSelect()
2771 {
2772     for (const auto& listItem : items_) {
2773         if (!listItem) {
2774             continue;
2775         }
2776         listItem->MarkIsSelected(false);
2777     }
2778 }
2779 
MultiSelectWithoutKeyboard(const Rect & selectedZone)2780 void RenderList::MultiSelectWithoutKeyboard(const Rect& selectedZone)
2781 {
2782     if (!selectedZone.IsValid()) {
2783         Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2784         auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2785         if (!listItem) {
2786             return;
2787         }
2788         if (!listItem->GetSelectable()) {
2789             return;
2790         }
2791         listItem->MarkIsSelected(true);
2792         if (listItem->GetOnSelectId()) {
2793             (listItem->GetOnSelectId())(listItem->IsSelected());
2794         }
2795         return;
2796     }
2797 
2798     for (const auto& listItem : items_) {
2799         if (!listItem) {
2800             continue;
2801         }
2802         if (!listItem->GetSelectable()) {
2803             continue;
2804         }
2805         if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2806             listItem->MarkIsSelected(false);
2807             if (listItem->GetOnSelectId()) {
2808                 (listItem->GetOnSelectId())(listItem->IsSelected());
2809             }
2810             continue;
2811         }
2812         listItem->MarkIsSelected(true);
2813         if (listItem->GetOnSelectId()) {
2814             (listItem->GetOnSelectId())(listItem->IsSelected());
2815         }
2816     }
2817 }
2818 
HandleMouseEventWithoutKeyboard(const MouseEvent & event)2819 void RenderList::HandleMouseEventWithoutKeyboard(const MouseEvent& event)
2820 {
2821     if (event.button == MouseButton::LEFT_BUTTON) {
2822         if (event.action == MouseAction::PRESS) {
2823             ClearMultiSelect();
2824             mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2825             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2826             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2827             MultiSelectWithoutKeyboard(selectedZone);
2828             MarkNeedRender();
2829         } else if (event.action == MouseAction::MOVE) {
2830             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2831             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2832             MultiSelectWithoutKeyboard(selectedZone);
2833             MarkNeedRender();
2834         } else if (event.action == MouseAction::RELEASE) {
2835             mouseStartOffset_ = Offset(0.0, 0.0);
2836             mouseEndOffset_ = Offset(0.0, 0.0);
2837             MarkNeedRender();
2838         }
2839     }
2840 }
2841 
GetPressItemWhenShiftDown(const Rect & selectedZone)2842 RefPtr<RenderListItem> RenderList::GetPressItemWhenShiftDown(const Rect& selectedZone)
2843 {
2844     if (!selectedZone.IsValid()) {
2845         Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2846         auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2847         if (!listItem) {
2848             return nullptr;
2849         }
2850         if (!listItem->GetSelectable()) {
2851             return nullptr;
2852         }
2853         return listItem;
2854     }
2855     return nullptr;
2856 }
2857 
MultiSelectWhenShiftDown(const Rect & selectedZone)2858 void RenderList::MultiSelectWhenShiftDown(const Rect& selectedZone)
2859 {
2860     for (const auto& listItem : items_) {
2861         if (!listItem) {
2862             continue;
2863         }
2864         if (!listItem->GetSelectable()) {
2865             continue;
2866         }
2867         if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2868             continue;
2869         }
2870         listItem->MarkIsSelected(true);
2871         if (listItem->GetOnSelectId()) {
2872             (listItem->GetOnSelectId())(listItem->IsSelected());
2873         }
2874     }
2875 }
2876 
HandleMouseEventWhenShiftDown(const MouseEvent & event)2877 void RenderList::HandleMouseEventWhenShiftDown(const MouseEvent& event)
2878 {
2879     if (event.button == MouseButton::LEFT_BUTTON) {
2880         if (event.action == MouseAction::PRESS) {
2881             mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2882             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2883             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2884             if (firstItemWithShift_ == nullptr) {
2885                 firstItemWithShift_ = GetPressItemWhenShiftDown(selectedZone);
2886             }
2887             secondItemWithShift_ = GetPressItemWhenShiftDown(selectedZone);
2888             MultiSelectAllInRange(firstItemWithShift_, secondItemWithShift_);
2889             MarkNeedRender();
2890         } else if (event.action == MouseAction::MOVE) {
2891             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
2892             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
2893             MultiSelectWhenShiftDown(selectedZone);
2894             MarkNeedRender();
2895         } else if (event.action == MouseAction::RELEASE) {
2896             mouseStartOffset_ = Offset(0.0, 0.0);
2897             mouseEndOffset_ = Offset(0.0, 0.0);
2898             MarkNeedRender();
2899         }
2900     }
2901 }
2902 
MultiSelectAllInRange(const RefPtr<RenderListItem> & firstItem,const RefPtr<RenderListItem> & secondItem)2903 void RenderList::MultiSelectAllInRange(
2904     const RefPtr<RenderListItem>& firstItem, const RefPtr<RenderListItem>& secondItem)
2905 {
2906     ClearMultiSelect();
2907     if (!firstItem) {
2908         return;
2909     }
2910 
2911     if (!secondItem) {
2912         firstItem->MarkIsSelected(true);
2913         if (firstItem->GetOnSelectId()) {
2914             (firstItem->GetOnSelectId())(firstItem->IsSelected());
2915         }
2916         return;
2917     }
2918 
2919     auto fromItemIndex = std::min(GetIndexByListItem(firstItem), GetIndexByListItem(secondItem));
2920     auto toItemIndex = std::max(GetIndexByListItem(firstItem), GetIndexByListItem(secondItem));
2921 
2922     for (const auto& listItem : items_) {
2923         if (!listItem) {
2924             continue;
2925         }
2926         if (!listItem->GetSelectable()) {
2927             continue;
2928         }
2929 
2930         auto nowIndex = GetIndexByListItem(listItem);
2931         if (nowIndex <= toItemIndex && nowIndex >= fromItemIndex) {
2932             listItem->MarkIsSelected(true);
2933             if (listItem->GetOnSelectId()) {
2934                 (listItem->GetOnSelectId())(listItem->IsSelected());
2935             }
2936         }
2937     }
2938 }
2939 
MultiSelectWhenCtrlDown(const Rect & selectedZone)2940 void RenderList::MultiSelectWhenCtrlDown(const Rect& selectedZone)
2941 {
2942     if (!selectedZone.IsValid()) {
2943         Point mousePoint(selectedZone.GetOffset().GetX(), selectedZone.GetOffset().GetY());
2944         auto listItem = FindChildNodeOfClass<RenderListItem>(mousePoint, mousePoint);
2945         if (!listItem) {
2946             return;
2947         }
2948         if (!listItem->GetSelectable()) {
2949             return;
2950         }
2951 
2952         if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2953             listItem->MarkIsSelected(false);
2954         } else {
2955             listItem->MarkIsSelected(true);
2956         }
2957 
2958         if (listItem->GetOnSelectId()) {
2959             (listItem->GetOnSelectId())(listItem->IsSelected());
2960         }
2961         return;
2962     }
2963 
2964     for (const auto& listItem : items_) {
2965         if (!listItem) {
2966             continue;
2967         }
2968         if (!listItem->GetSelectable()) {
2969             continue;
2970         }
2971         if (!selectedZone.IsIntersectWith(listItem->GetPaintRect())) {
2972             if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2973                 listItem->MarkIsSelected(true);
2974             } else {
2975                 listItem->MarkIsSelected(false);
2976             }
2977             if (listItem->GetOnSelectId()) {
2978                 (listItem->GetOnSelectId())(listItem->IsSelected());
2979             }
2980             continue;
2981         }
2982 
2983         if (selectedItemsWithCtrl_.find(listItem) != selectedItemsWithCtrl_.end()) {
2984             listItem->MarkIsSelected(false);
2985         } else {
2986             listItem->MarkIsSelected(true);
2987         }
2988 
2989         if (listItem->GetOnSelectId()) {
2990             (listItem->GetOnSelectId())(listItem->IsSelected());
2991         }
2992     }
2993 }
2994 
HandleMouseEventWhenCtrlDown(const MouseEvent & event)2995 void RenderList::HandleMouseEventWhenCtrlDown(const MouseEvent& event)
2996 {
2997     if (event.button == MouseButton::LEFT_BUTTON) {
2998         if (event.action == MouseAction::PRESS) {
2999             mouseStartOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3000             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3001             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
3002             MultiSelectWhenCtrlDown(selectedZone);
3003             MarkNeedRender();
3004         } else if (event.action == MouseAction::MOVE) {
3005             mouseEndOffset_ = event.GetOffset() - GetPaintRect().GetOffset();
3006             auto selectedZone = ComputeSelectedZone(mouseStartOffset_, mouseEndOffset_);
3007             MultiSelectWhenCtrlDown(selectedZone);
3008             MarkNeedRender();
3009         } else if (event.action == MouseAction::RELEASE) {
3010             mouseStartOffset_ = Offset(0.0, 0.0);
3011             mouseEndOffset_ = Offset(0.0, 0.0);
3012             MarkNeedRender();
3013             CollectSelectedItems();
3014         }
3015     }
3016 }
3017 
CollectSelectedItems()3018 void RenderList::CollectSelectedItems()
3019 {
3020     selectedItemsWithCtrl_.clear();
3021     for (const auto& listItem : items_) {
3022         if (!listItem) {
3023             continue;
3024         }
3025         if (!listItem->GetSelectable()) {
3026             continue;
3027         }
3028         if (listItem->IsSelected()) {
3029             selectedItemsWithCtrl_.insert(listItem);
3030         }
3031     }
3032 }
3033 
MultiSelectAllWhenCtrlA()3034 void RenderList::MultiSelectAllWhenCtrlA()
3035 {
3036     for (const auto& listItem : items_) {
3037         if (!listItem) {
3038             continue;
3039         }
3040         if (!listItem->GetSelectable()) {
3041             continue;
3042         }
3043         listItem->MarkIsSelected(true);
3044         if (listItem->GetOnSelectId()) {
3045             (listItem->GetOnSelectId())(listItem->IsSelected());
3046         }
3047     }
3048     MarkNeedRender();
3049 }
3050 
RequestNextFocus(bool vertical,bool reverse)3051 int32_t RenderList::RequestNextFocus(bool vertical, bool reverse)
3052 {
3053     bool rightToLeft_ = false;
3054     int32_t moveStep = DIRECTION_MAP.at(rightToLeft_).at(vertical_).at(vertical).at(reverse);
3055     if (moveStep == STEP_INVALID) {
3056         return -1;
3057     }
3058     focusIndex_ += moveStep;
3059     return focusIndex_;
3060 }
3061 
ProvideRestoreInfo()3062 std::string RenderList::ProvideRestoreInfo()
3063 {
3064     if (firstDisplayIndex_ > 0) {
3065         return std::to_string(firstDisplayIndex_);
3066     }
3067     return "";
3068 }
3069 
ApplyRestoreInfo()3070 void RenderList::ApplyRestoreInfo()
3071 {
3072     if (GetRestoreInfo().empty()) {
3073         return;
3074     }
3075     startIndex_ = static_cast<size_t>(StringUtils::StringToInt(GetRestoreInfo()));
3076     SetRestoreInfo("");
3077 }
3078 
LayoutChild(RefPtr<RenderNode> child,double referencePos,bool forward)3079 void RenderList::LayoutChild(RefPtr<RenderNode> child, double referencePos, bool forward)
3080 {
3081     auto innerLayout = MakeInnerLayout();
3082     auto renderNode = child;
3083     RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3084     if (listItemGroup) {
3085         renderNode = listItemGroup->GetRenderNode();
3086         ListItemLayoutParam param = {
3087             .startCacheCount = (cachedCount_ > 0 && !isLaneList_) ? cachedCount_ - startCachedCount_ : 0,
3088             .endCacheCount = (cachedCount_ > 0 && !isLaneList_) ? cachedCount_ - endCachedCount_ : 0,
3089             .startMainPos = (cachedCount_ == 0 || isLaneList_) ? startMainPos_ : 0,
3090             .endMainPos = (cachedCount_ == 0 || isLaneList_) ? endMainPos_ : mainSize_,
3091             .listMainSize = mainSize_,
3092             .referencePos = referencePos,
3093             .maxLaneLength = isLaneList_ ? maxLaneLength_ : 0.0,
3094             .forwardLayout = forward,
3095             .isVertical = vertical_,
3096             .sticky = sticky_,
3097             .lanes = isLaneList_ ? lanes_ : 1,
3098             .align = component_->GetAlignListItemAlign(),
3099         };
3100         listItemGroup->SetItemGroupLayoutParam(param);
3101         listItemGroup->SetNeedLayoutDeep();
3102     } else if (isLaneList_) {
3103         innerLayout = MakeInnerLayoutForLane();
3104     }
3105     if (renderNode) {
3106         renderNode->Layout(innerLayout);
3107     }
3108 }
3109 
PaintChild(const RefPtr<RenderNode> & child,RenderContext & context,const Offset & offset)3110 void RenderList::PaintChild(const RefPtr<RenderNode>& child, RenderContext& context, const Offset& offset)
3111 {
3112     RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3113     if (listItemGroup) {
3114         auto renderNode = listItemGroup->GetRenderNode();
3115         RenderNode::PaintChild(renderNode, context, offset);
3116     } else {
3117         RenderNode::PaintChild(child, context, offset);
3118     }
3119 }
3120 
SetChildPosition(RefPtr<RenderNode> child,const Offset & offset)3121 void RenderList::SetChildPosition(RefPtr<RenderNode> child, const Offset& offset)
3122 {
3123     auto renderNode = child;
3124     RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3125     if (listItemGroup) {
3126         renderNode = listItemGroup->GetRenderNode();
3127     }
3128     if (renderNode) {
3129         renderNode->SetPosition(offset);
3130     }
3131 }
3132 
AddChildItem(RefPtr<RenderNode> child)3133 void RenderList::AddChildItem(RefPtr<RenderNode> child)
3134 {
3135     auto renderNode = child;
3136     RefPtr<RenderListItemGroup> listItemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3137     if (listItemGroup) {
3138         renderNode = listItemGroup->GetRenderNode();
3139     }
3140     AddChild(renderNode);
3141 }
3142 
SizeChangeOffset(double newWindowHeight)3143 void RenderList::SizeChangeOffset(double newWindowHeight)
3144 {
3145     auto context = context_.Upgrade();
3146     if (!context) {
3147         return;
3148     }
3149     auto textFieldManager = AceType::DynamicCast<TextFieldManager>(context->GetTextFieldManager());
3150     // only need to offset vertical lists
3151     if (textFieldManager && vertical_) {
3152         // only when textField is onFocus
3153         if (!textFieldManager->GetOnFocusTextField().Upgrade()) {
3154             return;
3155         }
3156         auto position = textFieldManager->GetClickPosition().GetY();
3157         double offset = newWindowHeight - position;
3158         if (LessOrEqual(offset, 0.0)) {
3159             // negative offset to scroll down
3160             currentOffset_ += offset;
3161             startIndexOffset_ += offset;
3162         }
3163     }
3164 }
3165 
AdjustForReachEnd(double mainSize,double curMainPos)3166 void RenderList::AdjustForReachEnd(double mainSize, double curMainPos)
3167 {
3168     double delta = mainSize - curMainPos;
3169     for (auto rit = items_.rbegin(); rit != items_.rend(); rit++) {
3170         auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(*rit);
3171         if (itemGroup) {
3172             double size = GetMainSize(itemGroup->GetLayoutSize());
3173             LayoutChild(itemGroup, itemGroup->GetReferencePos() + delta, itemGroup->IsForwardLayout());
3174             double newSize = GetMainSize(itemGroup->GetLayoutSize());
3175             delta -= (newSize - size);
3176         }
3177     }
3178     currentOffset_ += delta;
3179 }
3180 
AdjustForReachStart(double & curMainPos)3181 void RenderList::AdjustForReachStart(double& curMainPos)
3182 {
3183     double delta = currentOffset_;
3184     for (const auto& child : items_) {
3185         auto itemGroup = AceType::DynamicCast<RenderListItemGroup>(child);
3186         if (itemGroup) {
3187             double size = GetMainSize(itemGroup->GetLayoutSize());
3188             LayoutChild(itemGroup, itemGroup->GetReferencePos() - delta, itemGroup->IsForwardLayout());
3189             double newSize = GetMainSize(itemGroup->GetLayoutSize());
3190             delta -= (newSize - size);
3191         }
3192     }
3193     curMainPos -= delta;
3194 }
3195 
3196 } // namespace OHOS::Ace::V2
3197