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