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