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/components_ng/pattern/list/list_item_pattern.h"
21 #include "core/pipeline/base/element_register.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23
24 namespace OHOS::Ace::NG {
25 namespace {
MakeNodeMapById(const std::list<RefPtr<UINode>> & nodes,const std::list<std::string> & ids,std::map<std::string,RefPtr<UINode>> & result)26 void MakeNodeMapById(const std::list<RefPtr<UINode>>& nodes, const std::list<std::string>& ids,
27 std::map<std::string, RefPtr<UINode>>& result)
28 {
29 ACE_DCHECK(ids.size() == nodes.size());
30 auto idsIter = ids.begin();
31 auto nodeIter = nodes.begin();
32 while (idsIter != ids.end() && nodeIter != nodes.end()) {
33 result.emplace(*idsIter, *nodeIter);
34 ++idsIter;
35 ++nodeIter;
36 }
37 }
38 } // namespace
39
GetOrCreateForEachNode(int32_t nodeId)40 RefPtr<ForEachNode> ForEachNode::GetOrCreateForEachNode(int32_t nodeId)
41 {
42 auto node = ElementRegister::GetInstance()->GetSpecificItemById<ForEachNode>(nodeId);
43 if (node) {
44 return node;
45 }
46 node = MakeRefPtr<ForEachNode>(nodeId);
47 ElementRegister::GetInstance()->AddUINode(node);
48 return node;
49 }
50
GetOrCreateRepeatNode(int32_t nodeId)51 RefPtr<ForEachNode> ForEachNode::GetOrCreateRepeatNode(int32_t nodeId)
52 {
53 auto node = ForEachNode::GetOrCreateForEachNode(nodeId);
54 if (node) {
55 node->isThisRepeatNode_ = true;
56 }
57 return node;
58 }
59
CreateTempItems()60 void ForEachNode::CreateTempItems()
61 {
62 std::swap(ids_, tempIds_);
63 TraversingCheck();
64 std::swap(ModifyChildren(), tempChildren_);
65
66 // RepeatNode only
67 if (isThisRepeatNode_) {
68 tempChildrenOfRepeat_ = std::vector<RefPtr<UINode>>(tempChildren_.begin(), tempChildren_.end());
69 }
70 }
71
CollectRemovingIds(std::list<int32_t> & removedElmtId)72 void ForEachNode::CollectRemovingIds(std::list<int32_t>& removedElmtId)
73 {
74 tempOldIdsSet_.insert(tempIds_.begin(), tempIds_.end());
75 MakeNodeMapById(tempChildren_, tempIds_, oldNodeByIdMap_);
76
77 for (const auto& newId : ids_) {
78 auto oldIdIt = tempOldIdsSet_.find(newId);
79 if (oldIdIt != tempOldIdsSet_.end()) {
80 tempOldIdsSet_.erase(oldIdIt);
81 }
82 }
83
84 for (const auto& oldId : tempOldIdsSet_) {
85 auto iter = oldNodeByIdMap_.find(oldId);
86 if (iter != oldNodeByIdMap_.end()) {
87 CollectRemovedChildren({ iter->second }, removedElmtId, false);
88 }
89 }
90 }
91
92 // same as foundation/arkui/ace_engine/frameworks/core/components_part_upd/foreach/foreach_element.cpp.
CompareAndUpdateChildren()93 void ForEachNode::CompareAndUpdateChildren()
94 {
95 if (isThisRepeatNode_) {
96 return;
97 }
98
99 // result of id gen function of most re-recent render
100 // create a map for quicker find/search
101 std::unordered_set<std::string> newIdsSet(ids_.begin(), ids_.end());
102
103 // result of id gen function of previous render/re-render
104 // create a map for quicker find/search
105 std::unordered_set<std::string> oldIdsSet(tempIds_.begin(), tempIds_.end());
106
107 // ForEachNode only includes children for newly created_ array items
108 // it does not include children of array items that were rendered on a previous
109 // render
110 std::list<RefPtr<UINode>> additionalChildComps;
111 auto& children = ModifyChildren();
112
113 // swap new children to tempChildren, old children back to children
114 std::swap(children, tempChildren_);
115
116 for (const auto& oldId : tempOldIdsSet_) {
117 auto iter = oldNodeByIdMap_.find(oldId);
118 if (iter != oldNodeByIdMap_.end()) {
119 // Remove and trigger all Detach callback.
120 RemoveChild(iter->second, true);
121 }
122 }
123
124 std::swap(additionalChildComps, tempChildren_);
125 std::swap(children, tempChildren_);
126
127 MappingChildWithId(oldIdsSet, additionalChildComps, oldNodeByIdMap_);
128
129 ACE_SCOPED_TRACE("ForEachNode::Update Id[%d] preIds[%zu] newIds[%zu] tempOldIdsSet[%zu] additionalChildComps[%zu]",
130 GetId(), tempIds_.size(), ids_.size(), tempOldIdsSet_.size(), additionalChildComps.size());
131
132 if (IsOnMainTree()) {
133 for (const auto& newChild : additionalChildComps) {
134 newChild->AttachToMainTree(false, GetContext());
135 }
136 }
137
138 tempChildren_.clear();
139 tempOldIdsSet_.clear();
140 oldNodeByIdMap_.clear();
141
142 if (auto frameNode = GetParentFrameNode()) {
143 frameNode->ChildrenUpdatedFrom(0);
144 }
145 }
146
MappingChildWithId(std::unordered_set<std::string> & oldIdsSet,std::list<RefPtr<UINode>> & additionalChildComps,std::map<std::string,RefPtr<UINode>> & oldNodeByIdMap)147 void ForEachNode::MappingChildWithId(std::unordered_set<std::string>& oldIdsSet,
148 std::list<RefPtr<UINode>>& additionalChildComps, std::map<std::string, RefPtr<UINode>>& oldNodeByIdMap)
149 {
150 int32_t additionalChildIndex = 0;
151 for (const auto& newId : ids_) {
152 auto oldIdIt = oldIdsSet.find(newId);
153 if (oldIdIt == oldIdsSet.end()) {
154 // found a newly added ID
155 // insert new child item.
156 auto newCompsIter = additionalChildComps.begin();
157 std::advance(newCompsIter, additionalChildIndex++);
158 if (newCompsIter != additionalChildComps.end()) {
159 // Call AddChild to execute AttachToMainTree of new child.
160 // Allow adding default transition.
161 AddChild(*newCompsIter, DEFAULT_NODE_SLOT, false, true);
162 InitDragManager(*newCompsIter);
163 }
164 } else {
165 auto iter = oldNodeByIdMap.find(newId);
166 // the ID was used before, only need to update the child position.
167 if (iter != oldNodeByIdMap.end() && iter->second) {
168 AddChild(iter->second, DEFAULT_NODE_SLOT, true);
169 }
170 }
171 }
172 }
173
FlushUpdateAndMarkDirty()174 void ForEachNode::FlushUpdateAndMarkDirty()
175 {
176 if (ids_ == tempIds_ && !isThisRepeatNode_) {
177 tempIds_.clear();
178 return;
179 }
180 tempIds_.clear();
181 // mark parent dirty to flush measure.
182 MarkNeedSyncRenderTree(true);
183 MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT | PROPERTY_UPDATE_BY_CHILD_REQUEST);
184 }
185
186 // RepeatNode only
FinishRepeatRender(std::list<int32_t> & removedElmtId)187 void ForEachNode::FinishRepeatRender(std::list<int32_t>& removedElmtId)
188 {
189 ACE_SCOPED_TRACE("ForEachNode::FinishRepeatRender");
190
191 // Required to build unordered_set of RefPtr<UINodes>
192 struct Hash {
193 size_t operator()(const RefPtr<UINode>& node) const
194 {
195 return node->GetId();
196 }
197 };
198
199 // includes "newly-added" and "reused" children
200 const auto& children = GetChildren();
201
202 std::unordered_set<RefPtr<UINode>, Hash>
203 newNodeSet(children.begin(), children.end());
204
205 // remove "unused" children
206 for (const auto& oldNode: tempChildrenOfRepeat_) {
207 if (newNodeSet.find(oldNode) == newNodeSet.end()) {
208 // Adding silently, so that upon removal node is a part the tree.
209 AddChild(oldNode, DEFAULT_NODE_SLOT, true);
210 // Remove and trigger all Detach callback.
211 RemoveChild(oldNode, true);
212 // Collect IDs of removed nodes starting from 'oldNode' (incl.)
213 CollectRemovedChildren({ oldNode }, removedElmtId, false);
214 }
215 }
216
217 if (IsOnMainTree()) {
218 for (const auto& child : children) {
219 child->AttachToMainTree(false, GetContext());
220 }
221 }
222
223 tempChildren_.clear();
224 tempChildrenOfRepeat_.clear();
225
226 if (auto frameNode = GetParentFrameNode()) {
227 frameNode->ChildrenUpdatedFrom(0);
228 }
229 }
230
231 // RepeatNode only
MoveChild(uint32_t fromIndex)232 void ForEachNode::MoveChild(uint32_t fromIndex)
233 {
234 // copy child from tempChildrenOfRepeat_[fromIndex] and append to children_
235 if (fromIndex < tempChildrenOfRepeat_.size()) {
236 auto& node = tempChildrenOfRepeat_.at(fromIndex);
237 AddChild(node, DEFAULT_NODE_SLOT, true);
238 }
239 }
240
SetOnMove(std::function<void (int32_t,int32_t)> && onMove)241 void ForEachNode::SetOnMove(std::function<void(int32_t, int32_t)>&& onMove)
242 {
243 if (onMove && !onMoveEvent_) {
244 auto parentNode = GetParentFrameNode();
245 if (parentNode) {
246 InitAllChildrenDragManager(true);
247 } else {
248 auto piplineContext = GetContext();
249 CHECK_NULL_VOID(piplineContext);
250 auto taskExecutor = piplineContext->GetTaskExecutor();
251 CHECK_NULL_VOID(taskExecutor);
252 taskExecutor->PostTask(
253 [weak = WeakClaim(this)]() mutable {
254 auto forEach = weak.Upgrade();
255 CHECK_NULL_VOID(forEach);
256 forEach->InitAllChildrenDragManager(true);
257 },
258 TaskExecutor::TaskType::UI, "ArkUIInitAllChildrenDragManager");
259 }
260 } else if (!onMove && onMoveEvent_) {
261 InitAllChildrenDragManager(false);
262 }
263 onMoveEvent_ = onMove;
264 }
265
MoveData(int32_t from,int32_t to)266 void ForEachNode::MoveData(int32_t from, int32_t to)
267 {
268 if (from == to) {
269 return;
270 }
271
272 auto idIter = ids_.begin();
273 std::advance(idIter, from);
274 auto id = *idIter;
275 ids_.erase(idIter);
276 idIter = ids_.begin();
277 std::advance(idIter, to);
278 ids_.insert(idIter, id);
279
280 auto& children = ModifyChildren();
281 auto fromIter = children.begin();
282 std::advance(fromIter, from);
283 auto child = *fromIter;
284 TraversingCheck(child);
285 children.erase(fromIter);
286 auto toIter = children.begin();
287 std::advance(toIter, to);
288 children.insert(toIter, child);
289 MarkNeedSyncRenderTree(true);
290 MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT | PROPERTY_UPDATE_BY_CHILD_REQUEST);
291 }
292
GetFrameNode(int32_t index)293 RefPtr<FrameNode> ForEachNode::GetFrameNode(int32_t index)
294 {
295 return AceType::DynamicCast<FrameNode>(GetFrameChildByIndex(index, false, false));
296 }
297
InitDragManager(const RefPtr<UINode> & child)298 void ForEachNode::InitDragManager(const RefPtr<UINode>& child)
299 {
300 CHECK_NULL_VOID(onMoveEvent_);
301 CHECK_NULL_VOID(child);
302 auto childNode = AceType::DynamicCast<FrameNode>(child->GetFrameChildByIndex(0, false));
303 CHECK_NULL_VOID(childNode);
304 auto parentNode = GetParentFrameNode();
305 CHECK_NULL_VOID(parentNode);
306 if (parentNode->GetTag() != V2::LIST_ETS_TAG) {
307 return;
308 }
309 auto pattern = childNode->GetPattern<ListItemPattern>();
310 CHECK_NULL_VOID(pattern);
311 pattern->InitDragManager(AceType::Claim(this));
312 }
313
InitAllChildrenDragManager(bool init)314 void ForEachNode::InitAllChildrenDragManager(bool init)
315 {
316 auto parentNode = GetParentFrameNode();
317 CHECK_NULL_VOID(parentNode);
318 if (parentNode->GetTag() != V2::LIST_ETS_TAG) {
319 onMoveEvent_ = nullptr;
320 return;
321 }
322 const auto& children = GetChildren();
323 for (const auto& child : children) {
324 if (!child || (child->GetChildren().size() != 1)) {
325 continue;
326 }
327 auto listItem = AceType::DynamicCast<FrameNode>(child->GetFirstChild());
328 if (!listItem) {
329 continue;
330 }
331 auto pattern = listItem->GetPattern<ListItemPattern>();
332 if (!pattern) {
333 continue;
334 }
335 if (init) {
336 pattern->InitDragManager(AceType::Claim(this));
337 } else {
338 pattern->DeInitDragManager();
339 }
340 }
341 }
342 } // namespace OHOS::Ace::NG
343