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