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/inspector.h" 34 #include "core/components_ng/base/ui_node.h" 35 #include "core/components_ng/base/view_stack_processor.h" 36 #include "core/components_ng/pattern/list/list_item_pattern.h" 37 #include "core/components_v2/foreach/lazy_foreach_component.h" 38 39 namespace OHOS::Ace::NG { 40 41 using LazyForEachChild = std::pair<std::string, RefPtr<UINode>>; 42 using LazyForEachCacheChild = std::pair<int32_t, RefPtr<UINode>>; 43 44 class ACE_EXPORT LazyForEachBuilder : public virtual AceType { 45 DECLARE_ACE_TYPE(NG::LazyForEachBuilder, AceType) 46 public: 47 LazyForEachBuilder() = default; 48 ~LazyForEachBuilder() override = default; 49 GetTotalCount()50 int32_t GetTotalCount() 51 { 52 return OnGetTotalCount(); 53 } 54 55 std::pair<std::string, RefPtr<UINode>> GetChildByIndex(int32_t index, bool needBuild); 56 ExpandChildrenOnInitial()57 void ExpandChildrenOnInitial() 58 { 59 OnExpandChildrenOnInitialInNG(); 60 } 61 OnDataReloaded()62 void OnDataReloaded() 63 { 64 for (auto& [key, node] : expiringItem_) { 65 node.first = -1; 66 } 67 for (auto& [index, node] : cachedItems_) { 68 if (node.second) { 69 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(-1, std::move(node.second))); 70 } 71 } 72 cachedItems_.clear(); 73 needTransition = true; 74 } 75 OnDataAdded(size_t index)76 bool OnDataAdded(size_t index) 77 { 78 NotifyDataAdded(index); 79 if (!cachedItems_.empty() && index <= static_cast<size_t>(cachedItems_.rbegin()->first)) { 80 decltype(cachedItems_) temp(std::move(cachedItems_)); 81 82 for (auto& [oldindex, id] : temp) { 83 cachedItems_.try_emplace( 84 index > static_cast<size_t>(oldindex) ? oldindex : oldindex + 1, std::move(id)); 85 } 86 } 87 for (auto& [key, node] : expiringItem_) { 88 if (static_cast<size_t>(node.first) >= index && node.first != -1) { 89 node.first++; 90 } 91 } 92 93 return true; 94 } 95 OnDataDeleted(size_t index)96 RefPtr<UINode> OnDataDeleted(size_t index) 97 { 98 RefPtr<UINode> node; 99 if (cachedItems_.empty()) { 100 return node; 101 } 102 if (index <= static_cast<size_t>(cachedItems_.rbegin()->first)) { 103 decltype(cachedItems_) temp(std::move(cachedItems_)); 104 105 for (auto& [oldindex, child] : temp) { 106 if (static_cast<size_t>(oldindex) == index) { 107 node = child.second; 108 KeepRemovedItemInCache(child, expiringItem_); 109 } else { 110 cachedItems_.try_emplace( 111 index > static_cast<size_t>(oldindex) ? oldindex : oldindex - 1, std::move(child)); 112 } 113 } 114 } 115 NotifyDataDeleted(node, index, false); 116 for (auto& [key, child] : expiringItem_) { 117 if (static_cast<size_t>(child.first) > index) { 118 child.first--; 119 continue; 120 } 121 if (static_cast<size_t>(child.first) == index) { 122 child.first = -1; 123 node = child.second; 124 } 125 } 126 127 return node; 128 } 129 OnDataChanged(size_t index)130 bool OnDataChanged(size_t index) 131 { 132 auto keyIter = cachedItems_.find(index); 133 if (keyIter != cachedItems_.end()) { 134 if (keyIter->second.second) { 135 NotifyDataChanged(index, keyIter->second.second, false); 136 expiringItem_.try_emplace( 137 keyIter->second.first, LazyForEachCacheChild(-1, std::move(keyIter->second.second))); 138 } else { 139 InvalidIndexOfChangedData(index); 140 } 141 cachedItems_.erase(keyIter); 142 return true; 143 } 144 return false; 145 } 146 OnDataMoved(size_t from,size_t to)147 bool OnDataMoved(size_t from, size_t to) 148 { 149 if (from == to) { 150 return false; 151 } 152 auto fromIter = cachedItems_.find(from); 153 auto toIter = cachedItems_.find(to); 154 if (fromIter != cachedItems_.end() && toIter != cachedItems_.end()) { 155 std::swap(fromIter->second, toIter->second); 156 } else if (fromIter != cachedItems_.end()) { 157 expiringItem_.try_emplace( 158 fromIter->second.first, LazyForEachCacheChild(to, std::move(fromIter->second.second))); 159 cachedItems_.erase(fromIter); 160 } else if (toIter != cachedItems_.end()) { 161 expiringItem_.try_emplace( 162 toIter->second.first, LazyForEachCacheChild(from, std::move(toIter->second.second))); 163 cachedItems_.erase(toIter); 164 } 165 return true; 166 } 167 InvalidIndexOfChangedData(size_t index)168 void InvalidIndexOfChangedData(size_t index) 169 { 170 for (auto& [key, child] : expiringItem_) { 171 if (static_cast<size_t>(child.first) == index) { 172 child.first = -1; 173 break; 174 } 175 } 176 } 177 GetChildByKey(const std::string & key)178 RefPtr<UINode> GetChildByKey(const std::string& key) 179 { 180 return nullptr; 181 } 182 GetItems(std::list<RefPtr<UINode>> & childList)183 std::map<int32_t, LazyForEachChild>& GetItems(std::list<RefPtr<UINode>>& childList) 184 { 185 startIndex_ = -1; 186 endIndex_ = -1; 187 int32_t lastIndex = -1; 188 bool isCertained = false; 189 190 decltype(cachedItems_) items(std::move(cachedItems_)); 191 192 for (auto& [index, node] : items) { 193 if (!node.second) { 194 cachedItems_.try_emplace(index, std::move(node)); 195 continue; 196 } 197 198 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 199 if (frameNode && !frameNode->IsActive()) { 200 frameNode->SetJSViewActive(false); 201 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second))); 202 continue; 203 } 204 cachedItems_.try_emplace(index, std::move(node)); 205 if (startIndex_ == -1) { 206 startIndex_ = index; 207 } 208 if (isLoop_) { 209 if (isCertained) { 210 continue; 211 } 212 if (lastIndex > -1 && index - lastIndex > 1) { 213 startIndex_ = index; 214 endIndex_ = lastIndex; 215 isCertained = true; 216 } else { 217 endIndex_ = std::max(endIndex_, index); 218 } 219 } else { 220 endIndex_ = std::max(endIndex_, index); 221 } 222 lastIndex = index; 223 } 224 225 if (needTransition) { 226 for (auto& [key, node] : expiringItem_) { 227 if (!node.second) { 228 continue; 229 } 230 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 231 if (frameNode && frameNode->IsOnMainTree()) { 232 childList.push_back(node.second); 233 } 234 } 235 needTransition = false; 236 } 237 238 return cachedItems_; 239 } 240 RemoveAllChild()241 void RemoveAllChild() 242 { 243 for (auto& [index, node] : cachedItems_) { 244 if (!node.second) { 245 continue; 246 } 247 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 248 if (frameNode) { 249 frameNode->SetActive(false); 250 } 251 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second))); 252 } 253 } 254 SetActiveChildRange(int32_t start,int32_t end)255 void SetActiveChildRange(int32_t start, int32_t end) 256 { 257 for (auto& [index, node] : cachedItems_) { 258 if ((start <= end && start <= index && end >= index) || 259 (start > end && (index <= end || index >= start))) { 260 if (node.second) { 261 continue; 262 } 263 auto keyIter = expiringItem_.find(node.first); 264 if (keyIter != expiringItem_.end() && keyIter->second.second) { 265 node.second = keyIter->second.second; 266 expiringItem_.erase(keyIter); 267 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 268 if (frameNode) { 269 frameNode->SetActive(true); 270 } 271 } 272 continue; 273 } 274 if (!node.second) { 275 continue; 276 } 277 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 278 if (frameNode) { 279 frameNode->SetActive(false); 280 } 281 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second))); 282 } 283 } 284 SetFlagForGeneratedItem(PropertyChangeFlag propertyChangeFlag)285 void SetFlagForGeneratedItem(PropertyChangeFlag propertyChangeFlag) 286 { 287 for (const auto& item : cachedItems_) { 288 if (!item.second.second) { 289 continue; 290 } 291 item.second.second->ForceUpdateLayoutPropertyFlag(propertyChangeFlag); 292 } 293 } 294 CacheItem(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,const std::optional<LayoutConstraintF> & itemConstraint,int64_t deadline,bool & isTimeout)295 RefPtr<UINode> CacheItem(int32_t index, std::unordered_map<std::string, LazyForEachCacheChild>& cache, 296 const std::optional<LayoutConstraintF>& itemConstraint, int64_t deadline, bool& isTimeout) 297 { 298 ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index); 299 auto itemInfo = OnGetChildByIndex(index, expiringItem_); 300 CHECK_NULL_RETURN(itemInfo.second, nullptr); 301 cache.try_emplace(itemInfo.first, LazyForEachCacheChild(index, itemInfo.second)); 302 if (!itemInfo.second->RenderCustomChild(deadline)) { 303 isTimeout = true; 304 return itemInfo.second; 305 } 306 ProcessOffscreenNode(itemInfo.second, false); 307 ViewStackProcessor::GetInstance()->SetPredict(itemInfo.second); 308 itemInfo.second->Build(nullptr); 309 auto frameNode = AceType::DynamicCast<FrameNode>(itemInfo.second->GetFrameChildByIndex(0, false)); 310 if (frameNode && frameNode->GetTag() == V2::LIST_ITEM_ETS_TAG) { 311 frameNode->GetPattern<ListItemPattern>()->BeforeCreateLayoutWrapper(); 312 } 313 ViewStackProcessor::GetInstance()->ResetPredict(); 314 itemInfo.second->SetJSViewActive(false); 315 cachedItems_[index] = LazyForEachChild(itemInfo.first, nullptr); 316 317 return itemInfo.second; 318 } 319 CheckCacheIndex(std::unordered_set<int32_t> & idleIndexes,int32_t count)320 void CheckCacheIndex(std::unordered_set<int32_t>& idleIndexes, int32_t count) { 321 for (int32_t i = 1; i <= cacheCount_; i++) { 322 if (isLoop_) { 323 if ((startIndex_ <= endIndex_ && endIndex_ + i < count) || 324 startIndex_ > endIndex_ + i) { 325 idleIndexes.emplace(endIndex_ + i); 326 } else if ((endIndex_ + i) % count < startIndex_) { 327 idleIndexes.emplace((endIndex_ + i) % count); 328 } 329 } else { 330 if (endIndex_ + i < count) { 331 idleIndexes.emplace(endIndex_ + i); 332 } 333 } 334 } 335 for (int32_t i = 1; i <= cacheCount_; i++) { 336 if (isLoop_) { 337 if ((startIndex_ <= endIndex_ && startIndex_ >= i) || 338 startIndex_ > endIndex_ + i) { 339 idleIndexes.emplace(startIndex_ - i); 340 } else if ((startIndex_ - i + count) % count > endIndex_) { 341 idleIndexes.emplace((startIndex_ - i + count) % count); 342 } 343 } else { 344 if (startIndex_ >= i) { 345 idleIndexes.emplace(startIndex_ - i); 346 } 347 } 348 } 349 } 350 PreBuildByIndex(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)351 bool PreBuildByIndex(int32_t index, std::unordered_map<std::string, LazyForEachCacheChild>& cache, 352 int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask) 353 { 354 if (GetSysTimestamp() > deadline) { 355 return false; 356 } 357 bool isTimeout = false; 358 preBuildingIndex_ = -1; 359 auto uiNode = CacheItem(index, cache, itemConstraint, deadline, isTimeout); 360 if (isTimeout) { 361 preBuildingIndex_ = index; 362 return false; 363 } 364 if (!canRunLongPredictTask && itemConstraint) { 365 return false; 366 } 367 if (canRunLongPredictTask && uiNode && itemConstraint) { 368 RefPtr<FrameNode> frameNode = DynamicCast<FrameNode>(uiNode); 369 while (!frameNode) { 370 auto tempNode = uiNode; 371 uiNode = tempNode->GetFirstChild(); 372 if (!uiNode) { 373 break; 374 } 375 frameNode = DynamicCast<FrameNode>(uiNode); 376 } 377 if (frameNode) { 378 frameNode->GetGeometryNode()->SetParentLayoutConstraint(itemConstraint.value()); 379 FrameNode::ProcessOffscreenNode(frameNode); 380 } 381 } 382 return true; 383 } 384 ProcessPreBuildingIndex(std::unordered_map<std::string,LazyForEachCacheChild> & cache,int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask,std::unordered_set<int32_t> & idleIndexes)385 bool ProcessPreBuildingIndex(std::unordered_map<std::string, LazyForEachCacheChild>& cache, int64_t deadline, 386 const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask, 387 std::unordered_set<int32_t>& idleIndexes) 388 { 389 if (idleIndexes.find(preBuildingIndex_) == idleIndexes.end()) { 390 preBuildingIndex_ = -1; 391 return true; 392 } 393 idleIndexes.erase(preBuildingIndex_); 394 return PreBuildByIndex(preBuildingIndex_, cache, deadline, itemConstraint, canRunLongPredictTask); 395 } 396 PreBuild(int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)397 bool PreBuild(int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask) 398 { 399 ACE_SCOPED_TRACE("expiringItem_ count:[%zu]", expiringItem_.size()); 400 if (itemConstraint && !canRunLongPredictTask) { 401 return false; 402 } 403 auto count = OnGetTotalCount(); 404 std::unordered_map<std::string, LazyForEachCacheChild> cache; 405 std::unordered_set<int32_t> idleIndexes; 406 if (startIndex_ != -1 && endIndex_ != -1) { 407 CheckCacheIndex(idleIndexes, count); 408 } 409 410 ProcessCachedIndex(cache, idleIndexes); 411 412 bool result = true; 413 result = ProcessPreBuildingIndex(cache, deadline, itemConstraint, canRunLongPredictTask, idleIndexes); 414 if (!result) { 415 expiringItem_.swap(cache); 416 return result; 417 } 418 419 for (auto index : idleIndexes) { 420 result = PreBuildByIndex(index, cache, deadline, itemConstraint, canRunLongPredictTask); 421 if (!result) { 422 break; 423 } 424 } 425 expiringItem_.swap(cache); 426 return result; 427 } 428 ProcessCachedIndex(std::unordered_map<std::string,LazyForEachCacheChild> & cache,std::unordered_set<int32_t> & idleIndexes)429 void ProcessCachedIndex(std::unordered_map<std::string, LazyForEachCacheChild>& cache, 430 std::unordered_set<int32_t>& idleIndexes) 431 { 432 for (auto& [key, node] : expiringItem_) { 433 auto iter = idleIndexes.find(node.first); 434 if (iter != idleIndexes.end() && node.second) { 435 ProcessOffscreenNode(node.second, false); 436 if (node.first == preBuildingIndex_) { 437 cache.try_emplace(key, node); 438 } else { 439 cache.try_emplace(key, std::move(node)); 440 cachedItems_.try_emplace(node.first, LazyForEachChild(key, nullptr)); 441 idleIndexes.erase(iter); 442 } 443 } else { 444 NotifyDataDeleted(node.second, static_cast<size_t>(node.first), true); 445 ProcessOffscreenNode(node.second, true); 446 } 447 } 448 } 449 ProcessOffscreenNode(RefPtr<UINode> uiNode,bool remove)450 void ProcessOffscreenNode(RefPtr<UINode> uiNode, bool remove) 451 { 452 if (uiNode) { 453 auto frameNode = DynamicCast<FrameNode>(uiNode); 454 while (!frameNode) { 455 auto tempNode = uiNode; 456 uiNode = tempNode->GetFirstChild(); 457 if (!uiNode) { 458 break; 459 } 460 frameNode = DynamicCast<FrameNode>(uiNode); 461 } 462 if (frameNode) { 463 if (!remove) { 464 Inspector::AddOffscreenNode(frameNode); 465 } else { 466 Inspector::RemoveOffscreenNode(frameNode); 467 } 468 } 469 } 470 } 471 ClearAllOffscreenNode()472 void ClearAllOffscreenNode() 473 { 474 for (auto& [key, node] : expiringItem_) { 475 ProcessOffscreenNode(node.second, true); 476 } 477 for (auto& [key, node] : cachedItems_) { 478 ProcessOffscreenNode(node.second, true); 479 } 480 } 481 482 virtual void ReleaseChildGroupById(const std::string& id) = 0; 483 484 virtual void RegisterDataChangeListener(const RefPtr<V2::DataChangeListener>& listener) = 0; 485 486 virtual void UnregisterDataChangeListener(V2::DataChangeListener* listener) = 0; 487 SetCacheCount(int32_t cacheCount)488 void SetCacheCount(int32_t cacheCount) 489 { 490 cacheCount_ = cacheCount; 491 } 492 SetIsLoop(bool isLoop)493 void SetIsLoop(bool isLoop) 494 { 495 isLoop_ = isLoop; 496 } 497 GetCachedUINodeMap()498 const std::unordered_map<std::string, LazyForEachCacheChild>& GetCachedUINodeMap() 499 { 500 return expiringItem_; 501 } 502 GetAllChildren()503 const std::map<int32_t, LazyForEachChild>& GetAllChildren() 504 { 505 if (!cachedItems_.empty()) { 506 startIndex_ = cachedItems_.begin()->first; 507 endIndex_ = cachedItems_.rbegin()->first; 508 } 509 if (isLoop_ && !cachedItems_.empty()) { 510 int32_t lastIndex = -1; 511 for (auto& [index, node] : cachedItems_) { 512 if (lastIndex > -1 && index - lastIndex > 1) { 513 startIndex_ = index; 514 endIndex_ = lastIndex; 515 break; 516 } 517 } 518 } 519 return cachedItems_; 520 } 521 SetJSViewActive(bool active)522 void SetJSViewActive(bool active) 523 { 524 for (const auto& node : cachedItems_) { 525 if (node.second.second == nullptr) { 526 continue; 527 } 528 node.second.second->SetJSViewActive(active); 529 } 530 for (const auto& node : expiringItem_) { 531 node.second.second->SetJSViewActive(active); 532 } 533 } 534 535 protected: 536 virtual int32_t OnGetTotalCount() = 0; 537 538 virtual LazyForEachChild OnGetChildByIndex( 539 int32_t index, std::unordered_map<std::string, LazyForEachCacheChild>& cachedItems) = 0; 540 541 virtual void OnExpandChildrenOnInitialInNG() = 0; 542 543 virtual void NotifyDataChanged(size_t index, RefPtr<UINode>& lazyForEachNode, bool isRebuild = true) = 0; 544 545 virtual void NotifyDataDeleted(RefPtr<UINode>& lazyForEachNode, size_t index, bool removeIds) = 0; 546 547 virtual void NotifyDataAdded(size_t index) = 0; 548 549 virtual void KeepRemovedItemInCache(NG::LazyForEachChild node, 550 std::unordered_map<std::string, NG::LazyForEachCacheChild>& cachedItems) = 0; 551 552 private: 553 std::map<int32_t, LazyForEachChild> cachedItems_; 554 std::unordered_map<std::string, LazyForEachCacheChild> expiringItem_; 555 556 int32_t startIndex_ = -1; 557 int32_t endIndex_ = -1; 558 int32_t cacheCount_ = 0; 559 int32_t preBuildingIndex_ = -1; 560 bool needTransition = false; 561 bool isLoop_ = false; 562 ACE_DISALLOW_COPY_AND_MOVE(LazyForEachBuilder); 563 }; 564 } // namespace OHOS::Ace::NG 565 566 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_SYNTAX_FOREACH_LAZY_FOR_EACH_BUILDER_H 567