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 // Call AddChild to execute AttachToMainTree of new child.
157 // Allow adding default transition.
158 AddChild(*newCompsIter, DEFAULT_NODE_SLOT, false, true);
159 InitDragManager(*newCompsIter);
160 }
161 } else {
162 auto iter = oldNodeByIdMap.find(newId);
163 // the ID was used before, only need to update the child position.
164 if (iter != oldNodeByIdMap.end() && iter->second) {
165 AddChild(iter->second, DEFAULT_NODE_SLOT, true);
166 }
167 }
168 }
169 }
170
FlushUpdateAndMarkDirty()171 void ForEachNode::FlushUpdateAndMarkDirty()
172 {
173 if (ids_ == tempIds_ && !isThisRepeatNode_) {
174 tempIds_.clear();
175 return;
176 }
177 tempIds_.clear();
178 // mark parent dirty to flush measure.
179 MarkNeedSyncRenderTree(true);
180 MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT | PROPERTY_UPDATE_BY_CHILD_REQUEST);
181 }
182
183 // RepeatNode only
FinishRepeatRender(std::list<int32_t> & removedElmtId)184 void ForEachNode::FinishRepeatRender(std::list<int32_t>& removedElmtId)
185 {
186 ACE_SCOPED_TRACE("ForEachNode::FinishRepeatRender");
187
188 // Required to build unordered_set of RefPtr<UINodes>
189 struct Hash {
190 size_t operator()(const RefPtr<UINode>& node) const
191 {
192 return node->GetId();
193 }
194 };
195
196 // includes "newly-added" and "reused" children
197 const auto& children = GetChildren();
198
199 std::unordered_set<RefPtr<UINode>, Hash>
200 newNodeSet(children.begin(), children.end());
201
202 // remove "unused" children
203 for (const auto& oldNode: tempChildrenOfRepeat_) {
204 if (newNodeSet.find(oldNode) == newNodeSet.end()) {
205 // Adding silently, so that upon removal node is a part the tree.
206 AddChild(oldNode, DEFAULT_NODE_SLOT, true);
207 // Remove and trigger all Detach callback.
208 RemoveChild(oldNode, true);
209 // Collect IDs of removed nodes starting from 'oldNode' (incl.)
210 CollectRemovedChildren({ oldNode }, removedElmtId, false);
211 }
212 }
213
214 if (IsOnMainTree()) {
215 for (const auto& child : children) {
216 child->AttachToMainTree(false, GetContext());
217 }
218 }
219
220 tempChildren_.clear();
221 tempChildrenOfRepeat_.clear();
222
223 if (auto frameNode = GetParentFrameNode()) {
224 frameNode->ChildrenUpdatedFrom(0);
225 }
226 }
227
228 // RepeatNode only
MoveChild(uint32_t fromIndex)229 void ForEachNode::MoveChild(uint32_t fromIndex)
230 {
231 // copy child from tempChildrenOfRepeat_[fromIndex] and append to children_
232 if (fromIndex < tempChildrenOfRepeat_.size()) {
233 auto& node = tempChildrenOfRepeat_.at(fromIndex);
234 AddChild(node, DEFAULT_NODE_SLOT, true);
235 }
236 }
237
SetOnMove(std::function<void (int32_t,int32_t)> && onMove)238 void ForEachNode::SetOnMove(std::function<void(int32_t, int32_t)>&& onMove)
239 {
240 if (onMove && !onMoveEvent_) {
241 auto parentNode = GetParentFrameNode();
242 if (parentNode) {
243 InitAllChildrenDragManager(true);
244 } else {
245 auto piplineContext = GetContext();
246 CHECK_NULL_VOID(piplineContext);
247 auto taskExecutor = piplineContext->GetTaskExecutor();
248 CHECK_NULL_VOID(taskExecutor);
249 taskExecutor->PostTask(
250 [weak = WeakClaim(this)]() mutable {
251 auto forEach = weak.Upgrade();
252 CHECK_NULL_VOID(forEach);
253 forEach->InitAllChildrenDragManager(true);
254 },
255 TaskExecutor::TaskType::UI, "ArkUIInitAllChildrenDragManager");
256 }
257 } else if (!onMove && onMoveEvent_) {
258 InitAllChildrenDragManager(false);
259 }
260 onMoveEvent_ = onMove;
261 }
262
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)263 void ForEachNode::SetItemDragHandler(std::function<void(int32_t)>&& onLongPress,
264 std::function<void(int32_t)>&& onDragStart, std::function<void(int32_t, int32_t)>&& onMoveThrough,
265 std::function<void(int32_t)>&& onDrop)
266 {
267 if (onMoveEvent_) {
268 onLongPressEvent_ = onLongPress;
269 onDragStartEvent_ = onDragStart;
270 onMoveThroughEvent_ = onMoveThrough;
271 onDropEvent_ = onDrop;
272 }
273 }
274
MoveData(int32_t from,int32_t to)275 void ForEachNode::MoveData(int32_t from, int32_t to)
276 {
277 if (from == to) {
278 return;
279 }
280
281 auto idIter = ids_.begin();
282 std::advance(idIter, from);
283 auto id = *idIter;
284 ids_.erase(idIter);
285 idIter = ids_.begin();
286 std::advance(idIter, to);
287 ids_.insert(idIter, id);
288
289 auto& children = ModifyChildren();
290 auto fromIter = children.begin();
291 std::advance(fromIter, from);
292 auto child = *fromIter;
293 TraversingCheck(child);
294 children.erase(fromIter);
295 auto toIter = children.begin();
296 std::advance(toIter, to);
297 children.insert(toIter, child);
298 MarkNeedSyncRenderTree(true);
299 MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT | PROPERTY_UPDATE_BY_CHILD_REQUEST);
300 }
301
GetFrameNode(int32_t index)302 RefPtr<FrameNode> ForEachNode::GetFrameNode(int32_t index)
303 {
304 return AceType::DynamicCast<FrameNode>(GetFrameChildByIndex(index, false, false));
305 }
306
InitDragManager(const RefPtr<UINode> & child)307 void ForEachNode::InitDragManager(const RefPtr<UINode>& child)
308 {
309 CHECK_NULL_VOID(onMoveEvent_);
310 CHECK_NULL_VOID(child);
311 auto childNode = AceType::DynamicCast<FrameNode>(child->GetFrameChildByIndex(0, false));
312 CHECK_NULL_VOID(childNode);
313 auto parentNode = GetParentFrameNode();
314 CHECK_NULL_VOID(parentNode);
315 if (parentNode->GetTag() != V2::LIST_ETS_TAG) {
316 return;
317 }
318 auto pattern = childNode->GetPattern<ListItemPattern>();
319 CHECK_NULL_VOID(pattern);
320 pattern->InitDragManager(AceType::Claim(this));
321 }
322
InitAllChildrenDragManager(bool init)323 void ForEachNode::InitAllChildrenDragManager(bool init)
324 {
325 auto parentNode = GetParentFrameNode();
326 CHECK_NULL_VOID(parentNode);
327 if (parentNode->GetTag() != V2::LIST_ETS_TAG) {
328 onMoveEvent_ = nullptr;
329 onLongPressEvent_ = nullptr;
330 onDragStartEvent_ = nullptr;
331 onMoveThroughEvent_ = nullptr;
332 onDropEvent_ = nullptr;
333 return;
334 }
335 const auto& children = GetChildren();
336 for (const auto& child : children) {
337 if (!child || (child->GetChildren().size() != 1)) {
338 continue;
339 }
340 auto listItem = AceType::DynamicCast<FrameNode>(child->GetFirstChild());
341 if (!listItem) {
342 continue;
343 }
344 auto pattern = listItem->GetPattern<ListItemPattern>();
345 if (!pattern) {
346 continue;
347 }
348 if (init) {
349 pattern->InitDragManager(AceType::Claim(this));
350 } else {
351 pattern->DeInitDragManager();
352 }
353 }
354 }
355 } // namespace OHOS::Ace::NG
356