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 
AdjustGridOffset()43 void LazyLayoutWrapperBuilder::AdjustGridOffset()
44 {
45     for (const auto& wrapper : childWrappers_) {
46         auto frameNode = wrapper->GetHostNode();
47         CHECK_NULL_VOID(frameNode);
48         frameNode->AdjustGridOffset();
49     }
50 }
51 
OnGetTotalCount()52 int32_t LazyLayoutWrapperBuilder::OnGetTotalCount()
53 {
54     CHECK_NULL_RETURN(builder_, 0);
55     return builder_->GetTotalCount();
56 }
57 
OnGetOrCreateWrapperByIndex(int32_t index)58 RefPtr<LayoutWrapper> LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndex(int32_t index)
59 {
60     auto totalCount = GetTotalCount();
61     if ((index < 0) || (index >= totalCount)) {
62         LOGE("index is illegal: %{public}d", index);
63         return nullptr;
64     }
65     // check if the index needs to be converted to virtual index.
66     if (lazySwiper_ &&
67         ((startIndex_ && index < startIndex_.value() - 1) || (endIndex_ && index > endIndex_.value() + 1))) {
68         if (index > startIndex_.value()) {
69             index -= totalCount;
70         } else {
71             index += totalCount;
72         }
73     }
74     return OnGetOrCreateWrapperByIndexLegacy(index);
75 }
76 
OnGetOrCreateWrapperByIndexLegacy(int32_t index)77 RefPtr<LayoutWrapper> LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndexLegacy(int32_t index)
78 {
79     auto totalCount = GetTotalCount();
80     // The first time get the item, do not do the range check, and the subsequent get the item
81     // needs to check whether it is in the upper and lower bounds (-1, +1) of the existing index.
82     if (!startIndex_) {
83         startIndex_ = index;
84         endIndex_ = index;
85     } else {
86         if ((index >= startIndex_.value()) && (index <= endIndex_.value())) {
87             auto iter = childWrappers_.begin();
88             std::advance(iter, index - startIndex_.value());
89             return *iter;
90         }
91         if ((index < (startIndex_.value() - 1)) || (index > (endIndex_.value() + 1))) {
92             LOGE("need to obtain the item node in order and by step one: %{public}d", index);
93             return nullptr;
94         }
95     }
96 
97     CHECK_NULL_RETURN(builder_, nullptr);
98     RefPtr<UINode> uiNode;
99     std::string id;
100     // get frame node from previous cached.
101     if ((index >= preStartIndex_) && (index <= preEndIndex_)) {
102         auto iter = preNodeIds_.begin();
103         std::advance(iter, index - preStartIndex_);
104         if ((iter != preNodeIds_.end()) && (iter->has_value())) {
105             id = iter->value();
106             uiNode = builder_->GetChildByKey(id);
107         }
108     }
109     if (!uiNode) {
110         // convert index to real index.
111         int32_t realIndex = index;
112         if (lazySwiper_) {
113             if (index >= totalCount) {
114                 realIndex -= totalCount;
115             } else if (index < 0) {
116                 realIndex += totalCount;
117             }
118         }
119         // create frame node.
120         auto itemInfo = builder_->GetChildByIndex(realIndex, true);
121         id = itemInfo.first;
122         uiNode = itemInfo.second;
123     }
124     CHECK_NULL_RETURN(uiNode, nullptr);
125     RefPtr<LayoutWrapper> wrapper;
126     auto frameNode = DynamicCast<FrameNode>(uiNode);
127     if (frameNode) {
128         wrapper = frameNode->CreateLayoutWrapper(forceMeasure_, forceLayout_);
129     } else {
130         wrapper = uiNode->CreateLayoutWrapper(forceMeasure_, forceLayout_);
131     }
132     CHECK_NULL_RETURN(wrapper, nullptr);
133     if (index == (startIndex_.value() - 1)) {
134         // insert at begin.
135         startIndex_ = index;
136         childWrappers_.emplace_front(wrapper);
137         nodeIds_.emplace_front(id);
138         return wrapper;
139     }
140     // insert at end.
141     endIndex_ = index;
142     childWrappers_.emplace_back(wrapper);
143     nodeIds_.emplace_back(id);
144     return wrapper;
145 }
146 
GetCachedChildLayoutWrapper()147 const std::list<RefPtr<LayoutWrapper>>& LazyLayoutWrapperBuilder::GetCachedChildLayoutWrapper()
148 {
149     return childWrappers_;
150 }
151 
OnExpandChildLayoutWrapper()152 const std::list<RefPtr<LayoutWrapper>>& LazyLayoutWrapperBuilder::OnExpandChildLayoutWrapper()
153 {
154     auto total = GetTotalCount();
155     if (!childWrappers_.empty()) {
156         if (static_cast<int32_t>(childWrappers_.size()) == total) {
157             return childWrappers_;
158         }
159         LOGE("can not mix lazy get and full get method!");
160         childWrappers_.clear();
161         return childWrappers_;
162     }
163 
164     CHECK_NULL_RETURN(builder_, childWrappers_);
165     for (int32_t index = 0; index < total; ++index) {
166         auto itemInfo = builder_->GetChildByIndex(index, true);
167         RefPtr<LayoutWrapper> wrapper;
168         auto frameNode = DynamicCast<FrameNode>(itemInfo.second);
169         auto uiNode = itemInfo.second;
170         if (frameNode) {
171             wrapper = frameNode->CreateLayoutWrapper(forceMeasure_, forceLayout_);
172         } else if (uiNode) {
173             wrapper = uiNode->CreateLayoutWrapper(forceMeasure_, forceLayout_);
174         }
175         if (!wrapper) {
176             LOGE("fail to create wrapper");
177             childWrappers_.clear();
178             return childWrappers_;
179         }
180         nodeIds_.emplace_back(itemInfo.first);
181         childWrappers_.emplace_back(wrapper);
182     }
183     startIndex_ = 0;
184     endIndex_ = total - 1;
185     return childWrappers_;
186 }
187 
GetKeyByIndexFromPreNodes(int32_t index)188 std::optional<std::string> LazyLayoutWrapperBuilder::GetKeyByIndexFromPreNodes(int32_t index)
189 {
190     if ((index >= preStartIndex_) && (index <= preEndIndex_)) {
191         auto iter = preNodeIds_.begin();
192         std::advance(iter, index - preStartIndex_);
193         if ((iter != preNodeIds_.end()) && (iter->has_value())) {
194             return iter->value();
195         }
196     }
197     return std::nullopt;
198 }
199 
200 } // namespace OHOS::Ace::NG
201