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