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