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