• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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