• 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/ui_node.h"
34 #include "core/components_ng/base/view_stack_processor.h"
35 #include "core/components_v2/foreach/lazy_foreach_component.h"
36 
37 namespace OHOS::Ace::NG {
38 
39 using LazyForEachChild = std::pair<std::string, RefPtr<UINode>>;
40 using LazyForEachCacheChild = std::pair<int32_t, RefPtr<UINode>>;
41 
42 class ACE_EXPORT LazyForEachBuilder : public virtual AceType {
43     DECLARE_ACE_TYPE(NG::LazyForEachBuilder, AceType)
44 public:
45     LazyForEachBuilder() = default;
46     ~LazyForEachBuilder() override = default;
47 
GetTotalCount()48     int32_t GetTotalCount()
49     {
50         return OnGetTotalCount();
51     }
52 
GetChildByIndex(int32_t index,bool needBuild)53     std::pair<std::string, RefPtr<UINode>> GetChildByIndex(int32_t index, bool needBuild)
54     {
55         auto iter = cachedItems_.find(index);
56         if (iter != cachedItems_.end()) {
57             if (iter->second.second) {
58                 return iter->second;
59             }
60             auto keyIter = expiringItem_.find(iter->second.first);
61             if (keyIter != expiringItem_.end() && keyIter->second.second) {
62                 iter->second.second = keyIter->second.second;
63                 expiringItem_.erase(keyIter);
64                 return iter->second;
65             }
66         }
67 
68         if (needBuild) {
69             ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index);
70             auto itemInfo = OnGetChildByIndex(index, expiringItem_);
71             CHECK_NULL_RETURN(itemInfo.second, itemInfo);
72             {
73                 cachedItems_[index] = itemInfo;
74             }
75             return itemInfo;
76         }
77         return {};
78     }
79 
ExpandChildrenOnInitial()80     void ExpandChildrenOnInitial()
81     {
82         OnExpandChildrenOnInitialInNG();
83     }
84 
OnDataReloaded()85     void OnDataReloaded()
86     {
87         for (auto& [key, node] : expiringItem_) {
88             node.first = -1;
89         }
90         for (auto& [index, node] : cachedItems_) {
91             if (node.second) {
92                 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(-1, std::move(node.second)));
93             }
94         }
95         cachedItems_.clear();
96         needTransition = true;
97     }
98 
OnDataAdded(size_t index)99     bool OnDataAdded(size_t index)
100     {
101         if (!cachedItems_.empty() && index <= static_cast<size_t>(cachedItems_.rbegin()->first)) {
102             decltype(cachedItems_) temp(std::move(cachedItems_));
103 
104             for (auto& [oldindex, id] : temp) {
105                 cachedItems_.try_emplace(
106                     index > static_cast<size_t>(oldindex) ? oldindex : oldindex + 1, std::move(id));
107             }
108         }
109         for (auto& [key, node] : expiringItem_) {
110             if (static_cast<size_t>(node.first) >= index) {
111                 node.first++;
112             }
113         }
114 
115         return true;
116     }
117 
OnDataDeleted(size_t index)118     RefPtr<UINode> OnDataDeleted(size_t index)
119     {
120         RefPtr<UINode> node;
121         if (cachedItems_.empty()) {
122             return node;
123         }
124         if (index <= static_cast<size_t>(cachedItems_.rbegin()->first)) {
125             decltype(cachedItems_) temp(std::move(cachedItems_));
126 
127             for (auto& [oldindex, child] : temp) {
128                 if (oldindex == index) {
129                     node = child.second;
130                 } else {
131                     cachedItems_.try_emplace(
132                         index > static_cast<size_t>(oldindex) ? oldindex : oldindex - 1, std::move(child));
133                 }
134             }
135         }
136         for (auto& [key, child] : expiringItem_) {
137             if (static_cast<size_t>(child.first) > index) {
138                 child.first--;
139             }
140             if (static_cast<size_t>(child.first) == index) {
141                 child.first = -1;
142                 node = child.second;
143             }
144         }
145 
146         return node;
147     }
148 
OnDataChanged(size_t index)149     bool OnDataChanged(size_t index)
150     {
151         auto keyIter = cachedItems_.find(index);
152         if (keyIter != cachedItems_.end()) {
153             if (keyIter->second.second) {
154                 expiringItem_.try_emplace(
155                     keyIter->second.first, LazyForEachCacheChild(-1, std::move(keyIter->second.second)));
156             }
157             cachedItems_.erase(keyIter);
158             return true;
159         }
160         return false;
161     }
162 
OnDataMoved(size_t from,size_t to)163     bool OnDataMoved(size_t from, size_t to)
164     {
165         return true;
166     }
167 
GetChildByKey(const std::string & key)168     RefPtr<UINode> GetChildByKey(const std::string& key)
169     {
170         return nullptr;
171     }
172 
GetItems(std::list<RefPtr<UINode>> & childList)173     std::map<int32_t, LazyForEachChild>& GetItems(std::list<RefPtr<UINode>>& childList)
174     {
175         startIndex_ = -1;
176         endIndex_ = -1;
177 
178         decltype(cachedItems_) items(std::move(cachedItems_));
179 
180         for (auto& [index, node] : items) {
181             if (!node.second) {
182                 cachedItems_.try_emplace(index, std::move(node));
183                 continue;
184             }
185 
186             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
187             if (frameNode && !frameNode->IsActive()) {
188                 frameNode->SetJSViewActive(false);
189                 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
190                 continue;
191             }
192             cachedItems_.try_emplace(index, std::move(node));
193             if (startIndex_ == -1) {
194                 startIndex_ = index;
195             }
196             endIndex_ = std::max(endIndex_, index);
197         }
198 
199         if (needTransition) {
200             for (auto& [key, node] : expiringItem_) {
201                 if (!node.second) {
202                     continue;
203                 }
204                 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
205                 if (frameNode && frameNode->IsOnMainTree()) {
206                     childList.push_back(node.second);
207                 }
208             }
209             needTransition = false;
210         }
211 
212         return cachedItems_;
213     }
214 
RemoveAllChild()215     void RemoveAllChild()
216     {
217         for (auto& [index, node] : cachedItems_) {
218             if (!node.second) {
219                 continue;
220             }
221             auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true));
222             if (frameNode) {
223                 frameNode->SetActive(false);
224             }
225             node.second->SetJSViewActive(false);
226             expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second)));
227         }
228     }
229 
SetFlagForGeneratedItem(PropertyChangeFlag propertyChangeFlag)230     void SetFlagForGeneratedItem(PropertyChangeFlag propertyChangeFlag)
231     {
232         for (const auto& item : cachedItems_) {
233             if (!item.second.second) {
234                 continue;
235             }
236             item.second.second->ForceUpdateLayoutPropertyFlag(propertyChangeFlag);
237         }
238     }
239 
CacheItem(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,const std::optional<LayoutConstraintF> & itemConstraint)240     RefPtr<UINode> CacheItem(int32_t index, std::unordered_map<std::string, LazyForEachCacheChild>& cache,
241         const std::optional<LayoutConstraintF>& itemConstraint)
242     {
243         ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index);
244         auto itemInfo = OnGetChildByIndex(index, expiringItem_);
245         CHECK_NULL_RETURN(itemInfo.second, nullptr);
246         cache.try_emplace(itemInfo.first, LazyForEachCacheChild(index, itemInfo.second));
247         ViewStackProcessor::GetInstance()->SetPredict(itemInfo.second);
248         itemInfo.second->Build();
249         ViewStackProcessor::GetInstance()->ResetPredict();
250         itemInfo.second->SetJSViewActive(false);
251         cachedItems_[index] = LazyForEachChild(itemInfo.first, nullptr);
252 
253         return itemInfo.second;
254     }
255 
PreBuild(int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)256     bool PreBuild(int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask)
257     {
258         ACE_SCOPED_TRACE("expiringItem_ count:[%zu]", expiringItem_.size());
259         auto count = OnGetTotalCount();
260         std::unordered_map<std::string, LazyForEachCacheChild> cache;
261         std::unordered_set<int32_t> idleIndexes;
262         if (startIndex_ != -1 && endIndex_ != -1) {
263             for (int32_t i = 1; i <= cacheCount_; i++) {
264                 if (startIndex_ >= i) {
265                     idleIndexes.emplace(startIndex_ - i);
266                 }
267                 if (endIndex_ + i < count) {
268                     idleIndexes.emplace(endIndex_ + i);
269                 }
270             }
271         }
272 
273         for (auto& [key, node] : expiringItem_) {
274             auto iter = idleIndexes.find(node.first);
275             if (iter != idleIndexes.end() && node.second) {
276                 cache.try_emplace(key, std::move(node));
277                 cachedItems_.try_emplace(node.first, LazyForEachChild(key, nullptr));
278                 idleIndexes.erase(iter);
279             }
280         }
281 
282         bool result = true;
283         for (auto index : idleIndexes) {
284             if (GetSysTimestamp() > deadline) {
285                 result = false;
286                 continue;
287             }
288             auto uiNode = CacheItem(index, cache, itemConstraint);
289             if (!canRunLongPredictTask && itemConstraint) {
290                 result = false;
291                 continue;
292             }
293             if (canRunLongPredictTask && uiNode && itemConstraint) {
294                 RefPtr<FrameNode> frameNode = DynamicCast<FrameNode>(uiNode);
295                 while (!frameNode) {
296                     uiNode = uiNode->GetFirstChild();
297                     if (!uiNode) {
298                         break;
299                     }
300                     frameNode = DynamicCast<FrameNode>(uiNode);
301                 }
302                 if (frameNode) {
303                     frameNode->GetGeometryNode()->SetParentLayoutConstraint(itemConstraint.value());
304                     FrameNode::ProcessOffscreenNode(frameNode);
305                 }
306             }
307         }
308         expiringItem_.swap(cache);
309         return result;
310     }
311 
312     virtual void ReleaseChildGroupById(const std::string& id) = 0;
313 
314     virtual void RegisterDataChangeListener(const RefPtr<V2::DataChangeListener>& listener) = 0;
315 
316     virtual void UnregisterDataChangeListener(const RefPtr<V2::DataChangeListener>& listener) = 0;
317 
SetCacheCount(int32_t cacheCount)318     void SetCacheCount(int32_t cacheCount)
319     {
320         cacheCount_ = cacheCount;
321     }
322 
GetAllChildren()323     const std::map<int32_t, LazyForEachChild>& GetAllChildren() const
324     {
325         return cachedItems_;
326     }
327 
328 protected:
329     virtual int32_t OnGetTotalCount() = 0;
330 
331     virtual LazyForEachChild OnGetChildByIndex(
332         int32_t index, std::unordered_map<std::string, LazyForEachCacheChild>& cachedItems) = 0;
333 
334     virtual void OnExpandChildrenOnInitialInNG() = 0;
335 
336 private:
337     std::map<int32_t, LazyForEachChild> cachedItems_;
338     std::unordered_map<std::string, LazyForEachCacheChild> expiringItem_;
339 
340     int32_t startIndex_ = -1;
341     int32_t endIndex_ = -1;
342     int32_t cacheCount_ = 1;
343     bool needTransition = false;
344     ACE_DISALLOW_COPY_AND_MOVE(LazyForEachBuilder);
345 };
346 } // namespace OHOS::Ace::NG
347 
348 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_FOREACH_LAZY_FOR_EACH_BUILDER_H