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