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