1 /*
2 * Copyright (c) 2022-2024 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_ng/syntax/for_each_node.h"
17
18 #include "base/log/ace_trace.h"
19 #include "core/components_ng/base/frame_node.h"
20 #include "core/pipeline/base/element_register.h"
21
22 namespace OHOS::Ace::NG {
23 namespace {
MakeNodeMapById(const std::list<RefPtr<UINode>> & nodes,const std::list<std::string> & ids,std::map<std::string,RefPtr<UINode>> & result)24 void MakeNodeMapById(const std::list<RefPtr<UINode>>& nodes, const std::list<std::string>& ids,
25 std::map<std::string, RefPtr<UINode>>& result)
26 {
27 ACE_DCHECK(ids.size() == nodes.size());
28 auto idsIter = ids.begin();
29 auto nodeIter = nodes.begin();
30 while (idsIter != ids.end() && nodeIter != nodes.end()) {
31 result.emplace(*idsIter, *nodeIter);
32 ++idsIter;
33 ++nodeIter;
34 }
35 }
36 } // namespace
37
GetOrCreateForEachNode(int32_t nodeId)38 RefPtr<ForEachNode> ForEachNode::GetOrCreateForEachNode(int32_t nodeId)
39 {
40 auto node = ElementRegister::GetInstance()->GetSpecificItemById<ForEachNode>(nodeId);
41 if (node) {
42 return node;
43 }
44 node = MakeRefPtr<ForEachNode>(nodeId);
45 ElementRegister::GetInstance()->AddUINode(node);
46 return node;
47 }
48
CreateTempItems()49 void ForEachNode::CreateTempItems()
50 {
51 std::swap(ids_, tempIds_);
52 std::swap(ModifyChildren(), tempChildren_);
53 }
54
55 // same as foundation/arkui/ace_engine/frameworks/core/components_part_upd/foreach/foreach_element.cpp.
CompareAndUpdateChildren()56 void ForEachNode::CompareAndUpdateChildren()
57 {
58 ACE_SCOPED_TRACE("ForEachNode::CompareAndUpdateChildren");
59
60 // result of id gen function of most re-recent render
61 // create a map for quicker find/search
62 std::unordered_set<std::string> newIdsSet(ids_.begin(), ids_.end());
63
64 // result of id gen function of previous render/re-render
65 // create a map for quicker find/search
66 std::unordered_set<std::string> oldIdsSet(tempIds_.begin(), tempIds_.end());
67
68 // ForEachNode only includes children for newly created_ array items
69 // it does not include children of array items that were rendered on a previous
70 // render
71 std::list<RefPtr<UINode>> additionalChildComps;
72 auto& children = ModifyChildren();
73 std::swap(additionalChildComps, children);
74
75 // create map id -> Node
76 // old children
77 std::map<std::string, RefPtr<UINode>> oldNodeByIdMap;
78 MakeNodeMapById(tempChildren_, tempIds_, oldNodeByIdMap);
79
80 int32_t additionalChildIndex = 0;
81 for (const auto& newId : ids_) {
82 auto oldIdIt = oldIdsSet.find(newId);
83 if (oldIdIt == oldIdsSet.end()) {
84 // found a newly added ID
85 // insert new child item.
86 auto newCompsIter = additionalChildComps.begin();
87 std::advance(newCompsIter, additionalChildIndex++);
88 if (newCompsIter != additionalChildComps.end()) {
89 // Call AddChild to execute AttachToMainTree of new child.
90 // Allow adding default transition.
91 AddChild(*newCompsIter, DEFAULT_NODE_SLOT, false, true);
92 }
93 } else {
94 auto iter = oldNodeByIdMap.find(newId);
95 // the ID was used before, only need to update the child position.
96 if (iter != oldNodeByIdMap.end() && iter->second) {
97 AddChild(iter->second, DEFAULT_NODE_SLOT, true);
98 }
99 oldIdsSet.erase(oldIdIt);
100 }
101 }
102
103 for (const auto& oldId : oldIdsSet) {
104 auto iter = oldNodeByIdMap.find(oldId);
105 if (iter != oldNodeByIdMap.end()) {
106 // Adding silently, so that upon removal
107 // node is a part the tree.
108 // OnDetachFromMainTree to be called while node
109 // still part of the tree, we need to find
110 // position in the tab tab for the tab.
111 AddChild(iter->second, DEFAULT_NODE_SLOT, true);
112 // Remove and trigger all Detach callback.
113 RemoveChild(iter->second, true);
114 }
115 }
116
117 if (IsOnMainTree()) {
118 for (const auto& newChild : additionalChildComps) {
119 newChild->AttachToMainTree();
120 }
121 }
122
123 tempChildren_.clear();
124
125 auto parent = GetParent();
126 while (parent) {
127 auto frameNode = AceType::DynamicCast<FrameNode>(parent);
128 if (frameNode) {
129 frameNode->ChildrenUpdatedFrom(0);
130 break;
131 }
132 parent = parent->GetParent();
133 }
134 }
135
FlushUpdateAndMarkDirty()136 void ForEachNode::FlushUpdateAndMarkDirty()
137 {
138 if (ids_ == tempIds_) {
139 tempIds_.clear();
140 return;
141 }
142 tempIds_.clear();
143 // mark parent dirty to flush measure.
144 MarkNeedSyncRenderTree(true);
145 MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT | PROPERTY_UPDATE_BY_CHILD_REQUEST);
146 }
147
148 } // namespace OHOS::Ace::NG
149