• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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/lazy_layout_wrapper_builder.h"
17 
18 #include <cstdint>
19 #include <iterator>
20 #include <list>
21 #include <map>
22 #include <optional>
23 #include <string>
24 #include <unordered_map>
25 
26 #include "base/log/ace_trace.h"
27 #include "base/memory/referenced.h"
28 #include "base/utils/utils.h"
29 #include "core/components_ng/base/frame_node.h"
30 #include "core/components_ng/base/ui_node.h"
31 #include "core/components_ng/layout/layout_wrapper.h"
32 #include "core/components_ng/syntax/lazy_for_each_node.h"
33 
34 namespace OHOS::Ace::NG {
35 
LazyLayoutWrapperBuilder(const RefPtr<LazyForEachBuilder> & builder,const WeakPtr<LazyForEachNode> & host)36 LazyLayoutWrapperBuilder::LazyLayoutWrapperBuilder(
37     const RefPtr<LazyForEachBuilder>& builder, const WeakPtr<LazyForEachNode>& host)
38     : builder_(builder), host_(host)
39 {}
40 
SwapDirtyAndUpdateBuildCache()41 void LazyLayoutWrapperBuilder::SwapDirtyAndUpdateBuildCache()
42 {
43     auto host = host_.Upgrade();
44     CHECK_NULL_VOID(host);
45 
46     if (childWrappers_.empty()) {
47         decltype(nodeIds_) nodeIds;
48         std::unordered_map<int32_t, std::optional<std::string>> cacheItems;
49         host->UpdateLazyForEachItems(-1, -1, std::move(nodeIds), std::move(cacheItems));
50         return;
51     }
52 
53     // check front active flag.
54     auto item = childWrappers_.front();
55     decltype(nodeIds_) frontNodeIds;
56     decltype(nodeIds_) backNodeIds;
57 
58     while (item && !item->IsActive()) {
59         childWrappers_.pop_front();
60         frontNodeIds.push_front(nodeIds_.front());
61         nodeIds_.pop_front();
62         startIndex_ = startIndex_.value() + 1;
63         item = childWrappers_.empty() ? nullptr : childWrappers_.front();
64     }
65     // check end active flag.
66     item = childWrappers_.empty() ? nullptr : childWrappers_.back();
67     while (item && !item->IsActive()) {
68         backNodeIds.push_front(nodeIds_.back());
69         nodeIds_.pop_back();
70         childWrappers_.pop_back();
71         endIndex_ = endIndex_.value() - 1;
72         item = childWrappers_.empty() ? nullptr : childWrappers_.back();
73     }
74 
75     for (const auto& wrapper : childWrappers_) {
76         wrapper->SwapDirtyLayoutWrapperOnMainThread();
77     }
78     int32_t frontCount = 0;
79     int32_t backCount = 0;
80     auto totalCount = OnGetTotalCount();
81 
82     std::list<int32_t> idleIndexes;
83     std::unordered_map<int32_t, std::optional<std::string>> cacheItems;
84     for (int32_t i = 0; i < cacheCount_; i++) {
85         if (frontNodeIds.empty()) {
86             if (startIndex_.value() - i > 0) {
87                 auto idleIndex = startIndex_.value() - 1 - i;
88                 auto cacheInfo = builder_->GetCacheItemInfo(idleIndex);
89                 if (!cacheInfo) {
90                     cacheInfo = GetKeyByIndexFromPreNodes(idleIndex);
91                 }
92                 if (!cacheInfo) {
93                     idleIndexes.emplace_back(idleIndex);
94                 }
95                 cacheItems.try_emplace(idleIndex, std::move(cacheInfo));
96             }
97         } else {
98             cacheItems.try_emplace(startIndex_.value() - frontCount - 1, frontNodeIds.front());
99             frontNodeIds.pop_front();
100             frontCount++;
101         }
102 
103         if (backNodeIds.empty()) {
104             if (endIndex_.value() + i < (totalCount - 1)) {
105                 auto idleIndex = endIndex_.value() + 1 + i;
106                 auto cacheInfo = builder_->GetCacheItemInfo(idleIndex);
107                 if (!cacheInfo) {
108                     idleIndexes.emplace_back(idleIndex);
109                 }
110                 if (!cacheInfo) {
111                     cacheInfo = GetKeyByIndexFromPreNodes(idleIndex);
112                 }
113                 cacheItems.try_emplace(idleIndex, std::move(cacheInfo));
114             }
115         } else {
116             cacheItems.try_emplace(endIndex_.value() + 1 + i, backNodeIds.front());
117             backNodeIds.pop_front();
118             backCount++;
119         }
120     }
121     host->UpdateLazyForEachItems(startIndex_.value(), endIndex_.value(), std::move(nodeIds_), std::move(cacheItems));
122     host->PostIdleTask(std::move(idleIndexes));
123 }
124 
AdjustGridOffset()125 void LazyLayoutWrapperBuilder::AdjustGridOffset()
126 {
127     for (const auto& wrapper : childWrappers_) {
128         wrapper->GetHostNode()->AdjustGridOffset();
129     }
130 }
131 
OnGetTotalCount()132 int32_t LazyLayoutWrapperBuilder::OnGetTotalCount()
133 {
134     CHECK_NULL_RETURN_NOLOG(builder_, 0);
135     return builder_->GetTotalCount();
136 }
137 
OnGetOrCreateWrapperByIndex(int32_t index)138 RefPtr<LayoutWrapper> LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndex(int32_t index)
139 {
140     LOGD("OnGetOrCreateWrapperByIndex index: %{private}d startIndex: %{private}d endIndex: %{private}d", index,
141         startIndex_.value_or(-1), endIndex_.value_or(-1));
142     auto totalCount = GetTotalCount();
143     if ((index < 0) || (index >= totalCount)) {
144         LOGE("index is illegal: %{public}d", index);
145         return nullptr;
146     }
147     // check if the index needs to be converted to virtual index.
148     if (lazySwiper_ && startIndex_ && index < startIndex_.value()) {
149         index += totalCount;
150     }
151     return OnGetOrCreateWrapperByIndexLegacy(index);
152 }
153 
OnGetOrCreateWrapperByIndexLegacy(int32_t index)154 RefPtr<LayoutWrapper> LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndexLegacy(int32_t index)
155 {
156     auto totalCount = GetTotalCount();
157     // The first time get the item, do not do the range check, and the subsequent get the item
158     // needs to check whether it is in the upper and lower bounds (-1, +1) of the existing index.
159     if (!startIndex_) {
160         startIndex_ = index;
161         endIndex_ = index;
162     } else {
163         if ((index >= startIndex_.value()) && (index <= endIndex_.value())) {
164             auto iter = childWrappers_.begin();
165             std::advance(iter, index - startIndex_.value());
166             return *iter;
167         }
168         if ((index < (startIndex_.value() - 1)) || (index > (endIndex_.value() + 1))) {
169             LOGE("need to obtain the item node in order and by step one: %{public}d", index);
170             return nullptr;
171         }
172     }
173 
174     CHECK_NULL_RETURN(builder_, nullptr);
175     RefPtr<UINode> uiNode;
176     std::string id;
177     // get frame node from previous cached.
178     if ((index >= preStartIndex_) && (index <= preEndIndex_)) {
179         auto iter = preNodeIds_.begin();
180         std::advance(iter, index - preStartIndex_);
181         if ((iter != preNodeIds_.end()) && (iter->has_value())) {
182             id = iter->value();
183             uiNode = builder_->GetChildByKey(id);
184         }
185     }
186     if (!uiNode) {
187         // convert index to real index.
188         int32_t realIndex = index;
189         if (lazySwiper_ && index >= totalCount) {
190             realIndex -= totalCount;
191         }
192         // create frame node.
193         auto itemInfo = builder_->CreateChildByIndex(realIndex);
194         id = itemInfo.first;
195         uiNode = itemInfo.second;
196     }
197     CHECK_NULL_RETURN(uiNode, nullptr);
198     RefPtr<LayoutWrapper> wrapper;
199     auto frameNode = DynamicCast<FrameNode>(uiNode);
200     if (frameNode) {
201         wrapper = frameNode->CreateLayoutWrapper(forceMeasure_, forceLayout_);
202     } else {
203         wrapper = uiNode->CreateLayoutWrapper(forceMeasure_, forceLayout_);
204     }
205     CHECK_NULL_RETURN(wrapper, nullptr);
206     if (index == (startIndex_.value() - 1)) {
207         // insert at begin.
208         startIndex_ = index;
209         childWrappers_.emplace_front(wrapper);
210         nodeIds_.emplace_front(id);
211         return wrapper;
212     }
213     // insert at end.
214     endIndex_ = index;
215     childWrappers_.emplace_back(wrapper);
216     nodeIds_.emplace_back(id);
217     return wrapper;
218 }
219 
GetCachedChildLayoutWrapper()220 const std::list<RefPtr<LayoutWrapper>>& LazyLayoutWrapperBuilder::GetCachedChildLayoutWrapper()
221 {
222     return childWrappers_;
223 }
224 
OnExpandChildLayoutWrapper()225 const std::list<RefPtr<LayoutWrapper>>& LazyLayoutWrapperBuilder::OnExpandChildLayoutWrapper()
226 {
227     auto total = GetTotalCount();
228     if (!childWrappers_.empty()) {
229         if (static_cast<int32_t>(childWrappers_.size()) == total) {
230             return childWrappers_;
231         }
232         LOGE("can not mix lazy get and full get method!");
233         childWrappers_.clear();
234         return childWrappers_;
235     }
236 
237     CHECK_NULL_RETURN(builder_, childWrappers_);
238     for (int32_t index = 0; index < total; ++index) {
239         auto itemInfo = builder_->CreateChildByIndex(index);
240         RefPtr<LayoutWrapper> wrapper;
241         auto frameNode = DynamicCast<FrameNode>(itemInfo.second);
242         auto uiNode = itemInfo.second;
243         if (frameNode) {
244             wrapper = frameNode->CreateLayoutWrapper(forceMeasure_, forceLayout_);
245         } else if (uiNode) {
246             wrapper = uiNode->CreateLayoutWrapper(forceMeasure_, forceLayout_);
247         }
248         if (!wrapper) {
249             LOGE("fail to create wrapper");
250             childWrappers_.clear();
251             return childWrappers_;
252         }
253         nodeIds_.emplace_back(itemInfo.first);
254         childWrappers_.emplace_back(wrapper);
255     }
256     startIndex_ = 0;
257     endIndex_ = total - 1;
258     return childWrappers_;
259 }
260 
GetKeyByIndexFromPreNodes(int32_t index)261 std::optional<std::string> LazyLayoutWrapperBuilder::GetKeyByIndexFromPreNodes(int32_t index)
262 {
263     if ((index >= preStartIndex_) && (index <= preEndIndex_)) {
264         auto iter = preNodeIds_.begin();
265         std::advance(iter, index - preStartIndex_);
266         if ((iter != preNodeIds_.end()) && (iter->has_value())) {
267             return iter->value();
268         }
269     }
270     return std::nullopt;
271 }
272 
273 } // namespace OHOS::Ace::NG
274