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