• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/list/render_list.h"
17 
18 #include <algorithm>
19 
20 #include "base/log/ace_trace.h"
21 #include "base/log/log.h"
22 #include "base/utils/utils.h"
23 #include "core/components/list/grid_layout_manager.h"
24 #include "core/components/list/list_component.h"
25 #include "core/components/list/list_layout_manager.h"
26 #include "core/components/list/list_watch_layout_manager.h"
27 #include "core/components/list/render_list_item.h"
28 #include "core/components/list/render_list_item_group.h"
29 #include "core/components/scroll/render_multi_child_scroll.h"
30 
31 namespace OHOS::Ace {
32 namespace {
33 
34 constexpr double ITEM_STICKY_OFFSET = 1.0;
35 
36 } // namespace
37 
Update(const RefPtr<Component> & component)38 void RenderList::Update(const RefPtr<Component>& component)
39 {
40     const RefPtr<ListComponent> list = AceType::DynamicCast<ListComponent>(component);
41     if (!list) {
42         return;
43     }
44 
45     needRefresh_ = true;
46     int32_t preColumnCount = columnCount_;
47     direction_ = list->GetDirection();
48     maxCount_ = list->GetTotalCount();
49     itemsCount_ = list->GetItemsCount();
50     cachedCount_ = list->GetCachedCount();
51     beginIndex_ = list->GetBeginIndex();
52     endIndex_ = list->GetEndIndex();
53     repeatLength_ = list->GetRepeatedLength();
54     // length_ is set in ListElement::Update
55     indexOffset_ = list->GetIndexOffset();
56     crossAxisAlign_ = list->GetFlexAlign();
57     columnCount_ = list->GetColumnCount();
58     columnExtent_ = list->GetColumnExtent();
59     listWidth_ = list->GetWidth();
60     listHeight_ = list->GetHeight();
61     controller_ = list->GetPositionController();
62     itemExtent_ = list->GetItemExtent();
63     rightToLeft_ = list->GetRightToLeft();
64     supportItemCenter_ = list->GetSupportItemCenter();
65     updateEffect_ = list->GetUpdateEffect();
66     pageReady_ = list->GetPageReady();
67     itemScale_ = list->IsItemScale();
68     isCenterLayout_ = list->IsCenterLayout();
69     const static int32_t PLATFORM_VERSION_FIVE = 5;
70     auto context = GetContext().Upgrade();
71     if (context && context->GetMinPlatformVersion() <= PLATFORM_VERSION_FIVE) {
72         // api 5 not have center layout use item scale
73         isCenterLayout_ = itemScale_;
74     }
75 
76     if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
77         chainAnimation_ = list->GetChainAnimation();
78     } else {
79         chainAnimation_ = false;
80     }
81     if (chainAnimation_) {
82         chainProperty_ = list->GetChainProperty();
83     }
84 
85     if (!layoutManager_ || preColumnCount != columnCount_) {
86         if (columnCount_ <= 1) {
87             if (SystemProperties::GetDeviceType() == DeviceType::WATCH) {
88                 layoutManager_ = AceType::MakeRefPtr<ListWatchLayoutManager>(*this);
89             } else {
90                 layoutManager_ = AceType::MakeRefPtr<ListLayoutManager>(*this);
91             }
92         } else {
93             layoutManager_ = AceType::MakeRefPtr<GridLayoutManager>(*this);
94         }
95     }
96 
97     const auto& rotationController = list->GetRotationController();
98     if (rotationController) {
99         // list parent is scroll
100         rotationController->SetRequestRotationImpl(GetParent(), context_);
101     }
102     SetOnRotateCallback(list);
103 
104     UpdateAccessibilityAttr();
105     if (layoutManager_) {
106         layoutManager_->Update();
107     }
108     overSpringProperty_ = list->OverSpringProperty();
109 }
110 
UpdateTouchRect()111 void RenderList::UpdateTouchRect()
112 {
113     touchRect_.SetSize(viewPort_);
114     touchRect_.SetOffset(GetPosition());
115     touchRectList_.emplace_back(touchRect_);
116     SetTouchRectList(touchRectList_);
117 }
118 
MarkNeedRefresh()119 void RenderList::MarkNeedRefresh()
120 {
121     layoutManager_->MarkNeedRefresh();
122 }
123 
RefreshOffset(double offset)124 void RenderList::RefreshOffset(double offset)
125 {
126     RefPtr<RenderNode> parentNode = GetParent().Upgrade();
127     RefPtr<RenderMultiChildScroll> scroll = AceType::DynamicCast<RenderMultiChildScroll>(parentNode);
128     if (!scroll) {
129         LOGE("Parent scroll is null, RefreshOffset failed.");
130         return;
131     }
132     scroll->RefreshOffset(offset);
133 }
134 
RefreshScrollExtent()135 void RenderList::RefreshScrollExtent()
136 {
137     RefPtr<RenderNode> parentNode = GetParent().Upgrade();
138     RefPtr<RenderMultiChildScroll> scroll = AceType::DynamicCast<RenderMultiChildScroll>(parentNode);
139     if (!scroll) {
140         LOGE("Parent scroll is null, RefreshScrollExtent failed.");
141         return;
142     }
143     scroll->CalculateMainScrollExtent();
144 }
145 
PerformLayout()146 void RenderList::PerformLayout()
147 {
148     layoutManager_->PerformLayout();
149     int32_t firstIndex = GetIndexByPosition(-GetMainPosition(currentOffset_));
150     if (firstIndex >= 0) {
151         firstItemIndex_ = firstIndex;
152         if (controller_) {
153             controller_->SetFirstItemIndex(firstIndex);
154         }
155     }
156     auto slipFactor = layoutManager_->GetSlipFactor();
157     if (slipFactor > 0.0 && slipFactorSetting_) {
158         slipFactorSetting_(slipFactor);
159     }
160 }
161 
OnPaintFinish()162 void RenderList::OnPaintFinish()
163 {
164     RefPtr<RenderListItem> listItem;
165     Offset globalOffset = GetGlobalOffset();
166     Rect listItemRect;
167     Rect viewPortRect = Rect(globalOffset, viewPort_);
168     for (const auto& item : items_) {
169         if (!item.second || item.second->GetChildren().empty()) {
170             continue;
171         }
172         listItem = RenderListItem::GetRenderListItem(item.second);
173         if (!listItem) {
174             continue;
175         }
176         auto node = listItem->GetAccessibilityNode().Upgrade();
177         if (!node) {
178             continue;
179         }
180         bool visible = GetVisible();
181         if (visible) {
182             listItemRect.SetSize(item.second->GetLayoutSize());
183             listItemRect.SetOffset(globalOffset + item.second->GetPosition());
184             visible = listItemRect.IsIntersectWith(viewPortRect);
185         }
186         listItem->SetAccessibilityVisible(visible);
187         if (visible) {
188             Rect clampRect = listItemRect.Constrain(viewPortRect);
189             listItem->SetAccessibilityRect(clampRect);
190         } else {
191             listItem->NotifyPaintFinish();
192         }
193     }
194 }
195 
BuildNextItem(double start,double end,Offset position,Size viewPort)196 void RenderList::BuildNextItem(double start, double end, Offset position, Size viewPort)
197 {
198     ACE_FUNCTION_TRACE();
199     int32_t maxIndex = GetCurrentMaxIndex();
200     int32_t itemIndex = GetIndexByPosition(start);
201     double offset = GetMainPosition(position);
202     auto itemChild = GetChildByIndex(itemIndex);
203     double curMainSize = GetItemPosition(itemIndex);
204     if (offset < 0.0) {
205         bool needCreateNewItem = false;
206         while (itemChild) {
207             curMainSize += GetMainSize(itemChild->GetLayoutSize());
208             if (curMainSize >= end) {
209                 break;
210             } else if (itemIndex < maxIndex) {
211                 itemChild = GetChildByIndex(++itemIndex);
212             } else {
213                 needCreateNewItem = true;
214                 break;
215             }
216         }
217         if (needCreateNewItem) {
218             MarkNeedLayout();
219             itemChild = GetChildByIndex(++itemIndex);
220             layoutManager_->LayoutMore(end - curMainSize);
221         }
222     }
223 }
224 
RequestNextFocus(bool vertical,bool reverse)225 int32_t RenderList::RequestNextFocus(bool vertical, bool reverse)
226 {
227     return layoutManager_->RequestNextFocus(vertical, reverse);
228 }
229 
ListItemFocused(int32_t focusIndex)230 void RenderList::ListItemFocused(int32_t focusIndex)
231 {
232     layoutManager_->ListItemFocused(focusIndex);
233 }
234 
CalculateFocusIndexPosition()235 void RenderList::CalculateFocusIndexPosition()
236 {
237     layoutManager_->CalculateFocusIndexPosition();
238 }
239 
GetStickyMainSize(int32_t index)240 double RenderList::GetStickyMainSize(int32_t index)
241 {
242     double size = 0.0;
243     for (const auto& item : stickyItemMap_) {
244         if (item.first > index) {
245             return size;
246         } else if (item.first == index) {
247             return 0.0;
248         } else {
249             size = item.second;
250         }
251     }
252     return size;
253 }
254 
CalculateItemPosition(int32_t index,ScrollType type)255 double RenderList::CalculateItemPosition(int32_t index, ScrollType type)
256 {
257     switch (type) {
258         case ScrollType::SCROLL_INDEX: {
259             if (index >= GetCurrentMaxIndex()) {
260                 layoutManager_->LayoutToItem(index);
261                 layoutManager_->LayoutMore(GetMainSize(viewPort_));
262             }
263             double sticky = 0.0; // When exist sticky items, add sticky size when jump.
264             if (SystemProperties::GetDeviceType() != DeviceType::WATCH || !IsSupportScale()) {
265                 CalculateStickyItem(Offset(0.0, -GetItemPosition(index)));
266                 if (stickyItem_) {
267                     sticky = GetStickyMainSize(index);
268                 }
269             }
270             return std::max(GetItemPosition(index) - sticky, 0.0);
271         }
272         case ScrollType::SCROLL_PAGE_DOWN: {
273             layoutManager_->LayoutMore(GetMainSize(viewPort_));
274             double curMainSize = GetMainSize(GetLayoutSize());
275             double maxJumpSize = curMainSize - GetMainSize(viewPort_);
276             maxJumpSize += GetMainPosition(currentOffset_);
277             return std::clamp(maxJumpSize, 0.0, GetMainSize(viewPort_)) - GetMainPosition(currentOffset_);
278         }
279         case ScrollType::SCROLL_PAGE_UP: {
280             double position = -GetMainPosition(currentOffset_) - GetMainSize(viewPort_);
281             return std::max(position, 0.0);
282         }
283         case ScrollType::SCROLL_BOTTOM: {
284             layoutManager_->LayoutToItem(maxCount_);
285             double curMainSize = GetMainSize(GetLayoutSize());
286             double maxJumpSize = curMainSize - GetMainSize(viewPort_);
287             return std::max(maxJumpSize, 0.0);
288         }
289         case ScrollType::SCROLL_TOP: {
290             return GetItemPosition(0);
291         }
292         default: {
293             LOGE("Unknown type:%{public}d", type);
294             return -1.0;
295         }
296     }
297 }
298 
CalculateItemPosition(double targetPos)299 void RenderList::CalculateItemPosition(double targetPos)
300 {
301     layoutManager_->LayoutToPosition(targetPos);
302     layoutManager_->LayoutMore(GetMainSize(viewPort_));
303 }
304 
MoveItemToViewPort(double position)305 void RenderList::MoveItemToViewPort(double position)
306 {
307     layoutManager_->MoveItemToViewPort(position);
308 }
309 
MoveItemGroupToViewPort(double position,double size)310 void RenderList::MoveItemGroupToViewPort(double position, double size)
311 {
312     layoutManager_->MoveItemGroupToViewPort(position, size);
313 }
314 
ResetLayoutRange(double head,double tail,Offset position,Size viewport)315 void RenderList::ResetLayoutRange(double head, double tail, Offset position, Size viewport)
316 {
317     viewPort_ = viewport;
318     currentOffset_ = position;
319     UpdateTouchRect();
320     layoutManager_->ResetLayoutRange(head, tail, position, viewport);
321 }
322 
AddListItem(int32_t index,const RefPtr<RenderNode> & renderNode)323 void RenderList::AddListItem(int32_t index, const RefPtr<RenderNode>& renderNode)
324 {
325     if (renderNode) {
326         items_[index] = renderNode;
327     }
328 }
329 
GetChildByPosition(double position) const330 RefPtr<RenderNode> RenderList::GetChildByPosition(double position) const
331 {
332     double startPosition = 0.0;
333     double endPosition = 0.0;
334     RefPtr<RenderNode> node;
335     RefPtr<RenderListItem> listItem;
336     for (const auto& item : items_) {
337         if (!item.second || item.second->GetChildren().empty()) {
338             continue;
339         }
340         listItem = RenderListItem::GetRenderListItem(item.second);
341         if (!listItem) {
342             break;
343         }
344         startPosition = GetItemPosition(listItem->GetIndex());
345         endPosition = startPosition + GetMainSize(item.second->GetLayoutSize());
346         if ((position > startPosition && position < endPosition) || NearEqual(position, startPosition)) {
347             return item.second;
348         }
349     }
350     return node;
351 }
352 
GetNearChildByPosition(double position) const353 RefPtr<RenderNode> RenderList::GetNearChildByPosition(double position) const
354 {
355     if (!layoutManager_) {
356         LOGE("Get near child by position failed. layout manager is null.");
357         return nullptr;
358     }
359     double startPosition = 0.0;
360     double endPosition = 0.0;
361     RefPtr<RenderNode> node;
362     RefPtr<RenderListItem> listItem;
363     for (const auto& item : items_) {
364         if (!item.second || item.second->GetChildren().empty()) {
365             continue;
366         }
367         listItem = RenderListItem::GetRenderListItem(item.second);
368         if (!listItem) {
369             break;
370         }
371         layoutManager_->GetChainItemRange(listItem->GetIndex(), startPosition, endPosition);
372         if ((position > startPosition && position < endPosition) || NearEqual(position, startPosition)) {
373             return item.second;
374         }
375         node = item.second;
376     }
377     return node;
378 }
379 
FindChildByIndex(int32_t index)380 RefPtr<RenderNode> RenderList::FindChildByIndex(int32_t index)
381 {
382     auto item = items_.find(index);
383     if (item != items_.end()) {
384         return item->second;
385     } else {
386         return nullptr;
387     }
388 }
389 
GetChildByIndex(int32_t index)390 RefPtr<RenderNode> RenderList::GetChildByIndex(int32_t index)
391 {
392     if (index < 0) {
393         LOGE("invalid index: %{public}d", index);
394         return nullptr;
395     }
396 
397     auto item = items_.find(index);
398     if (item != items_.end()) {
399         return item->second;
400     }
401 
402     if (beginIndex_ == LIST_PARAM_INVAID && endIndex_ == LIST_PARAM_INVAID && maxCount_ > 0 && index >= maxCount_) {
403         LOGW("reach max count! index: %{public}d", index);
404         return nullptr;
405     }
406 
407     // Try to build one new data;
408     if (buildItem_ && !buildItem_(index)) {
409         LOGW("no more new item to be built, index: %{public}d", index);
410         return nullptr;
411     }
412 
413     item = items_.find(index);
414     if (item != items_.end()) {
415         return item->second;
416     }
417 
418     LOGD("build succ but get child failed, index: %{public}d", index);
419     return nullptr;
420 }
421 
GetItemByIndex(int32_t index)422 RefPtr<RenderListItem> RenderList::GetItemByIndex(int32_t index)
423 {
424     auto child = GetChildByIndex(index);
425     if (!child || child->GetChildren().empty()) {
426         return nullptr;
427     }
428     return RenderListItem::GetRenderListItem(child);
429 }
430 
GetIndexByPosition(double position) const431 int32_t RenderList::GetIndexByPosition(double position) const
432 {
433     int32_t index = -1;
434     auto child = GetChildByPosition(position);
435     if (child && !child->GetChildren().empty()) {
436         auto listItem = RenderListItem::GetRenderListItem(child);
437         if (listItem) {
438             index = listItem->GetIndex();
439         }
440     }
441     return index;
442 }
443 
RecycleByItems(const std::vector<int32_t> & needRemoveItems)444 bool RenderList::RecycleByItems(const std::vector<int32_t>& needRemoveItems)
445 {
446     if (needRemoveItems.empty()) {
447         return true;
448     }
449     if (!recycleByItems_) {
450         return false;
451     }
452     return recycleByItems_(needRemoveItems);
453 }
454 
RecycleByRange(int32_t from,int32_t to)455 bool RenderList::RecycleByRange(int32_t from, int32_t to)
456 {
457     LOGD("RecycleChild: fromIndex: %{public}d, toIndex: %{public}d", from, to);
458     if (!recycleByRange_ || from < 0 || from > to) {
459         return false;
460     }
461     bool recycleResult = false;
462     bool success = recycleByRange_(from, to);
463     if (success && from >= 0) {
464         for (int32_t i = from; i <= to; i++) {
465             auto iter = items_.find(i);
466             if (iter != items_.end()) {
467                 LOGD("Recycle index %{public}d success", i);
468                 ResetGroupItem(iter->second);
469                 items_.erase(iter);
470                 recycleResult = true;
471             }
472         }
473     }
474     return recycleResult;
475 }
476 
RecycleAllChild()477 bool RenderList::RecycleAllChild()
478 {
479     if (items_.empty()) {
480         return true;
481     }
482 
483     int32_t fromIndex = items_.begin()->first;
484     int32_t toIndex = items_.rbegin()->first;
485 
486     return RecycleByRange(fromIndex, toIndex);
487 }
488 
SyncIndex(int32_t begin,int32_t end)489 void RenderList::SyncIndex(int32_t begin, int32_t end)
490 {
491     if (items_.empty()) {
492         return;
493     }
494 
495     int32_t fromIndex = items_.begin()->first;
496     int32_t toIndex = items_.rbegin()->first;
497 
498     if (begin > 0 && fromIndex < begin) {
499         RecycleByRange(fromIndex, begin - 1);
500     }
501     if (end > 0 && toIndex > end) {
502         RecycleByRange(end + 1, toIndex);
503     }
504 }
505 
506 // recycle from [start to head] index.
RecycleHead(int32_t head)507 void RenderList::RecycleHead(int32_t head)
508 {
509     if (head >= 0) {
510         RecycleByRange(0, head);
511     }
512 }
513 
514 // recycle from [tail to end] index.
RecycleTail(int32_t tail)515 void RenderList::RecycleTail(int32_t tail)
516 {
517     if (tail < maxCount_) {
518         RecycleByRange(tail, maxCount_);
519     }
520 }
521 
RequestMoreItems(int32_t index,int32_t count)522 void RenderList::RequestMoreItems(int32_t index, int32_t count)
523 {
524     if (requestItems_ && count > 0) {
525         requestItems_(index, count);
526     }
527 }
528 
CalculateStickyItem(const Offset & position)529 void RenderList::CalculateStickyItem(const Offset& position)
530 {
531     // reset item state
532     if (stickyItem_) {
533         stickyItem_->SetVisible(false);
534     }
535     stickyNext_ = nullptr;
536     RefPtr<RenderListItem> listItem = RenderListItem::GetRenderListItem(stickyItem_);
537     if (listItem) {
538         listItem->SetSticky(false);
539     }
540     double listPosition = -GetMainPosition(position);
541     if (listPosition < 0.0) {
542         return;
543     }
544 
545     // calculate current ceiling item
546     int32_t index = GetIndexByPosition(listPosition);
547     if (index < 0) {
548         return;
549     }
550     int32_t newStickyIndex = stickyItemSearcher_(index);
551     if (newStickyIndex < 0) {
552         if (stickyItem_) {
553             listItem = RenderListItem::GetRenderListItem(stickyItem_);
554             if (listItem) {
555                 listItem->HandleStickyEvent(false);
556             }
557         }
558         stickyItem_ = nullptr;
559         return;
560     }
561 
562     listItem = RenderListItem::GetRenderListItem(stickyItem_);
563     int32_t currentStickyIndex = INVALID_INDEX;
564     stickyItemOffset_ = Offset::Zero();
565     if (listItem) {
566         currentStickyIndex = listItem->GetIndex();
567         listItem->SetSticky(false);
568         if (stickyItem_) {
569             stickyItem_->SetVisible(false);
570         }
571     }
572 
573     if (newStickyIndex != currentStickyIndex) {
574         LOGD("Sticky index change from %{public}d to %{public}d", currentStickyIndex, newStickyIndex);
575         if (listItem) {
576             listItem->HandleStickyEvent(false);
577         }
578         stickyItem_ = stickyItemBuilder_(newStickyIndex, false);
579         listItem = RenderListItem::GetRenderListItem(stickyItem_);
580         if (listItem) {
581             listItem->HandleStickyEvent(true);
582         }
583     }
584 
585     if (listItem) {
586         listItem->SetSticky(true);
587     }
588 
589     // layout sticky item
590     LayoutParam itemLayout;
591     itemLayout.SetMaxSize(viewPort_);
592     if (stickyItem_) {
593         stickyItem_->Layout(itemLayout);
594         stickyItem_->SetVisible(false);
595         stickyItemMap_[newStickyIndex] = GetMainSize(stickyItem_->GetLayoutSize());
596     }
597 
598     // check next item
599     CalculateStickyItemOffset(index, listPosition);
600 }
601 
CalculateStickyItemOffset(int32_t index,double position)602 void RenderList::CalculateStickyItemOffset(int32_t index, double position)
603 {
604     stickyItemOffset_ = Offset::Zero();
605     if (!stickyItem_) {
606         return;
607     }
608 
609     // check next item
610     auto nextStickyIndex = GetIndexByPosition(position + GetMainSize(stickyItem_->GetLayoutSize()));
611     auto item = items_.find(nextStickyIndex);
612     if (item == items_.end()) {
613         return;
614     }
615 
616     RefPtr<RenderListItem> listItem = RenderListItem::GetRenderListItem(item->second);
617     if (!listItem || !listItem->GetSticky()) {
618         stickyNext_ = nullptr;
619         return;
620     }
621 
622     double nextItemPosition = GetItemPosition(listItem->GetIndex());
623     double ceilingItemPosition = GetMainSize(stickyItem_->GetLayoutSize());
624     double offset = nextItemPosition - position - ceilingItemPosition;
625     if (offset > 0.0) {
626         offset = 0.0;
627         stickyNext_ = nullptr;
628     } else {
629         RefPtr<RenderListItem> listItemNext = RenderListItem::GetRenderListItem(item->second);
630         if (listItemNext) {
631             stickyNext_ = stickyItemBuilder_(listItemNext->GetIndex(), true);
632         }
633     }
634     // layout sticky next
635     if (stickyNext_) {
636         LayoutParam itemLayout;
637         itemLayout.SetMaxSize(viewPort_);
638         stickyNext_->Layout(itemLayout);
639         stickyNext_->SetVisible(false);
640     }
641     double nextItemOffset = nextItemPosition - position;
642     nextItemOffset = nextItemOffset > ITEM_STICKY_OFFSET ? nextItemOffset - ITEM_STICKY_OFFSET : nextItemOffset;
643     if (direction_ == FlexDirection::COLUMN || direction_ == FlexDirection::COLUMN_REVERSE) {
644         stickyItemOffset_.SetY(offset);
645         stickyNextOffset_.SetY(nextItemOffset);
646     } else {
647         stickyItemOffset_.SetX(offset);
648         stickyNextOffset_.SetX(nextItemOffset);
649     }
650 }
651 
UpdateAccessibilityAttr()652 void RenderList::UpdateAccessibilityAttr()
653 {
654     auto scroll = AceType::DynamicCast<RenderMultiChildScroll>(GetParent().Upgrade());
655     if (!scroll) {
656         LOGE("GetRenderMultiChildScroll failed.");
657         return;
658     }
659     auto refPtr = scroll->GetAccessibilityNode().Upgrade();
660     if (!refPtr) {
661         return;
662     }
663     auto collectionInfo = refPtr->GetCollectionInfo();
664     collectionInfo.rows = maxCount_ > 0 ? maxCount_ : itemsCount_;
665     collectionInfo.columns = columnCount_;
666     refPtr->SetCollectionInfo(collectionInfo);
667     refPtr->SetScrollableState(true);
668     refPtr->SetActionScrollForward([weakList = AceType::WeakClaim(this)]() {
669         auto list = weakList.Upgrade();
670         if (list) {
671             LOGI("Trigger ScrollForward by Accessibility.");
672             return list->HandleActionScroll(true);
673         }
674         return false;
675     });
676     refPtr->SetActionScrollBackward([weakList = AceType::WeakClaim(this)]() {
677         auto list = weakList.Upgrade();
678         if (list) {
679             LOGI("Trigger ScrollBackward by Accessibility.");
680             return list->HandleActionScroll(false);
681         }
682         return false;
683     });
684     refPtr->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
685     refPtr->AddSupportAction(AceAction::ACTION_SCROLL_BACKWARD);
686 }
687 
HandleActionScroll(bool forward)688 bool RenderList::HandleActionScroll(bool forward)
689 {
690     RefPtr<RenderNode> parent = GetParent().Upgrade();
691     RefPtr<RenderMultiChildScroll> scroll = AceType::DynamicCast<RenderMultiChildScroll>(parent);
692     if (!scroll) {
693         LOGE("Parent scroll is null, trigger scroll failed.");
694         return false;
695     }
696     return scroll->ScrollPage(!forward, true);
697 }
698 
ResetGroupItem(const RefPtr<RenderNode> & renderNode)699 void RenderList::ResetGroupItem(const RefPtr<RenderNode>& renderNode)
700 {
701     auto renderListItem = RenderListItem::GetRenderListItem(renderNode);
702     if (renderListItem) {
703         auto renderListItemGroup = AceType::DynamicCast<RenderListItemGroup>(renderListItem);
704         if (renderListItemGroup) {
705             renderListItemGroup->ResetLayout();
706         }
707     }
708 }
709 
OnChildRemoved(const RefPtr<RenderNode> & child)710 void RenderList::OnChildRemoved(const RefPtr<RenderNode>& child)
711 {
712     if (child) {
713         child->SetAccessibilityVisible(false);
714         child->ClearAccessibilityRect();
715     }
716 }
717 
SetGroupState(int32_t index,bool expand)718 void RenderList::SetGroupState(int32_t index, bool expand)
719 {
720     if (index >= 0) {
721         layoutManager_->AddItemGroupExpand(index, expand);
722     } else if (index == INDEX_EXPAND_ALL) {
723         layoutManager_->ClearItemPosition();
724         layoutManager_->SetExpandAll(expand);
725         layoutManager_->ClearItemGroupsExpand();
726         RefreshOffset(0.0);
727     }
728 }
729 
TouchTest(const Point & globalPoint,const Point & parentLocalPoint,const TouchRestrict & touchRestrict,TouchTestResult & result)730 bool RenderList::TouchTest(const Point& globalPoint, const Point& parentLocalPoint,
731     const TouchRestrict& touchRestrict, TouchTestResult& result)
732 {
733     RefPtr<RenderNode> parent = GetParent().Upgrade();
734     RefPtr<RenderMultiChildScroll> scroll = AceType::DynamicCast<RenderMultiChildScroll>(parent);
735 
736     if (stickyItem_) {
737         const auto localPoint = parentLocalPoint - GetPaintRect().GetOffset();
738         stickyItem_->TouchTest(globalPoint, localPoint, touchRestrict, result);
739     }
740 
741     if (!scroll->IsDragging()) {
742         return RenderNode::TouchTest(globalPoint, parentLocalPoint, touchRestrict, result);
743     }
744 
745     return true;
746 }
747 
748 // notify start position in global main axis
NotifyDragStart(double startPosition)749 void RenderList::NotifyDragStart(double startPosition)
750 {
751     if (!layoutManager_) {
752         LOGE("NotifyDragStart failed. layout manager is null.");
753         return;
754     }
755     double globalMainOffset = (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE)
756                                   ? GetGlobalOffset().GetX()
757                                   : GetGlobalOffset().GetY();
758     double localOffset = startPosition - globalMainOffset;
759     double scrollPosition = (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE)
760                                 ? layoutManager_->GetPosition().GetX()
761                                 : layoutManager_->GetPosition().GetY();
762     double position = localOffset - scrollPosition;
763     auto render = RenderListItem::GetRenderListItem(GetNearChildByPosition(position));
764     if (render) {
765         dragStartIndexPending_ = render->GetIndex();
766         LOGD("DragStart. startPosition: %{public}.1f, position: %{public}.1f, index: %{public}d", startPosition,
767             position, render->GetIndex());
768     } else {
769         LOGE("DragStart Mismatch. position: %{public}.1f, scrollPosition: %{public}.1f", position, scrollPosition);
770     }
771 }
772 
773 // notify drag offset in global main axis
NotifyDragUpdate(double dragOffset)774 void RenderList::NotifyDragUpdate(double dragOffset)
775 {
776     LOGD("NotifyDragUpdate. dragOffset: %{public}.1lf.", dragOffset);
777     currentDelta_ = dragOffset;
778 }
779 
NotifyScrollOver(double velocity,bool isCrashTop,bool isCrashBottom)780 void RenderList::NotifyScrollOver(double velocity, bool isCrashTop, bool isCrashBottom)
781 {
782     if (!chainAnimation_) {
783         return;
784     }
785     if (NearZero(velocity)) {
786         LOGD("velocity is zero, no need to handle in chain animation.");
787         return;
788     }
789     // when scroll over head/tail, control node needs to switch to head/tail node.
790     if (isCrashTop) {
791         dragStartIndexPending_ = GetCurrentMinIndex();
792     } else if (isCrashBottom) {
793         dragStartIndexPending_ = GetCurrentMaxIndex();
794     } else {
795         LOGW("ScrollOver but neither top nor bottom crashed. velocity: %{public}f", velocity);
796     }
797     LOGD("NotifyScrollOver. velocity: %{public}.1lf, dragStartIndex_: %{public}d", velocity, dragStartIndexPending_);
798 }
799 
SetOnRotateCallback(const RefPtr<ListComponent> & component)800 void RenderList::SetOnRotateCallback(const RefPtr<ListComponent>& component)
801 {
802     const auto& onRotateId = component->GetOnRotateId();
803     if (onRotateId.IsEmpty()) {
804         return;
805     }
806     rotationEvent_ = AceAsyncEvent<void(const RotationEvent&)>::Create(onRotateId, context_);
807 }
808 
GetItemIndex(const RefPtr<RenderNode> & node)809 int32_t RenderList::GetItemIndex(const RefPtr<RenderNode>& node)
810 {
811     for (const auto& child : items_) {
812         if (child.second == node) {
813             return child.first;
814         }
815     }
816     return 0;
817 }
818 
PaintItems(RenderContext & context,const Offset & offset)819 void RenderList::PaintItems(RenderContext& context, const Offset& offset)
820 {
821     auto childList = GetChildren();
822     childList.sort([wp = WeakClaim(this)](RefPtr<RenderNode>& node1, RefPtr<RenderNode>& node2) {
823         auto list = wp.Upgrade();
824         if (list) {
825             return list->GetItemIndex(node1) < list->GetItemIndex(node2);
826         }
827         return true;
828     });
829     PaintChildList(childList, context, offset);
830 }
831 } // namespace OHOS::Ace
832