• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_FOREACH_LAZY_FOR_EACH_BUILDER_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_FOREACH_LAZY_FOR_EACH_BUILDER_H
18 
19 #include <cstdint>
20 #include <list>
21 #include <map>
22 #include <optional>
23 #include <string>
24 #include <unordered_map>
25 #include <unordered_set>
26 #include <utility>
27 
28 #include "base/log/ace_trace.h"
29 #include "base/utils/noncopyable.h"
30 #include "base/utils/time_util.h"
31 #include "base/utils/utils.h"
32 #include "core/components_ng/base/frame_node.h"
33 #include "core/components_ng/base/inspector.h"
34 #include "core/components_ng/base/ui_node.h"
35 #include "core/components_ng/base/view_stack_processor.h"
36 #include "core/components_ng/pattern/list/list_item_pattern.h"
37 #include "core/components_v2/foreach/lazy_foreach_component.h"
38 
39 namespace OHOS::Ace::NG {
40 
41 using LazyForEachChild = std::pair<std::string, RefPtr<UINode>>;
42 using LazyForEachCacheChild = std::pair<int32_t, RefPtr<UINode>>;
43 
44 class ACE_EXPORT LazyForEachBuilder : public virtual AceType {
45     DECLARE_ACE_TYPE(NG::LazyForEachBuilder, AceType)
46 public:
47     LazyForEachBuilder() = default;
48     ~LazyForEachBuilder() override = default;
49 
GetTotalCount()50     int32_t GetTotalCount()
51     {
52         return OnGetTotalCount();
53     }
54 
55     std::pair<std::string, RefPtr<UINode>> GetChildByIndex(int32_t index, bool needBuild);
56 
ExpandChildrenOnInitial()57     void ExpandChildrenOnInitial()
58     {
59         OnExpandChildrenOnInitialInNG();
60     }
61 
OnDataReloaded()62     void OnDataReloaded()
63     {
64         for (auto& [key, node] : expiringItem_) {
65             node.first = -1;
66         }
67         for (auto& [index, node] : cachedItems_) {
68             if (node.second) {
69                 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(-1, std::move(node.second)));
70             }
71         }
72         cachedItems_.clear();
73         needTransition = true;
74     }
75 
OnDataAdded(size_t index)76     bool OnDataAdded(size_t index)
77     {
78         NotifyDataAdded(index);
79         if (!cachedItems_.empty() && index <= static_cast<size_t>(cachedItems_.rbegin()->first)) {
80             decltype(cachedItems_) temp(std::move(cachedItems_));
81 
82             for (auto& [oldindex, id] : temp) {
83                 cachedItems_.try_emplace(
84                     index > static_cast<size_t>(oldindex) ? oldindex : oldindex + 1, std::move(id));
85             }
86         }
87         for (auto& [key, node] : expiringItem_) {
88             if (static_cast<size_t>(node.first) >= index && node.first != -1) {
89                 node.first++;
90             }
91         }
92 
93         return true;
94     }
95 
OnDataDeleted(size_t index)96     RefPtr<UINode> OnDataDeleted(size_t index)
97     {
98         RefPtr<UINode> node;
99         if (cachedItems_.empty()) {
100             return node;
101         }
102         if (index <= static_cast<size_t>(cachedItems_.rbegin()->first)) {
103             decltype(cachedItems_) temp(std::move(cachedItems_));
104 
105             for (auto& [oldindex, child] : temp) {
106                 if (static_cast<size_t>(oldindex) == index) {
107                     node = child.second;
108                     KeepRemovedItemInCache(child, expiringItem_);
109                 } else {
110                     cachedItems_.try_emplace(
111                         index > static_cast<size_t>(oldindex) ? oldindex : oldindex - 1, std::move(child));
112                 }
113             }
114         }
115         NotifyDataDeleted(node, index, false);
116         for (auto& [key, child] : expiringItem_) {
117             if (static_cast<size_t>(child.first) > index) {
118                 child.first--;
119                 continue;
120             }
121             if (static_cast<size_t>(child.first) == index) {
122                 child.first = -1;
123                 node = child.second;
124             }
125         }
126 
127         return node;
128     }
129 
OnDataChanged(size_t index)130     bool OnDataChanged(size_t index)
131     {
132         auto keyIter = cachedItems_.find(index);
133         if (keyIter != cachedItems_.end()) {
134             if (keyIter->second.second) {
135                 NotifyDataChanged(index, keyIter->second.second, false);
136                 expiringItem_.try_emplace(
137                     keyIter->second.first, LazyForEachCacheChild(-1, std::move(keyIter->second.second)));
138             } else {
139                 InvalidIndexOfChangedData(index);
140             }
141             cachedItems_.erase(keyIter);
142             return true;
143         }
144         return false;
145     }
146 
OnDataMoved(size_t from,size_t to)147     bool OnDataMoved(size_t from, size_t to)
148     {
149         if (from == to) {
150             return false;
151         }
152         auto fromIter = cachedItems_.find(from);
153         auto toIter = cachedItems_.find(to);
154         if (fromIter != cachedItems_.end() && toIter != cachedItems_.end()) {
155             std::swap(fromIter->second, toIter->second);
156         } else if (fromIter != cachedItems_.end()) {
157             expiringItem_.try_emplace(
158                 fromIter->second.first, LazyForEachCacheChild(to, std::move(fromIter->second.second)));
159             cachedItems_.erase(fromIter);
160         } else if (toIter != cachedItems_.end()) {
161             expiringItem_.try_emplace(
162                 toIter->second.first, LazyForEachCacheChild(from, std::move(toIter->second.second)));
163             cachedItems_.erase(toIter);
164         }
165         return true;
166     }
167 
InvalidIndexOfChangedData(size_t index)168     void InvalidIndexOfChangedData(size_t index)
169     {
170         for (auto& [key, child] : expiringItem_) {
171             if (static_cast<size_t>(child.first) == index) {
172                 child.first = -1;
173                 break;
174             }
175         }
176     }
177 
GetChildByKey(const std::string & key)178     RefPtr<UINode> GetChildByKey(const std::string& key)
179     {
180         return nullptr;
181     }
182 
GetItems(std::list<RefPtr<UINode>> & childList)183     std::map<int32_t, LazyForEachChild>& GetItems(std::list<RefPtr<UINode>>& childList)
184     {
185         startIndex_ = -1;
186         endIndex_ = -1;
187         int32_t lastIndex = -1;
188         bool isCertained = false;
189 
190         decltype(cachedItems_) items(std::move(cachedItems_));
191 
192         for (auto& [index, node] : items) {
193             if (!node.second) {
194                 cachedItems_.try_emplace(index, std::move(node));
195                 continue;
196             }
197 
198             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
199             if (frameNode && !frameNode->IsActive()) {
200                 frameNode->SetJSViewActive(false);
201                 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
202                 continue;
203             }
204             cachedItems_.try_emplace(index, std::move(node));
205             if (startIndex_ == -1) {
206                 startIndex_ = index;
207             }
208             if (isLoop_) {
209                 if (isCertained) {
210                     continue;
211                 }
212                 if (lastIndex > -1 && index - lastIndex > 1) {
213                     startIndex_ = index;
214                     endIndex_ = lastIndex;
215                     isCertained = true;
216                 } else {
217                     endIndex_ = std::max(endIndex_, index);
218                 }
219             } else {
220                 endIndex_ = std::max(endIndex_, index);
221             }
222             lastIndex = index;
223         }
224 
225         if (needTransition) {
226             for (auto& [key, node] : expiringItem_) {
227                 if (!node.second) {
228                     continue;
229                 }
230                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
231                 if (frameNode && frameNode->IsOnMainTree()) {
232                     childList.push_back(node.second);
233                 }
234             }
235             needTransition = false;
236         }
237 
238         return cachedItems_;
239     }
240 
RemoveAllChild()241     void RemoveAllChild()
242     {
243         for (auto& [index, node] : cachedItems_) {
244             if (!node.second) {
245                 continue;
246             }
247             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
248             if (frameNode) {
249                 frameNode->SetActive(false);
250             }
251             expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
252         }
253     }
254 
SetActiveChildRange(int32_t start,int32_t end)255     void SetActiveChildRange(int32_t start, int32_t end)
256     {
257         for (auto& [index, node] : cachedItems_) {
258             if ((start <= end && start <= index && end >= index) ||
259                 (start > end && (index <= end || index >= start))) {
260                 if (node.second) {
261                     continue;
262                 }
263                 auto keyIter = expiringItem_.find(node.first);
264                 if (keyIter != expiringItem_.end() && keyIter->second.second) {
265                     node.second = keyIter->second.second;
266                     expiringItem_.erase(keyIter);
267                     auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
268                     if (frameNode) {
269                         frameNode->SetActive(true);
270                     }
271                 }
272                 continue;
273             }
274             if (!node.second) {
275                 continue;
276             }
277             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
278             if (frameNode) {
279                 frameNode->SetActive(false);
280             }
281             expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
282         }
283     }
284 
SetFlagForGeneratedItem(PropertyChangeFlag propertyChangeFlag)285     void SetFlagForGeneratedItem(PropertyChangeFlag propertyChangeFlag)
286     {
287         for (const auto& item : cachedItems_) {
288             if (!item.second.second) {
289                 continue;
290             }
291             item.second.second->ForceUpdateLayoutPropertyFlag(propertyChangeFlag);
292         }
293     }
294 
CacheItem(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,const std::optional<LayoutConstraintF> & itemConstraint,int64_t deadline,bool & isTimeout)295     RefPtr<UINode> CacheItem(int32_t index, std::unordered_map<std::string, LazyForEachCacheChild>& cache,
296         const std::optional<LayoutConstraintF>& itemConstraint, int64_t deadline, bool& isTimeout)
297     {
298         ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index);
299         auto itemInfo = OnGetChildByIndex(index, expiringItem_);
300         CHECK_NULL_RETURN(itemInfo.second, nullptr);
301         cache.try_emplace(itemInfo.first, LazyForEachCacheChild(index, itemInfo.second));
302         if (!itemInfo.second->RenderCustomChild(deadline)) {
303             isTimeout = true;
304             return itemInfo.second;
305         }
306         ProcessOffscreenNode(itemInfo.second, false);
307         ViewStackProcessor::GetInstance()->SetPredict(itemInfo.second);
308         itemInfo.second->Build(nullptr);
309         auto frameNode = AceType::DynamicCast<FrameNode>(itemInfo.second->GetFrameChildByIndex(0, false));
310         if (frameNode && frameNode->GetTag() == V2::LIST_ITEM_ETS_TAG) {
311             frameNode->GetPattern<ListItemPattern>()->BeforeCreateLayoutWrapper();
312         }
313         ViewStackProcessor::GetInstance()->ResetPredict();
314         itemInfo.second->SetJSViewActive(false);
315         cachedItems_[index] = LazyForEachChild(itemInfo.first, nullptr);
316 
317         return itemInfo.second;
318     }
319 
CheckCacheIndex(std::unordered_set<int32_t> & idleIndexes,int32_t count)320     void CheckCacheIndex(std::unordered_set<int32_t>& idleIndexes, int32_t count) {
321         for (int32_t i = 1; i <= cacheCount_; i++) {
322             if (isLoop_) {
323                 if ((startIndex_ <= endIndex_ && endIndex_ + i < count) ||
324                     startIndex_ > endIndex_ + i) {
325                     idleIndexes.emplace(endIndex_ + i);
326                 } else if ((endIndex_ + i) % count < startIndex_) {
327                     idleIndexes.emplace((endIndex_ + i) % count);
328                 }
329             } else {
330                 if (endIndex_ + i < count) {
331                     idleIndexes.emplace(endIndex_ + i);
332                 }
333             }
334         }
335         for (int32_t i = 1; i <= cacheCount_; i++) {
336             if (isLoop_) {
337                 if ((startIndex_ <= endIndex_ && startIndex_ >= i) ||
338                     startIndex_ > endIndex_ + i) {
339                     idleIndexes.emplace(startIndex_ - i);
340                 } else if ((startIndex_ - i + count) % count > endIndex_) {
341                     idleIndexes.emplace((startIndex_ - i + count) % count);
342                 }
343             } else {
344                 if (startIndex_ >= i) {
345                     idleIndexes.emplace(startIndex_ - i);
346                 }
347             }
348         }
349     }
350 
PreBuildByIndex(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)351     bool PreBuildByIndex(int32_t index, std::unordered_map<std::string, LazyForEachCacheChild>& cache,
352         int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask)
353     {
354         if (GetSysTimestamp() > deadline) {
355             return false;
356         }
357         bool isTimeout = false;
358         preBuildingIndex_ = -1;
359         auto uiNode = CacheItem(index, cache, itemConstraint, deadline, isTimeout);
360         if (isTimeout) {
361             preBuildingIndex_ = index;
362             return false;
363         }
364         if (!canRunLongPredictTask && itemConstraint) {
365             return false;
366         }
367         if (canRunLongPredictTask && uiNode && itemConstraint) {
368             RefPtr<FrameNode> frameNode = DynamicCast<FrameNode>(uiNode);
369             while (!frameNode) {
370                 auto tempNode = uiNode;
371                 uiNode = tempNode->GetFirstChild();
372                 if (!uiNode) {
373                     break;
374                 }
375                 frameNode = DynamicCast<FrameNode>(uiNode);
376             }
377             if (frameNode) {
378                 frameNode->GetGeometryNode()->SetParentLayoutConstraint(itemConstraint.value());
379                 FrameNode::ProcessOffscreenNode(frameNode);
380             }
381         }
382         return true;
383     }
384 
ProcessPreBuildingIndex(std::unordered_map<std::string,LazyForEachCacheChild> & cache,int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask,std::unordered_set<int32_t> & idleIndexes)385     bool ProcessPreBuildingIndex(std::unordered_map<std::string, LazyForEachCacheChild>& cache, int64_t deadline,
386         const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask,
387         std::unordered_set<int32_t>& idleIndexes)
388     {
389         if (idleIndexes.find(preBuildingIndex_) == idleIndexes.end()) {
390             preBuildingIndex_ = -1;
391             return true;
392         }
393         idleIndexes.erase(preBuildingIndex_);
394         return PreBuildByIndex(preBuildingIndex_, cache, deadline, itemConstraint, canRunLongPredictTask);
395     }
396 
PreBuild(int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)397     bool PreBuild(int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask)
398     {
399         ACE_SCOPED_TRACE("expiringItem_ count:[%zu]", expiringItem_.size());
400         if (itemConstraint && !canRunLongPredictTask) {
401             return false;
402         }
403         auto count = OnGetTotalCount();
404         std::unordered_map<std::string, LazyForEachCacheChild> cache;
405         std::unordered_set<int32_t> idleIndexes;
406         if (startIndex_ != -1 && endIndex_ != -1) {
407             CheckCacheIndex(idleIndexes, count);
408         }
409 
410         ProcessCachedIndex(cache, idleIndexes);
411 
412         bool result = true;
413         result = ProcessPreBuildingIndex(cache, deadline, itemConstraint, canRunLongPredictTask, idleIndexes);
414         if (!result) {
415             expiringItem_.swap(cache);
416             return result;
417         }
418 
419         for (auto index : idleIndexes) {
420             result = PreBuildByIndex(index, cache, deadline, itemConstraint, canRunLongPredictTask);
421             if (!result) {
422                 break;
423             }
424         }
425         expiringItem_.swap(cache);
426         return result;
427     }
428 
ProcessCachedIndex(std::unordered_map<std::string,LazyForEachCacheChild> & cache,std::unordered_set<int32_t> & idleIndexes)429     void ProcessCachedIndex(std::unordered_map<std::string, LazyForEachCacheChild>& cache,
430         std::unordered_set<int32_t>& idleIndexes)
431     {
432         for (auto& [key, node] : expiringItem_) {
433             auto iter = idleIndexes.find(node.first);
434             if (iter != idleIndexes.end() && node.second) {
435                 ProcessOffscreenNode(node.second, false);
436                 if (node.first == preBuildingIndex_) {
437                     cache.try_emplace(key, node);
438                 } else {
439                     cache.try_emplace(key, std::move(node));
440                     cachedItems_.try_emplace(node.first, LazyForEachChild(key, nullptr));
441                     idleIndexes.erase(iter);
442                 }
443             } else {
444                 NotifyDataDeleted(node.second, static_cast<size_t>(node.first), true);
445                 ProcessOffscreenNode(node.second, true);
446             }
447         }
448     }
449 
ProcessOffscreenNode(RefPtr<UINode> uiNode,bool remove)450     void ProcessOffscreenNode(RefPtr<UINode> uiNode, bool remove)
451     {
452         if (uiNode) {
453             auto frameNode = DynamicCast<FrameNode>(uiNode);
454             while (!frameNode) {
455                 auto tempNode = uiNode;
456                 uiNode = tempNode->GetFirstChild();
457                 if (!uiNode) {
458                     break;
459                 }
460                 frameNode = DynamicCast<FrameNode>(uiNode);
461             }
462             if (frameNode) {
463                 if (!remove) {
464                     Inspector::AddOffscreenNode(frameNode);
465                 } else {
466                     Inspector::RemoveOffscreenNode(frameNode);
467                 }
468             }
469         }
470     }
471 
ClearAllOffscreenNode()472     void ClearAllOffscreenNode()
473     {
474         for (auto& [key, node] : expiringItem_) {
475             ProcessOffscreenNode(node.second, true);
476         }
477         for (auto& [key, node] : cachedItems_) {
478             ProcessOffscreenNode(node.second, true);
479         }
480     }
481 
482     virtual void ReleaseChildGroupById(const std::string& id) = 0;
483 
484     virtual void RegisterDataChangeListener(const RefPtr<V2::DataChangeListener>& listener) = 0;
485 
486     virtual void UnregisterDataChangeListener(V2::DataChangeListener* listener) = 0;
487 
SetCacheCount(int32_t cacheCount)488     void SetCacheCount(int32_t cacheCount)
489     {
490         cacheCount_ = cacheCount;
491     }
492 
SetIsLoop(bool isLoop)493     void SetIsLoop(bool isLoop)
494     {
495         isLoop_ = isLoop;
496     }
497 
GetCachedUINodeMap()498     const std::unordered_map<std::string, LazyForEachCacheChild>& GetCachedUINodeMap()
499     {
500         return expiringItem_;
501     }
502 
GetAllChildren()503     const std::map<int32_t, LazyForEachChild>& GetAllChildren()
504     {
505         if (!cachedItems_.empty()) {
506             startIndex_ = cachedItems_.begin()->first;
507             endIndex_ = cachedItems_.rbegin()->first;
508         }
509         if (isLoop_ && !cachedItems_.empty()) {
510             int32_t lastIndex = -1;
511             for (auto& [index, node] : cachedItems_) {
512                 if (lastIndex > -1 && index - lastIndex > 1) {
513                     startIndex_ = index;
514                     endIndex_ = lastIndex;
515                     break;
516                 }
517             }
518         }
519         return cachedItems_;
520     }
521 
SetJSViewActive(bool active)522     void SetJSViewActive(bool active)
523     {
524         for (const auto& node : cachedItems_) {
525             if (node.second.second == nullptr) {
526                 continue;
527             }
528             node.second.second->SetJSViewActive(active);
529         }
530         for (const auto& node : expiringItem_) {
531             node.second.second->SetJSViewActive(active);
532         }
533     }
534 
535 protected:
536     virtual int32_t OnGetTotalCount() = 0;
537 
538     virtual LazyForEachChild OnGetChildByIndex(
539         int32_t index, std::unordered_map<std::string, LazyForEachCacheChild>& cachedItems) = 0;
540 
541     virtual void OnExpandChildrenOnInitialInNG() = 0;
542 
543     virtual void NotifyDataChanged(size_t index, RefPtr<UINode>& lazyForEachNode, bool isRebuild = true) = 0;
544 
545     virtual void NotifyDataDeleted(RefPtr<UINode>& lazyForEachNode, size_t index, bool removeIds) = 0;
546 
547     virtual void NotifyDataAdded(size_t index) = 0;
548 
549     virtual void KeepRemovedItemInCache(NG::LazyForEachChild node,
550         std::unordered_map<std::string, NG::LazyForEachCacheChild>& cachedItems) = 0;
551 
552 private:
553     std::map<int32_t, LazyForEachChild> cachedItems_;
554     std::unordered_map<std::string, LazyForEachCacheChild> expiringItem_;
555 
556     int32_t startIndex_ = -1;
557     int32_t endIndex_ = -1;
558     int32_t cacheCount_ = 0;
559     int32_t preBuildingIndex_ = -1;
560     bool needTransition = false;
561     bool isLoop_ = false;
562     ACE_DISALLOW_COPY_AND_MOVE(LazyForEachBuilder);
563 };
564 } // namespace OHOS::Ace::NG
565 
566 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_FOREACH_LAZY_FOR_EACH_BUILDER_H
567