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