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