• 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                 // 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