• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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/list_element.h"
17 
18 #include "base/log/log.h"
19 #include "base/utils/utils.h"
20 #include "core/common/frontend.h"
21 #include "core/components/list/list_component.h"
22 #include "core/components/list/list_item_component.h"
23 #include "core/components/list/list_item_element.h"
24 #include "core/components/list/render_list.h"
25 #include "core/components/list/render_list_item.h"
26 #include "core/components/proxy/render_item_proxy.h"
27 #include "core/components/scroll/render_multi_child_scroll.h"
28 #include "core/event/ace_event_helper.h"
29 #include "core/pipeline/base/composed_element.h"
30 
31 namespace OHOS::Ace {
32 namespace {
33 
BuildEventParam(int32_t beginIndex,int32_t endIndex)34 std::string BuildEventParam(int32_t beginIndex, int32_t endIndex)
35 {
36     return std::string("{\"begin\":")
37         .append(std::to_string(beginIndex))
38         .append(",\"end\":")
39         .append(std::to_string(endIndex))
40         .append("}");
41 }
42 
43 } // namespace
44 
CreateRenderNode()45 RefPtr<RenderNode> ListElement::CreateRenderNode()
46 {
47     auto context = context_.Upgrade();
48     if (!context) {
49         LOGE("context is nullptr!");
50         return nullptr;
51     }
52     isJsCard_ = context->IsJsCard();
53     RefPtr<RenderNode> node = ComponentGroupElement::CreateRenderNode();
54     renderList_ = AceType::DynamicCast<RenderList>(node);
55     auto multiChildScroll = AceType::DynamicCast<RenderMultiChildScroll>(RenderMultiChildScroll::Create());
56     multiChildScroll->AddChild(renderList_);
57     renderList_->RegisterRequestItemsCallback(
58         [weakListElement = AceType::WeakClaim(this)](int32_t index, int32_t count) {
59             auto listElement = weakListElement.Upgrade();
60             if (listElement) {
61                 listElement->RetrieveListData(index, count);
62             }
63         });
64 
65     renderList_->RegisterRecycleByRangeCallback(
66         [weakListElement = AceType::WeakClaim(this)](int32_t& from, int32_t& to) -> bool {
67             auto listElement = weakListElement.Upgrade();
68             if (!listElement) {
69                 LOGE("list element is nullptr");
70                 return false;
71             }
72             listElement->RecycleByRange(from, to);
73             return true;
74         });
75 
76     renderList_->RegisterRecycleByItemsCallback(
77         [weakListElement = AceType::WeakClaim(this)](const std::vector<int32_t>& items) -> bool {
78             auto listElement = weakListElement.Upgrade();
79             if (listElement) {
80                 listElement->RecycleByItems(items);
81                 return true;
82             }
83             return false;
84         });
85 
86     renderList_->RegisterBuildItemCallback(
87         [weakListElement = AceType::WeakClaim(this)](int32_t index) -> bool {
88             auto listElement = weakListElement.Upgrade();
89             if (listElement) {
90                 return listElement->BuildListData(index);
91             }
92             return false;
93         });
94 
95     renderList_->RegisterOnRefreshedCallback(
96         [weakListElement = AceType::WeakClaim(this)]() {
97             auto listElement = weakListElement.Upgrade();
98             if (listElement) {
99                 listElement->OnRefreshed();
100             }
101         });
102 
103     InitStickyFunc();
104 
105     return multiChildScroll;
106 }
107 
RetrieveListData(int32_t beginIndex,int32_t endIndex)108 void ListElement::RetrieveListData(int32_t beginIndex, int32_t endIndex)
109 {
110     if (requestItemAsync_ && !building_) {
111         std::string command("\"");
112         command.append(LIST_EVENT_REQUEST_ITEM);
113         command.append("\",");
114         command.append(BuildEventParam(beginIndex, endIndex));
115         requestItemAsync_(command);
116         building_ = true;
117     }
118 
119     if (endIndex > 0) {
120         std::string param = BuildEventParam(beginIndex, endIndex);
121         LOGD("RetrieveListData [begin = %{public}d, end = %{public}d)", beginIndex, endIndex);
122         std::string result;
123         if (requestItem_) {
124             requestItem_(param, result);
125         }
126     }
127 }
128 
BuildListDataFromChild(int32_t index)129 bool ListElement::BuildListDataFromChild(int32_t index)
130 {
131     if (beginIndex_ != LIST_PARAM_INVAID && endIndex_ != LIST_PARAM_INVAID) {
132         if (index > itemVectorHit_.first) {
133             for (int32_t position = itemVectorHit_.second + 1; position < (int32_t)itemComponents_.size(); position++) {
134                 auto itemComponent = ListItemComponent::GetListItem(itemComponents_[position]);
135                 if (!itemComponent) {
136                     LOGE("itemComponent exist but is null");
137                     return false;
138                 }
139                 if (index == itemComponent->GetIndex()) {
140                     itemVectorHit_.first = index;
141                     itemVectorHit_.second = position;
142                     return BuildListComponent(itemComponents_[position]);
143                 }
144             }
145         } else {
146             for (int position = itemVectorHit_.second; position >= 0; position--) {
147                 auto itemComponent = ListItemComponent::GetListItem(itemComponents_[position]);
148                 if (!itemComponent) {
149                     LOGE("itemComponent exist but is null");
150                     return false;
151                 }
152                 if (index == itemComponent->GetIndex()) {
153                     itemVectorHit_.first = index;
154                     itemVectorHit_.second = position;
155                     return BuildListComponent(itemComponents_[position]);
156                 }
157             }
158         }
159         LOGW("list-item component (index=%{public}d) not in cache!", index);
160         return false;
161     }
162     // now just for prebuild more items.
163     preBuildCount_ = static_cast<int32_t>(itemComponents_.size());
164     if (index >= preBuildCount_ - cachedCount_ && requestItemAsync_) {
165         RetrieveListData(0, preBuildCount_ + cachedCount_);
166     }
167 
168     if (index < 0 || index >= preBuildCount_) {
169         LOGE("invalid index: %{public}d, size: %{public}d", index, preBuildCount_);
170         return false;
171     }
172 
173     auto item = itemComponents_[index];
174     return BuildListComponent(item);
175 }
176 
BuildListData(int32_t index)177 bool ListElement::BuildListData(int32_t index)
178 {
179     if (BuildListDataFromChild(index)) {
180         return true;
181     }
182 
183     bool result = false;
184     auto iter = newListItemsMap_.find(index);
185     if (iter != newListItemsMap_.end()) {
186         auto component = iter->second;
187         newListItemsMap_.erase(iter);
188         result = BuildListComponent(component);
189     }
190 
191     return result;
192 }
193 
InitStickyFunc()194 void ListElement::InitStickyFunc()
195 {
196     renderList_->RegisterStickyItemBuilderCallback(
197         [weakListElement = AceType::WeakClaim(this)](int32_t index, bool next) -> RefPtr<RenderNode> {
198             auto listElement = weakListElement.Upgrade();
199             if (listElement) {
200                 return listElement->BuildStickyItem(index, next);
201             } else {
202                 return nullptr;
203             }
204         });
205 
206     renderList_->RegisterStickyItemSearcherCallback(
207         [weakListElement = AceType::WeakClaim(this)](int32_t index) -> int32_t {
208             auto listElement = weakListElement.Upgrade();
209             if (listElement && listElement->SupportStickyItem()) {
210                 return listElement->SearchStickyItem(index);
211             } else {
212                 return -1;
213             }
214         });
215 }
216 
ResetStickyItem()217 void ListElement::ResetStickyItem()
218 {
219     if (!stickyElement_) {
220         return;
221     }
222     auto sticky = stickyElement_->GetRenderNode()->GetParent().Upgrade();
223     auto listItem = RenderListItem::GetRenderListItem(sticky);
224     if (listItem) {
225         listItem->SetIndex(-1);
226     }
227 }
228 
BuildStickyItem(int32_t index,bool next)229 RefPtr<RenderNode> ListElement::BuildStickyItem(int32_t index, bool next)
230 {
231     RefPtr<Component> component;
232     for (auto& item : itemComponents_) {
233         auto itemComponent = ListItemComponent::GetListItem(item);
234         if (itemComponent && itemComponent->GetIndex() == index) {
235             component = item;
236             break;
237         }
238     }
239     if (!component) {
240         LOGE("Build sticky item for index:%{public}d next:%{public}d failed", index, next);
241         return nullptr;
242     }
243 
244     RefPtr<Element> sticky;
245     if (next) {
246         stickyNextElement_ = UpdateChild(stickyNextElement_, component);
247         if (!stickyNextElement_) {
248             LOGE("get second sticky element failed.");
249             return nullptr;
250         }
251         sticky = stickyNextElement_;
252     } else {
253         stickyElement_ = UpdateChild(stickyElement_, component);
254         if (!stickyElement_) {
255             LOGE("get first sticky element failed.");
256             return nullptr;
257         }
258         sticky = stickyElement_;
259     }
260     RefPtr<RenderListItem> renderItem = AceType::DynamicCast<RenderListItem>(sticky->GetRenderNode());
261     if (renderItem && renderList_) {
262         renderItem->SetScrollController(renderList_->GetController());
263         renderItem->SetClonedBySticky(true);
264         renderItem->SetNeedUpdateAccessibility(false);
265     }
266     return sticky->GetRenderNode()->GetParent().Upgrade();
267 }
268 
SearchStickyItem(int32_t index)269 int32_t ListElement::SearchStickyItem(int32_t index)
270 {
271     for (auto iter = itemComponents_.rbegin(); iter != itemComponents_.rend(); ++iter) {
272         auto item = ListItemComponent::GetListItem(*iter);
273         if (item && item->GetIndex() <= index && item->GetSticky()) {
274             return item->GetIndex();
275         }
276     }
277     return INVALID_INDEX;
278 }
279 
SupportStickyItem() const280 bool ListElement::SupportStickyItem() const
281 {
282     if (!renderList_) {
283         LOGE("Render is null, do not support sticky item.");
284         return false;
285     }
286     return renderList_->SupportStickyItem();
287 }
288 
RemoveComposedChildFromMap(RefPtr<Element> element)289 void ListElement::RemoveComposedChildFromMap(RefPtr<Element> element)
290 {
291     // Remove all composed element children in list-item from pipeline context composed element map.
292     // Make sure updating list-item can only be done by using list-item composed id.
293     if (element) {
294         const auto& children = element->GetChildren();
295         for (const auto& child : children) {
296             const auto& composedChild = AceType::DynamicCast<ComposedElement>(child);
297             if (composedChild) {
298                 composedChild->Detached();
299             }
300             RemoveComposedChildFromMap(child);
301         }
302     }
303 }
304 
UpdateListItemElement(const RefPtr<Component> & component)305 void ListElement::UpdateListItemElement(const RefPtr<Component>& component)
306 {
307     auto itemComponent = ListItemComponent::GetListItem(component);
308     if (!itemComponent) {
309         LOGE("itemComponent exist but is null");
310         return;
311     }
312 
313     // Update New Component to Element.
314     int32_t index = itemComponent->GetIndex();
315     RefPtr<Element> element;
316     auto item = itemElements_.find(index);
317     if (item != itemElements_.end() && item->second) {
318         element = item->second;
319     }
320     if (element) {
321         auto itemElement = ListItemElement::GetListItem(element);
322         if (itemElement->GetKey() == -1 || itemElement->GetKey() != itemComponent->GetKey()) {
323             UpdateChild(element, component);
324             if (accessibilityDisabled_) {
325                 auto renderNode = element->GetRenderNode();
326                 if (renderNode) {
327                     renderNode->SetNeedUpdateAccessibility(false);
328                 }
329             }
330         }
331     }
332 }
333 
BuildListComponent(const RefPtr<Component> & component)334 bool ListElement::BuildListComponent(const RefPtr<Component>& component)
335 {
336     auto itemComponent = ListItemComponent::GetListItem(component);
337     if (!itemComponent) {
338         LOGE("itemComponent exist but is null");
339         return false;
340     }
341 
342     RefPtr<Element> element;
343     int32_t index = itemComponent->GetIndex();
344 
345     if (itemComponent->TestFlag(LIST_ITEM_FLAG_FROM_CHILD)) {
346         auto item = itemElements_.find(index);
347         if (item != itemElements_.end()) {
348             element = item->second;
349         }
350     }
351 
352     if (element) {
353         Element::AddChild(element);
354         if (!element->GetRenderNode()) {
355             LOGW("no render node in this recycled element");
356             element = nullptr;
357         }
358     }
359 
360     LOGD("build item index:%{public}d type:%{public}s", index, itemComponent->GetType().c_str());
361     element = UpdateChild(element, component);
362     if (itemComponent->TestFlag(LIST_ITEM_FLAG_DYNAMIC)) {
363         RemoveComposedChildFromMap(element);
364     }
365 
366     if (!element) {
367         LOGE("no element found!");
368         return false;
369     }
370 
371     // Get element proxy.
372     auto parent = element->GetRenderNode()->GetParent();
373     auto itemProxy = parent.Upgrade();
374     if (!itemProxy) {
375         LOGE("itemProxy is null");
376         return false;
377     }
378 
379     // Add list item element to focus tree.
380     auto itemElement = ListItemElement::GetListItem(element);
381     if (itemElement) {
382         itemElement->AddToFocus();
383     }
384 
385     itemElements_[index] = element;
386     renderList_->AddListItem(index, itemProxy);
387     itemProxy->SetHidden(false);
388     if (accessibilityDisabled_) {
389         auto renderNode = element->GetRenderNode();
390         if (renderNode) {
391             renderNode->SetNeedUpdateAccessibility(false);
392         }
393     }
394     // recover visible state.
395     if (itemProxy->GetVisible() != GetRenderNode()->GetVisible()) {
396         itemProxy->SetVisible(GetRenderNode()->GetVisible());
397     }
398 
399     // refresh focus
400     auto pipelineContext = context_.Upgrade();
401     if (pipelineContext) {
402         pipelineContext->RefreshStageFocus();
403     }
404 
405     return true;
406 }
407 
PreBuildListItems(int32_t index,const std::list<RefPtr<Component>> & newComponent,int32_t from)408 void ListElement::PreBuildListItems(int32_t index, const std::list<RefPtr<Component>>& newComponent, int32_t from)
409 {
410     for (const auto& child : newComponent) {
411         if (index >= preBuildCount_) {
412             auto itemComponent = ListItemComponent::GetListItem(child);
413             if (!itemComponent) {
414                 LOGE("cast to ListItemComponent failed");
415                 continue;
416             }
417             itemComponent->SetIndex(index);
418             itemComponent->SetFlags(from);
419             newListItemsMap_.emplace(std::make_pair(index, itemComponent));
420         }
421         index++;
422     }
423     LOGD("PreBuildListItems: index:%{public}d, count:%{public}d", index, preBuildCount_);
424     preBuildCount_ = index;
425 }
426 
ReleaseRecycledListItem(int32_t from,int32_t to)427 void ListElement::ReleaseRecycledListItem(int32_t from, int32_t to)
428 {
429     for (int32_t i = from; i <= to; ++i) {
430         auto item = itemElements_.find(i);
431         if (item == itemElements_.end()) {
432             continue;
433         }
434 
435         auto itemElement = ListItemElement::GetListItem(item->second);
436         if (!itemElement) {
437             LOGW("not item element(%{public}s)", AceType::TypeName(item->second));
438             continue;
439         }
440 
441         RefPtr<RenderNode> proxyNode;
442         auto renderNode = item->second->GetRenderNode();
443         if (renderNode) {
444             proxyNode = renderNode->GetParent().Upgrade();
445         }
446 
447         if (!proxyNode) {
448             LOGW("Proxy node is null.");
449             continue;
450         }
451 
452         // RemoveChild will reset render node in this element.
453         // Here we save and restore its render node for recycling.
454         Element::RemoveChild(item->second);
455         if (!itemElement->IsCurrentFocus()) {
456             // Remove list item element from focus tree.
457             auto focusNode = AceType::DynamicCast<FocusNode>(itemElement);
458             if (focusNode) {
459                 focusNode->RemoveSelf();
460             }
461         }
462         proxyNode->Unmount();
463         itemElement->AttachRenderNode(proxyNode);
464 
465         // Hidden the release node for stop it's layout and paint.
466         proxyNode->SetHidden(true);
467         itemElements_.erase(item);
468     }
469 }
470 
RecycleByItems(const std::vector<int32_t> & items)471 void ListElement::RecycleByItems(const std::vector<int32_t>& items)
472 {
473     if (!itemElements_.empty()) {
474         for (auto item : items) {
475             if (RecycleItem(item)) {
476                 ReleaseRecycledListItem(item, item);
477             }
478         }
479     }
480 
481     std::map<int32_t, RefPtr<Element>> newItems;
482     auto iter = itemElements_.begin();
483     while (iter != itemElements_.end()) {
484         auto item = iter->second;
485         auto proxyNode = item->GetRenderNode();
486         auto listItemNode = RenderListItem::GetRenderListItem(proxyNode);
487         if (listItemNode) {
488             int32_t index = listItemNode->GetIndex();
489             auto listItemElement = ListItemElement::GetListItem(item);
490             if (listItemElement) {
491                 listItemElement->SetIndex(index);
492             }
493             newItems.emplace(std::make_pair(index, item));
494         }
495 
496         ++iter;
497     }
498 
499     itemElements_.clear();
500     itemElements_.swap(newItems);
501 }
502 
RecycleItem(int32_t index)503 bool ListElement::RecycleItem(int32_t index)
504 {
505     auto element = itemElements_.find(index);
506     if (element == itemElements_.end()) {
507         return false;
508     }
509     auto itemElement = ListItemElement::GetListItem(element->second);
510     if (!itemElement) {
511         return false;
512     }
513     itemElement->SetKey(-1);
514     return true;
515 }
516 
RecycleByRange(int32_t & from,int32_t & to)517 void ListElement::RecycleByRange(int32_t& from, int32_t& to)
518 {
519     int32_t firstRecycled = -1;
520     int32_t lastRecycled = to;
521     LOGD("[recycle] recycle list items from: %{public}d, to: %{public}d", from, to);
522 
523     if (!itemElements_.empty()) {
524         int32_t start = std::max(itemElements_.begin()->first, from);
525         int32_t end = std::min(itemElements_.rbegin()->first, to);
526         for (int32_t i = start; i <= end; ++i) {
527             if (!RecycleItem(i)) {
528                 continue;
529             }
530             if (firstRecycled < 0) {
531                 firstRecycled = i;
532             }
533             lastRecycled = i;
534         }
535     }
536 
537     // Release all recycled items.
538     from = firstRecycled;
539     to = lastRecycled;
540     if (from >= 0) {
541         ReleaseRecycledListItem(from, to);
542     }
543 }
544 
OnRefreshed()545 void ListElement::OnRefreshed()
546 {
547     needRefresh_ = false;
548     RefPtr<ComponentGroup> group = AceType::DynamicCast<ComponentGroup>(listComponent_);
549     if (!group || group->GetChildren().empty()) {
550         listComponent_ = nullptr;
551         return;
552     }
553 
554     int32_t index = 0;
555     auto children = group->GetChildren();
556     auto iter = children.begin();
557     while (iter != children.end()) {
558         auto listItemComponent = ListItemComponent::GetListItem(*iter);
559         if (listItemComponent) {
560             if (listItemComponent->GetOperation() == LIST_ITEM_OP_REMOVE) {
561                 group->ComponentGroup::RemoveChild(*iter);
562             } else {
563                 listItemComponent->SetOperation(LIST_ITEM_OP_NONE);
564                 listItemComponent->RemoveFlag(LIST_ITEM_FLAG_IN_RANGE);
565                 listItemComponent->SetIndex(index++);
566             }
567         }
568         ++iter;
569     }
570 
571     listComponent_ = nullptr;
572 
573     for (auto& item : itemElements_) {
574         auto listItemElement = ListItemElement::GetListItem(item.second);
575         if (listItemElement) {
576             listItemElement->RemoveFlag(LIST_ITEM_FLAG_IN_RANGE);
577         }
578     }
579 }
580 
AddToCache(const RefPtr<Component> & item,int32_t index,bool isDynamic)581 inline int32_t ListElement::AddToCache(const RefPtr<Component>& item, int32_t index, bool isDynamic)
582 {
583     auto listItemComponent = ListItemComponent::GetListItem(item);
584     if (listItemComponent && listItemComponent->GetOperation() != LIST_ITEM_OP_REMOVE) {
585         listItemComponent->SetOperation(LIST_ITEM_OP_NONE);
586         listItemComponent->RemoveFlag(LIST_ITEM_FLAG_IN_RANGE);
587         listItemComponent->SetIndex(index);
588         if (isDynamic) {
589             listItemComponent->AddFlag(LIST_ITEM_FLAG_DYNAMIC);
590         }
591         itemComponents_.emplace_back(item);
592         UpdateListItemElement(item);
593         return index + 1;
594     }
595     return index;
596 }
597 
UpdateCachedComponent()598 void ListElement::UpdateCachedComponent()
599 {
600     // Normally, components are not saved in element.
601     // However, list-element may need caching list-item-components to improve the speed of sliding.
602     RefPtr<ComponentGroup> group = AceType::DynamicCast<ComponentGroup>(component_);
603     if (!group) {
604         LOGW("invalid input component!");
605         return;
606     }
607     if (beginIndex_ == LIST_PARAM_INVAID || endIndex_ == LIST_PARAM_INVAID || repeatedLength_ == LIST_PARAM_INVAID) {
608         LOGW("invalid list parameter");
609         return;
610     }
611 
612     itemComponents_.clear();
613     itemVectorHit_ = std::make_pair(-1, -1);
614     auto children = group->GetChildren();
615 
616     children.sort([](const RefPtr<Component>& node1, const RefPtr<Component>& node2) {
617         auto itemNode1 = ListItemComponent::GetListItem(node1);
618         auto itemNode2 = ListItemComponent::GetListItem(node2);
619         if (itemNode1 && itemNode2) {
620             return itemNode1->GetKey() < itemNode2->GetKey();
621         }
622         return false;
623     });
624 
625     auto child = children.begin();
626     int32_t index = 0;
627     for (index = 0; index < indexOffset_ && child != children.end(); ++child) {
628         index = AddToCache(*child, index);
629     }
630     for (index = beginIndex_ + indexOffset_; index < endIndex_ + indexOffset_ && child != children.end(); ++child) {
631         index = AddToCache(*child, index, true);
632     }
633     renderList_->SyncIndex(beginIndex_ + indexOffset_, endIndex_ + indexOffset_);
634     maxCount_ = index;
635     tailLength_ = 0;
636     if (repeatedLength_ != LIST_LENGTH_INFINITE) {
637         for (index = repeatedLength_ + indexOffset_; child != children.end(); ++child) {
638             index = AddToCache(*child, index);
639             ++tailLength_;
640         }
641         if (endIndex_ == repeatedLength_) {
642             maxCount_ += tailLength_;
643         }
644         length_ = indexOffset_ + repeatedLength_ + tailLength_;
645     } else {
646         length_ = LIST_LENGTH_INFINITE;
647     }
648     renderList_->SetLength(length_);
649 }
650 
UpdateListElement()651 void ListElement::UpdateListElement()
652 {
653     bool rebuild = false;
654     int32_t tailIndex = -1;
655     if (needRefresh_) {
656         renderList_->MarkNeedRefresh();
657     }
658 
659     GetRefreshItems(rebuild, tailIndex);
660     if (rebuild) {
661         LOGD("rebuild elements");
662         ResetStickyItem();
663         RebuildElements(tailIndex);
664         PatchElements(true);
665         renderList_->MarkNeedRefresh();
666         needRefresh_ = true;
667     } else {
668         LOGD("patch elements");
669         PatchElements(false);
670     }
671 }
672 
GetRefreshItems(bool & rebuild,int32_t & tailIndex)673 void ListElement::GetRefreshItems(bool& rebuild, int32_t& tailIndex)
674 {
675     needRefreshItems_.clear();
676     RefPtr<ComponentGroup> group = AceType::DynamicCast<ComponentGroup>(component_);
677     if (!group) {
678         return;
679     }
680 
681     int32_t currentMin = renderList_->GetCurrentMinIndex();
682     int32_t currentMax = renderList_->GetCurrentMaxIndex() + 1;
683     const auto& components = group->GetChildren();
684 
685     LOGD("currentMin: %{public}d, currentMax: %{public}d, components.size(): %{public}d", currentMin, currentMax,
686         static_cast<int>(components.size()));
687     int32_t head = 0;
688     bool needRefresh = true;
689     bool inRange = false;
690     itemComponents_.clear();
691     for (const auto& component : components) {
692         auto item = ListItemComponent::GetListItem(component);
693         if (!item) {
694             LOGW("item is not list item component");
695             continue;
696         }
697 
698         int32_t op = item->GetOperation();
699         if (op != LIST_ITEM_OP_REMOVE) {
700             itemComponents_.emplace_back(component);
701         }
702 
703         if (!needRefresh) {
704             continue;
705         }
706 
707         if (op != LIST_ITEM_OP_ADD) {
708             if (item->GetIndex() == currentMax) {
709                 needRefresh = false;
710             } else if (item->GetIndex() == currentMin) {
711                 inRange = true;
712                 tailIndex = head + currentMin - 1;
713             }
714         }
715 
716         if (!inRange) {
717             if (op == LIST_ITEM_OP_ADD) {
718                 head++;
719             } else if (op == LIST_ITEM_OP_REMOVE) {
720                 head--;
721             }
722             if (op != LIST_ITEM_OP_NONE) {
723                 needRefreshItems_.emplace_back(component);
724                 rebuild = true;
725             }
726         } else {
727             tailIndex++;
728             item->AddFlag(LIST_ITEM_FLAG_IN_RANGE);
729             needRefreshItems_.emplace_back(component);
730             if (op != LIST_ITEM_OP_NONE) {
731                 rebuild = true;
732             }
733         }
734     }
735     if (isJsCard_) {
736         rebuild = true;
737     }
738     maxCount_ = static_cast<int32_t>(itemComponents_.size());
739 }
740 
RebuildElements(int32_t tailIndex)741 void ListElement::RebuildElements(int32_t tailIndex)
742 {
743     LOGD("tailIndex: %{public}d", tailIndex);
744     listComponent_ = component_;
745     auto items = itemElements_;
746     renderList_->RecycleAllChild();
747     int32_t index = tailIndex;
748     auto riter = needRefreshItems_.rbegin();
749     while (index >= 0 && riter != needRefreshItems_.rend()) {
750         RefPtr<Element> updatedChild;
751         auto item = ListItemComponent::GetListItem(*riter);
752         if (item) {
753             if (item->TestFlag(LIST_ITEM_FLAG_IN_RANGE) && item->GetOperation() != LIST_ITEM_OP_ADD) {
754                 RefPtr<Element> oldElement;
755                 auto oldElementIter = items.find(item->GetIndex());
756                 if (oldElementIter != items.end()) {
757                     oldElement = oldElementIter->second;
758                 }
759 
760                 item->SetIndex(index);
761                 if (oldElement) {
762                     Element::AddChild(oldElement);
763                 }
764                 updatedChild = UpdateChild(oldElement, *riter);
765             } else {
766                 item->SetIndex(index);
767                 updatedChild = UpdateChild(nullptr, *riter);
768             }
769 
770             // Get item proxy.
771             auto parent = updatedChild->GetRenderNode()->GetParent();
772             auto itemProxy = parent.Upgrade();
773             if (itemProxy) {
774                 // Add list item element to focus tree.
775                 auto itemElement = ListItemElement::GetListItem(updatedChild);
776                 if (itemElement) {
777                     itemElement->AddToFocus();
778                 }
779 
780                 itemElements_[index] = updatedChild;
781                 renderList_->AddListItem(index, itemProxy);
782                 itemProxy->SetHidden(false);
783                 // recover visible state.
784                 if (itemProxy->GetVisible() != GetRenderNode()->GetVisible()) {
785                     itemProxy->SetVisible(GetRenderNode()->GetVisible());
786                 }
787             } else {
788                 LOGE("itemProxy is null");
789             }
790         }
791         --index;
792         ++riter;
793     }
794 }
795 
PatchElements(bool rebuild)796 void ListElement::PatchElements(bool rebuild)
797 {
798     if (needRefreshItems_.empty()) {
799         LOGE("need refresh is empty");
800         return;
801     }
802 
803     RefPtr<ListItemComponent> startItem;
804     if (rebuild) {
805         startItem = ListItemComponent::GetListItem(needRefreshItems_.back());
806     } else {
807         startItem = ListItemComponent::GetListItem(needRefreshItems_.front());
808     }
809     if (!startItem) {
810         LOGE("start item is not list item");
811         return;
812     }
813 
814     int32_t start = startItem->GetIndex();
815     if (rebuild) {
816         start += 1;
817     }
818 
819     RefPtr<ComponentGroup> group = AceType::DynamicCast<ComponentGroup>(component_);
820     if (!group || group->GetChildren().empty()) {
821         return;
822     }
823 
824     auto children = group->GetChildren();
825     auto iter = children.begin();
826     std::advance(iter, start);
827     while (iter != children.end()) {
828         auto listItemComponent = ListItemComponent::GetListItem(*iter);
829         if (listItemComponent) {
830             if (listItemComponent->GetOperation() == LIST_ITEM_OP_REMOVE) {
831                 group->ComponentGroup::RemoveChild(*iter);
832             } else {
833                 listItemComponent->SetOperation(LIST_ITEM_OP_NONE);
834                 listItemComponent->RemoveFlag(LIST_ITEM_FLAG_IN_RANGE);
835                 listItemComponent->SetIndex(start++);
836             }
837         }
838         ++iter;
839     }
840 }
841 
PerformBuild()842 void ListElement::PerformBuild()
843 {
844     building_ = false;
845     LOGD("pre max: %{public}d, cur max: %{public}d", renderList_->GetMaxCount(), maxCount_);
846     renderList_->SetMaxCount(maxCount_);
847     if (beginIndex_ != LIST_PARAM_INVAID && endIndex_ != LIST_PARAM_INVAID) {
848         if (endIndex_ == 0 && repeatedLength_ != LIST_PARAM_INVAID && repeatedLength_ != 0) {
849             LOGD("request initial children, cacheCount: %{public}d", cachedCount_);
850             RetrieveListData(0, cachedCount_);
851         }
852     } else {
853         if (maxCount_ == 0) {
854             LOGD("request initial children, cacheCount: %{public}d", cachedCount_);
855             RetrieveListData(0, cachedCount_);
856         }
857     }
858 }
859 
Update()860 void ListElement::Update()
861 {
862     RefPtr<ListComponent> list = AceType::DynamicCast<ListComponent>(component_);
863     if (!list) {
864         return;
865     }
866 
867     auto context = context_.Upgrade();
868     if (context && context->GetFrontend() && context->GetFrontendType() == FrontendType::JS) {
869         requestItemAsync_ = AceAsyncEvent<void(const std::string&)>::Create(list->GetOnRequestItem(), context_);
870     } else {
871         requestItem_ = AceSyncEvent<void(const std::string&, std::string&)>::Create(list->GetOnRequestItem(), context_);
872     }
873     cachedCount_ = list->GetCachedCount();
874     beginIndex_ = list->GetBeginIndex();
875     endIndex_ = list->GetEndIndex();
876     repeatedLength_ = list->GetRepeatedLength();
877     indexOffset_ = list->GetIndexOffset();
878     accessibilityDisabled_ = list->IsAccessibilityDisabled();
879 
880     if (beginIndex_ != LIST_PARAM_INVAID && endIndex_ != LIST_PARAM_INVAID) {
881         UpdateCachedComponent();
882         ComponentGroupElement::Update();
883     } else {
884         ComponentGroupElement::Update();
885         if (list->NeedUpdateElement() || isJsCard_) {
886             LOGD("update list element");
887             UpdateListElement();
888             list->MarkNeedUpdateElement(false);
889         } else if (list->NeedPreBuild() && newListItemsMap_.empty()) {
890             LOGD("update from json");
891             RefPtr<ComponentGroup> group = AceType::DynamicCast<ComponentGroup>(component_);
892             if (group) {
893                 const auto& children = group->GetChildren();
894                 PreBuildListItems(0, children, LIST_ITEM_FLAG_FROM_CHILD);
895                 maxCount_ = list->GetTotalCount();
896             }
897         }
898     }
899 }
900 
ApplyRenderChild(const RefPtr<RenderElement> & renderChild)901 void ListElement::ApplyRenderChild(const RefPtr<RenderElement>& renderChild)
902 {
903     if (!renderChild) {
904         LOGE("Element child is null");
905         return;
906     }
907 
908     if (!renderList_) {
909         LOGE("ListElement don't have a render list");
910         return;
911     }
912 
913     RefPtr<RenderNode> proxy;
914     auto listItemElement = ListItemElement::GetListItem(renderChild);
915     if (listItemElement) {
916         proxy = listItemElement->GetProxyRenderNode();
917     }
918     if (!proxy) {
919         proxy = RenderItemProxy::Create();
920     }
921 
922     proxy->AddChild(renderChild->GetRenderNode());
923     proxy->Attach(context_);
924     renderList_->AddChild(proxy);
925 }
926 
RequestNextFocus(bool vertical,bool reverse,const Rect & rect)927 bool ListElement::RequestNextFocus(bool vertical, bool reverse, const Rect& rect)
928 {
929     LOGD("[ListFocus]RequestNextFocus Vertical:%{public}d, Reverse:%{public}d.", vertical, reverse);
930     bool ret = false;
931     while (!ret) {
932         int32_t focusIndex = renderList_->RequestNextFocus(vertical, reverse);
933         if (focusIndex < 0) {
934             LOGW("No item can focus.");
935             return false;
936         }
937         for (auto focusNode : GetChildrenList()) {
938             auto listItem = AceType::DynamicCast<ListItemElement>(focusNode);
939             if (listItem && listItem->GetIndex() == focusIndex) {
940                 // If current Node can not obtain focus, move to next.
941                 if (!focusNode->IsFocusable()) {
942                     continue;
943                 }
944                 renderList_->CalculateFocusIndexPosition();
945                 ret = focusNode->RequestFocusImmediately();
946                 break;
947             }
948         }
949     }
950     return ret;
951 }
952 
MoveItemToViewPort(double position)953 void ListElement::MoveItemToViewPort(double position)
954 {
955     if (!needMoveFocusItem_) {
956         return;
957     }
958     LOGD("[ListFocus]MoveItemToViewPort position:%{public}.1lf.", position);
959     renderList_->MoveItemToViewPort(position);
960     needMoveFocusItem_ = false;
961 }
962 
MoveItemGroupToViewPort(double position,double size)963 void ListElement::MoveItemGroupToViewPort(double position, double size)
964 {
965     if (!needMoveFocusItem_) {
966         return;
967     }
968     LOGD("[ListFocus]MoveItemGroupToViewPort position:%{public}.1lf.", position);
969     renderList_->MoveItemGroupToViewPort(position, size);
970     needMoveFocusItem_ = false;
971 }
972 
SetGroupState(int32_t expandIndex,bool expand)973 void ListElement::SetGroupState(int32_t expandIndex, bool expand)
974 {
975     renderList_->SetGroupState(expandIndex, expand);
976 }
977 
AddItemGroupFocusIndex(int32_t groupIndex,int32_t groupFocusIndex)978 void ListElement::AddItemGroupFocusIndex(int32_t groupIndex, int32_t groupFocusIndex)
979 {
980     renderList_->GetLayoutManager()->AddItemGroupFocusIndex(groupIndex, groupFocusIndex);
981 }
982 
GetItemGroupFocusIndex(int32_t groupIndex)983 int32_t ListElement::GetItemGroupFocusIndex(int32_t groupIndex)
984 {
985     return renderList_->GetLayoutManager()->GetItemGroupFocusIndex(groupIndex);
986 }
987 
988 } // namespace OHOS::Ace
989