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