1 /* 2 * Copyright (c) 2024 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_for_each_builder.h" 17 #include "core/components_ng/pattern/recycle_view/recycle_dummy_node.h" 18 19 namespace OHOS::Ace::NG { GetChildByIndex(int32_t index,bool needBuild,bool isCache)20 std::pair<std::string, RefPtr<UINode>> LazyForEachBuilder::GetChildByIndex( 21 int32_t index, bool needBuild, bool isCache) 22 { 23 auto iter = cachedItems_.find(index); 24 if (iter != cachedItems_.end()) { 25 if (iter->second.second) { 26 return iter->second; 27 } 28 auto keyIter = expiringItem_.find(iter->second.first); 29 if (keyIter != expiringItem_.end() && keyIter->second.second) { 30 if (!isCache) { 31 iter->second.second = keyIter->second.second; 32 expiringItem_.erase(keyIter); 33 return iter->second; 34 } else { 35 return { keyIter->first, keyIter->second.second }; 36 } 37 } 38 } 39 40 if (needBuild) { 41 ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index); 42 std::pair<std::string, RefPtr<UINode>> itemInfo; 43 if (useNewInterface_) { 44 itemInfo = OnGetChildByIndexNew(ConvertFromToIndex(index), cachedItems_, expiringItem_); 45 } else { 46 itemInfo = OnGetChildByIndex(ConvertFromToIndex(index), expiringItem_); 47 } 48 CHECK_NULL_RETURN(itemInfo.second, itemInfo); 49 if (isCache) { 50 expiringItem_.emplace(itemInfo.first, LazyForEachCacheChild(index, itemInfo.second)); 51 cachedItems_[index] = LazyForEachChild(itemInfo.first, nullptr); 52 } else { 53 cachedItems_[index] = itemInfo; 54 } 55 return itemInfo; 56 } 57 return {}; 58 } 59 OnDataReloaded()60 void LazyForEachBuilder::OnDataReloaded() 61 { 62 for (auto& [key, node] : expiringItem_) { 63 node.first = -1; 64 } 65 for (auto& [index, node] : cachedItems_) { 66 if (node.second) { 67 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(-1, std::move(node.second))); 68 } 69 } 70 cachedItems_.clear(); 71 needTransition = true; 72 } 73 OnDataAdded(size_t index)74 bool LazyForEachBuilder::OnDataAdded(size_t index) 75 { 76 NotifyDataAdded(index); 77 if (!cachedItems_.empty() && index <= static_cast<size_t>(cachedItems_.rbegin()->first)) { 78 decltype(cachedItems_) temp(std::move(cachedItems_)); 79 80 for (auto& [oldindex, id] : temp) { 81 cachedItems_.try_emplace( 82 index > static_cast<size_t>(oldindex) ? oldindex : oldindex + 1, std::move(id)); 83 } 84 } 85 for (auto& [key, node] : expiringItem_) { 86 if (static_cast<size_t>(node.first) >= index && node.first != -1) { 87 node.first++; 88 } 89 } 90 91 return true; 92 } 93 OnDataBulkAdded(size_t index,size_t count)94 bool LazyForEachBuilder::OnDataBulkAdded(size_t index, size_t count) 95 { 96 if (!cachedItems_.empty() && index <= static_cast<size_t>(cachedItems_.rbegin()->first)) { 97 decltype(cachedItems_) temp(std::move(cachedItems_)); 98 99 for (auto& [oldindex, id] : temp) { 100 cachedItems_.try_emplace( 101 index > static_cast<size_t>(oldindex) ? oldindex : oldindex + count, std::move(id)); 102 } 103 } 104 for (auto& [key, node] : expiringItem_) { 105 if (static_cast<size_t>(node.first) >= index && node.first != -1) { 106 node.first = node.first + static_cast<int32_t>(count); 107 } 108 } 109 110 return true; 111 } 112 OnDataDeleted(size_t index)113 RefPtr<UINode> LazyForEachBuilder::OnDataDeleted(size_t index) 114 { 115 RefPtr<UINode> node; 116 if (cachedItems_.empty()) { 117 return node; 118 } 119 if (index <= static_cast<size_t>(cachedItems_.rbegin()->first)) { 120 decltype(cachedItems_) temp(std::move(cachedItems_)); 121 122 for (auto& [oldindex, child] : temp) { 123 if (static_cast<size_t>(oldindex) == index) { 124 node = child.second; 125 KeepRemovedItemInCache(child, expiringItem_); 126 } else { 127 cachedItems_.try_emplace( 128 index > static_cast<size_t>(oldindex) ? oldindex : oldindex - 1, std::move(child)); 129 } 130 } 131 } 132 NotifyDataDeleted(node, index, false); 133 for (auto& [key, child] : expiringItem_) { 134 if (static_cast<size_t>(child.first) > index) { 135 child.first--; 136 continue; 137 } 138 if (static_cast<size_t>(child.first) == index) { 139 child.first = -1; 140 node = child.second; 141 } 142 } 143 144 return node; 145 } 146 OnDataBulkDeleted(size_t index,size_t count)147 std::list<std::pair<std::string, RefPtr<UINode>>>& LazyForEachBuilder::OnDataBulkDeleted(size_t index, size_t count) 148 { 149 if (cachedItems_.empty()) { 150 return nodeList_; 151 } 152 if (index <= static_cast<size_t>(cachedItems_.rbegin()->first)) { 153 decltype(cachedItems_) temp(std::move(cachedItems_)); 154 155 for (auto& [oldindex, child] : temp) { 156 if (static_cast<size_t>(oldindex) >= index && static_cast<size_t>(oldindex) < index + count) { 157 nodeList_.emplace_back(child.first, child.second); 158 } else { 159 cachedItems_.try_emplace( 160 index > static_cast<size_t>(oldindex) ? oldindex : oldindex - count, std::move(child)); 161 } 162 } 163 } 164 165 if (DeleteExpiringItemImmediately()) { 166 decltype(expiringItem_) expiringTemp(std::move(expiringItem_)); 167 for (auto& [key, child] : expiringTemp) { 168 if (child.first < 0) { 169 nodeList_.emplace_back(key, child.second); 170 continue; 171 } 172 if (static_cast<size_t>(child.first) >= index + count) { 173 child.first -= static_cast<int32_t>(count); 174 expiringItem_.try_emplace(key, child); 175 continue; 176 } 177 if (static_cast<size_t>(child.first) >= index && static_cast<size_t>(child.first) < index + count) { 178 nodeList_.emplace_back(key, child.second); 179 } else { 180 expiringItem_.try_emplace(key, child); 181 } 182 } 183 } else { 184 for (auto& [key, child] : expiringItem_) { 185 if (static_cast<size_t>(child.first) >= index + count) { 186 child.first -= static_cast<int32_t>(count); 187 continue; 188 } 189 if (static_cast<size_t>(child.first) >= index && static_cast<size_t>(child.first) < index + count) { 190 child.first = -1; 191 } 192 } 193 } 194 195 return nodeList_; 196 } 197 OnDataChanged(size_t index)198 bool LazyForEachBuilder::OnDataChanged(size_t index) 199 { 200 auto keyIter = cachedItems_.find(index); 201 if (keyIter != cachedItems_.end()) { 202 if (keyIter->second.second) { 203 NotifyDataChanged(index, keyIter->second.second, false); 204 expiringItem_.try_emplace( 205 keyIter->second.first, LazyForEachCacheChild(-1, std::move(keyIter->second.second))); 206 } else { 207 InvalidIndexOfChangedData(index); 208 } 209 cachedItems_.erase(keyIter); 210 return true; 211 } 212 return false; 213 } 214 OnDataBulkChanged(size_t index,size_t count)215 std::list<std::pair<std::string, RefPtr<UINode>>>& LazyForEachBuilder::OnDataBulkChanged(size_t index, size_t count) 216 { 217 if (cachedItems_.empty()) { 218 return nodeList_; 219 } 220 if (static_cast<size_t>(cachedItems_.rbegin()->first) < index) { 221 return nodeList_; 222 } 223 auto iter = cachedItems_.begin(); 224 while (iter != cachedItems_.end()) { 225 auto itemIndex = iter->first; 226 const auto& child = iter->second; 227 if (static_cast<size_t>(itemIndex) >= index && static_cast<size_t>(itemIndex) < index + count) { 228 NotifyDataChanged(index, child.second, false); 229 nodeList_.emplace_back(child.first, child.second); 230 iter = cachedItems_.erase(iter); 231 } else { 232 iter++; 233 } 234 } 235 for (auto& [key, node] : expiringItem_) { 236 if (static_cast<size_t>(node.first) >= index && static_cast<size_t>(node.first) < index + count) { 237 node.first = -1; 238 } 239 } 240 return nodeList_; 241 } 242 OnDataMoveToNewPlace(size_t from,size_t to)243 void LazyForEachBuilder::OnDataMoveToNewPlace(size_t from, size_t to) 244 { 245 if (from == to) { 246 return; 247 } 248 decltype(cachedItems_) temp(std::move(cachedItems_)); 249 for (const auto& [itemIndex, child] : temp) { 250 auto position = static_cast<size_t>(itemIndex); 251 if (position > from && position <= to && position >= 1) { // from < position <= to 252 cachedItems_.emplace(position - 1, child); 253 } else if (position >= to && position < from) { // to <= position < from 254 cachedItems_.emplace(position + 1, child); 255 } else if (position == from) { 256 cachedItems_.emplace(to, child); 257 } else { 258 cachedItems_.emplace(itemIndex, child); 259 } 260 } 261 262 for (const auto& [key, child] : expiringItem_) { 263 auto position = static_cast<size_t>(child.first); 264 if (position > from && position <= to && position >= 1) { // from < position <= to 265 expiringItem_[key] = LazyForEachCacheChild(position - 1, std::move(child.second)); 266 } else if (position >= to && position < from) { // to <= position < from 267 expiringItem_[key] = LazyForEachCacheChild(position + 1, std::move(child.second)); 268 } else if (position == from) { 269 expiringItem_[key] = LazyForEachCacheChild(to, std::move(child.second)); 270 } 271 } 272 } 273 OnDataMoved(size_t from,size_t to)274 bool LazyForEachBuilder::OnDataMoved(size_t from, size_t to) 275 { 276 if (from == to) { 277 return false; 278 } 279 auto fromIter = cachedItems_.find(from); 280 auto toIter = cachedItems_.find(to); 281 if (fromIter != cachedItems_.end() && toIter != cachedItems_.end()) { 282 std::swap(fromIter->second, toIter->second); 283 } else if (fromIter != cachedItems_.end()) { 284 expiringItem_[fromIter->second.first] = LazyForEachCacheChild(to, std::move(fromIter->second.second)); 285 cachedItems_.erase(fromIter); 286 } else if (toIter != cachedItems_.end()) { 287 expiringItem_[toIter->second.first] = LazyForEachCacheChild(from, std::move(toIter->second.second)); 288 cachedItems_.erase(toIter); 289 } 290 return true; 291 } 292 GetAllItems(std::vector<UINode * > & items)293 void LazyForEachBuilder::GetAllItems(std::vector<UINode*>& items) 294 { 295 for (const auto& item : cachedItems_) { 296 items.emplace_back(RawPtr(item.second.second)); 297 } 298 for (const auto& item : expiringItem_) { 299 items.emplace_back(RawPtr(item.second.second)); 300 } 301 for (const auto& item : nodeList_) { 302 items.emplace_back(RawPtr(item.second)); 303 } 304 } 305 Transit(std::list<std::pair<std::string,RefPtr<UINode>>> & childList)306 void LazyForEachBuilder::Transit(std::list<std::pair<std::string, RefPtr<UINode>>>& childList) 307 { 308 if (needTransition) { 309 for (auto& [key, node] : expiringItem_) { 310 if (!node.second) { 311 continue; 312 } 313 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 314 if (frameNode && frameNode->IsOnMainTree()) { 315 childList.emplace_back(key, node.second); 316 } 317 } 318 needTransition = false; 319 } 320 } 321 GetItems(std::list<std::pair<std::string,RefPtr<UINode>>> & childList)322 std::map<int32_t, LazyForEachChild>& LazyForEachBuilder::GetItems( 323 std::list<std::pair<std::string, RefPtr<UINode>>>& childList) 324 { 325 startIndex_ = -1; 326 endIndex_ = -1; 327 int32_t lastIndex = -1; 328 bool isCertained = false; 329 330 decltype(cachedItems_) items(std::move(cachedItems_)); 331 332 for (auto& [index, node] : items) { 333 if (!node.second) { 334 cachedItems_.try_emplace(index, std::move(node)); 335 continue; 336 } 337 338 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 339 if (frameNode && !frameNode->IsActive()) { 340 ACE_SYNTAX_SCOPED_TRACE("LazyForEach not active index[%d]", index); 341 frameNode->SetJSViewActive(false, true); 342 expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second))); 343 continue; 344 } 345 cachedItems_.try_emplace(index, std::move(node)); 346 if (startIndex_ == -1) { 347 startIndex_ = index; 348 } 349 if (isLoop_) { 350 if (isCertained) { 351 continue; 352 } 353 if (lastIndex > -1 && index - lastIndex > 1) { 354 startIndex_ = index; 355 endIndex_ = lastIndex; 356 isCertained = true; 357 } else { 358 endIndex_ = std::max(endIndex_, index); 359 } 360 } else { 361 endIndex_ = std::max(endIndex_, index); 362 } 363 lastIndex = index; 364 } 365 366 Transit(childList); 367 368 return cachedItems_; 369 } 370 GetTotalCountOfOriginalDataset()371 int32_t LazyForEachBuilder::GetTotalCountOfOriginalDataset() 372 { 373 int32_t totalCount = GetTotalCount(); 374 int32_t totalCountOfOriginalDataset = historicalTotalCount_; 375 UpdateHistoricalTotalCount(totalCount); 376 return totalCountOfOriginalDataset; 377 } 378 OnDatasetChange(std::list<V2::Operation> DataOperations)379 std::pair<int32_t, std::list<std::pair<std::string, RefPtr<UINode>>>> LazyForEachBuilder::OnDatasetChange( 380 std::list<V2::Operation> DataOperations) 381 { 382 totalCountOfOriginalDataset_ = GetTotalCountOfOriginalDataset(); 383 int32_t initialIndex = totalCountOfOriginalDataset_; 384 std::map<int32_t, LazyForEachChild> expiringTempItem_; 385 std::list<std::string> expiringKeys; 386 for (auto& [key, cacheChild] : expiringItem_) { 387 if (cacheChild.first > -1) { 388 expiringTempItem_.try_emplace(cacheChild.first, LazyForEachChild(key, cacheChild.second)); 389 expiringKeys.emplace_back(key); 390 } 391 } 392 for (auto& key : expiringKeys) { 393 expiringItem_.erase(key); 394 } 395 decltype(expiringTempItem_) expiringTemp(std::move(expiringTempItem_)); 396 for (auto operation : DataOperations) { 397 bool isReload = ClassifyOperation(operation, initialIndex, cachedItems_, expiringTemp); 398 if (isReload) { 399 initialIndex = 0; 400 return std::pair(initialIndex, nodeList_); 401 } 402 } 403 decltype(cachedItems_) cachedTemp(std::move(cachedItems_)); 404 std::map<int32_t, int32_t> indexChangedMap; 405 CollectIndexChangedCount(indexChangedMap); 406 RepairDatasetItems(cachedTemp, cachedItems_, indexChangedMap); 407 RepairDatasetItems(expiringTemp, expiringTempItem_, indexChangedMap); 408 for (auto& [index, node] : expiringTempItem_) { 409 expiringItem_.emplace(node.first, LazyForEachCacheChild(index, node.second)); 410 } 411 operationList_.clear(); 412 return std::pair(initialIndex, nodeList_); 413 } 414 RepairDatasetItems(std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTempItem_,std::map<int32_t,int32_t> & indexChangedMap)415 void LazyForEachBuilder::RepairDatasetItems(std::map<int32_t, LazyForEachChild>& cachedTemp, 416 std::map<int32_t, LazyForEachChild>& expiringTempItem_, std::map<int32_t, int32_t>& indexChangedMap) 417 { 418 int32_t changedIndex = 0; 419 for (auto& [index, child] : cachedTemp) { 420 auto iter = indexChangedMap.find(index); 421 if (iter == indexChangedMap.end()) { 422 if (!indexChangedMap.empty()) { 423 iter--; 424 if (iter->first < index) { 425 changedIndex = iter->second; 426 } 427 } 428 } else { 429 changedIndex = iter->second; 430 } 431 if (operationList_.find(index) == operationList_.end()) { 432 expiringTempItem_.try_emplace(index + changedIndex, child); 433 continue; 434 } 435 if (!indexChangedMap.empty()) { 436 changedIndex = iter->second; 437 } 438 auto info = operationList_.find(index)->second; 439 if (info.isDeleting) { 440 nodeList_.emplace_back(child.first, child.second); 441 } else if (info.isChanged) { 442 expiringTempItem_.try_emplace(index + changedIndex, LazyForEachChild(info.key, nullptr)); 443 } else if (!info.extraKey.empty()) { 444 expiringTempItem_.try_emplace(index + changedIndex, child); 445 int32_t preChangedIndex = 0; 446 auto preIter = indexChangedMap.find(index - 1); 447 if (preIter != indexChangedMap.end()) { 448 preChangedIndex = preIter->second; 449 } 450 for (int32_t i = 0; i < static_cast<int32_t>(info.extraKey.size()); i++) { 451 expiringTempItem_.try_emplace( 452 index + preChangedIndex + i, LazyForEachChild(info.extraKey[i], nullptr)); 453 } 454 } else if (info.moveIn || info.isExchange) { 455 RepairMoveOrExchange(expiringTempItem_, info, child, index, changedIndex); 456 } else { 457 expiringTempItem_.try_emplace(index + changedIndex, child); 458 } 459 } 460 } 461 RepairMoveOrExchange(std::map<int32_t,LazyForEachChild> & expiringTempItem_,OperationInfo & info,LazyForEachChild & child,int32_t index,int32_t changedIndex)462 void LazyForEachBuilder::RepairMoveOrExchange(std::map<int32_t, LazyForEachChild>& expiringTempItem_, 463 OperationInfo& info, LazyForEachChild& child, int32_t index, int32_t changedIndex) 464 { 465 if (info.isExchange) { 466 // if the child will be exchanged with a null node, this child should be deleted 467 if (info.node == nullptr && child.second != nullptr) { 468 nodeList_.emplace_back(child.first, child.second); 469 } 470 // null node should not be put in expiringTempItem_ then expiringTempItem_ 471 if (info.node != nullptr) { 472 expiringTempItem_.try_emplace(index + changedIndex, LazyForEachChild(info.key, info.node)); 473 } 474 return; 475 } 476 if (info.moveIn) { 477 int32_t fromIndex = index + changedIndex - 1; 478 int32_t toIndex = index + changedIndex; 479 if (info.fromDiffTo > 0) { 480 fromIndex = index + changedIndex; 481 toIndex = index + changedIndex - 1; 482 } 483 expiringTempItem_.try_emplace(toIndex, LazyForEachChild(info.key, info.node)); 484 expiringTempItem_.try_emplace(fromIndex, child); 485 } 486 } 487 CollectIndexChangedCount(std::map<int32_t,int32_t> & indexChangedMap)488 void LazyForEachBuilder::CollectIndexChangedCount(std::map<int32_t, int32_t>& indexChangedMap) 489 { 490 int32_t changedIndex = 0; 491 for (auto& [index, operationInfo] : operationList_) { 492 if (indexChangedMap.size() >= static_cast<size_t>(1)) { 493 for (int32_t i = indexChangedMap.rbegin()->first + 1; i < index; i++) { 494 indexChangedMap.try_emplace(i, changedIndex); 495 } 496 } 497 operationInfo.changeCount += changedIndex; 498 changedIndex = operationInfo.changeCount; 499 indexChangedMap.try_emplace(index, changedIndex); 500 } 501 } 502 ClassifyOperation(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)503 bool LazyForEachBuilder::ClassifyOperation(V2::Operation& operation, int32_t& initialIndex, 504 std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp) 505 { 506 switch (operationTypeMap[operation.type]) { 507 case OP::ADD: 508 OperateAdd(operation, initialIndex); 509 break; 510 case OP::DEL: 511 OperateDelete(operation, initialIndex); 512 break; 513 case OP::CHANGE: 514 OperateChange(operation, initialIndex, cachedTemp, expiringTemp); 515 break; 516 case OP::MOVE: 517 OperateMove(operation, initialIndex, cachedTemp, expiringTemp); 518 break; 519 case OP::EXCHANGE: 520 OperateExchange(operation, initialIndex, cachedTemp, expiringTemp); 521 break; 522 case OP::RELOAD: 523 OperateReload(expiringTemp); 524 return true; 525 } 526 return false; 527 } 528 ValidateIndex(int32_t index,const std::string & type)529 bool LazyForEachBuilder::ValidateIndex(int32_t index, const std::string& type) 530 { 531 bool isValid = true; 532 if (operationTypeMap[type] == OP::ADD) { 533 // for add operation, the index can equal totalCountOfOriginalDataset_ 534 isValid = index >= 0 && index <= totalCountOfOriginalDataset_; 535 } else { 536 isValid = index >= 0 && index < totalCountOfOriginalDataset_; 537 } 538 if (!isValid) { 539 TAG_LOGE( 540 AceLogTag::ACE_LAZY_FOREACH, "%{public}s(%{public}d) Operation is out of range", type.c_str(), index); 541 } 542 return isValid; 543 } 544 OperateAdd(V2::Operation & operation,int32_t & initialIndex)545 void LazyForEachBuilder::OperateAdd(V2::Operation& operation, int32_t& initialIndex) 546 { 547 OperationInfo itemInfo; 548 if (!ValidateIndex(operation.index, operation.type)) { 549 return; 550 } 551 auto indexExist = operationList_.find(operation.index); 552 if (indexExist == operationList_.end()) { 553 itemInfo.changeCount = operation.count; 554 if (!operation.key.empty()) { 555 itemInfo.extraKey.push_back(operation.key); 556 } else if (operation.keyList.size() >= static_cast<size_t>(1)) { 557 for (std::string key : operation.keyList) { 558 itemInfo.extraKey.push_back(key); 559 } 560 } 561 initialIndex = std::min(initialIndex, operation.index); 562 operationList_.try_emplace(operation.index, itemInfo); 563 } else { 564 ThrowRepeatOperationError(operation.index); 565 } 566 } 567 OperateDelete(V2::Operation & operation,int32_t & initialIndex)568 void LazyForEachBuilder::OperateDelete(V2::Operation& operation, int32_t& initialIndex) 569 { 570 OperationInfo itemInfo; 571 if (!ValidateIndex(operation.index, operation.type)) { 572 return; 573 } 574 auto indexExist = operationList_.find(operation.index); 575 if (indexExist == operationList_.end()) { 576 itemInfo.changeCount = -operation.count; 577 itemInfo.isDeleting = true; 578 initialIndex = std::min(initialIndex, operation.index); 579 operationList_.try_emplace(operation.index, itemInfo); 580 for (int32_t i = operation.index + 1; i < operation.index + operation.count; i++) { 581 OperationInfo extraInfo; 582 if (operationList_.find(i) == operationList_.end()) { 583 extraInfo.isDeleting = true; 584 operationList_.try_emplace(i, extraInfo); 585 } else { 586 ThrowRepeatOperationError(i); 587 } 588 } 589 } else { 590 ThrowRepeatOperationError(operation.index); 591 } 592 } 593 OperateChange(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)594 void LazyForEachBuilder::OperateChange(V2::Operation& operation, int32_t& initialIndex, 595 std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp) 596 { 597 OperationInfo itemInfo; 598 if (!ValidateIndex(operation.index, operation.type)) { 599 return; 600 } 601 auto indexExist = operationList_.find(operation.index); 602 if (indexExist == operationList_.end()) { 603 itemInfo.isChanged = true; 604 auto iter = cachedTemp.find(operation.index); 605 if (iter == cachedTemp.end()) { 606 iter = expiringTemp.find(operation.index); 607 } 608 if (iter == expiringTemp.end()) { 609 return; 610 } 611 if (!operation.key.empty()) { 612 itemInfo.key = operation.key; 613 } else { 614 itemInfo.key = iter->second.first; 615 } 616 initialIndex = std::min(initialIndex, operation.index); 617 operationList_.try_emplace(operation.index, itemInfo); 618 } else { 619 ThrowRepeatOperationError(operation.index); 620 } 621 } 622 OperateMove(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)623 void LazyForEachBuilder::OperateMove(V2::Operation& operation, int32_t& initialIndex, 624 std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp) 625 { 626 OperationInfo fromInfo; 627 OperationInfo toInfo; 628 if (!ValidateIndex(operation.coupleIndex.first, operation.type) || 629 !ValidateIndex(operation.coupleIndex.second, operation.type)) { 630 return; 631 } 632 auto fromIndexExist = operationList_.find(operation.coupleIndex.first); 633 auto toIndexExist = operationList_.find(operation.coupleIndex.second); 634 if (fromIndexExist == operationList_.end()) { 635 fromInfo.changeCount = -1; 636 fromInfo.isDeleting = true; 637 initialIndex = std::min(initialIndex, operation.coupleIndex.first); 638 operationList_.try_emplace(operation.coupleIndex.first, fromInfo); 639 } else { 640 ThrowRepeatOperationError(operation.coupleIndex.first); 641 } 642 if (toIndexExist == operationList_.end()) { 643 toInfo.changeCount = 1; 644 auto iter = cachedTemp.find(operation.coupleIndex.first); 645 if (iter == cachedTemp.end()) { 646 iter = expiringTemp.find(operation.coupleIndex.first); 647 } 648 if (iter == expiringTemp.end()) { 649 return; 650 } 651 toInfo.node = iter->second.second; 652 toInfo.moveIn = true; 653 toInfo.fromDiffTo = operation.coupleIndex.first - operation.coupleIndex.second; 654 if (!operation.key.empty()) { 655 toInfo.key = operation.key; 656 } else { 657 toInfo.key = iter->second.first; 658 } 659 initialIndex = std::min(initialIndex, operation.coupleIndex.second); 660 operationList_.try_emplace(operation.coupleIndex.second, toInfo); 661 } else { 662 ThrowRepeatOperationError(operation.coupleIndex.second); 663 } 664 } 665 OperateExchange(V2::Operation & operation,int32_t & initialIndex,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)666 void LazyForEachBuilder::OperateExchange(V2::Operation& operation, int32_t& initialIndex, 667 std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp) 668 { 669 OperationInfo startInfo; 670 OperationInfo endInfo; 671 if (!ValidateIndex(operation.coupleIndex.first, operation.type) || 672 !ValidateIndex(operation.coupleIndex.second, operation.type)) { 673 return; 674 } 675 auto startIndexExist = operationList_.find(operation.coupleIndex.first); 676 auto endIndexExist = operationList_.find(operation.coupleIndex.second); 677 if (startIndexExist == operationList_.end()) { 678 auto iter = FindItem(operation.coupleIndex.first, cachedTemp, expiringTemp); 679 // if item can't be find in cachedItems_ nor expiringItem_, set UI node to null 680 if (iter == expiringTemp.end()) { 681 startInfo.node = nullptr; 682 } else { 683 startInfo.node = iter->second.second; 684 if (!operation.coupleKey.first.empty()) { 685 startInfo.key = operation.coupleKey.first; 686 } else { 687 startInfo.key = iter->second.first; 688 } 689 } 690 startInfo.isExchange = true; 691 initialIndex = std::min(initialIndex, operation.coupleIndex.second); 692 operationList_.try_emplace(operation.coupleIndex.second, startInfo); 693 } else { 694 ThrowRepeatOperationError(operation.coupleIndex.first); 695 } 696 if (endIndexExist == operationList_.end()) { 697 auto iter = FindItem(operation.coupleIndex.second, cachedTemp, expiringTemp); 698 // if item can't be find in cachedItems_ nor expiringItem_, set UI node to null 699 if (iter == expiringTemp.end()) { 700 endInfo.node = nullptr; 701 } else { 702 endInfo.node = iter->second.second; 703 if (!operation.coupleKey.second.empty()) { 704 endInfo.key = operation.coupleKey.second; 705 } else { 706 endInfo.key = iter->second.first; 707 } 708 } 709 endInfo.isExchange = true; 710 initialIndex = std::min(initialIndex, operation.coupleIndex.first); 711 operationList_.try_emplace(operation.coupleIndex.first, endInfo); 712 } else { 713 ThrowRepeatOperationError(operation.coupleIndex.second); 714 } 715 } 716 FindItem(int32_t index,std::map<int32_t,LazyForEachChild> & cachedTemp,std::map<int32_t,LazyForEachChild> & expiringTemp)717 std::map<int32_t, LazyForEachChild>::iterator LazyForEachBuilder::FindItem(int32_t index, 718 std::map<int32_t, LazyForEachChild>& cachedTemp, std::map<int32_t, LazyForEachChild>& expiringTemp) 719 { 720 auto iterOfCached = cachedTemp.find(index); 721 auto iterOfExpiring = expiringTemp.find(index); 722 // if UI node can't be find in cachedTemp, find it in expiringTemp 723 if (iterOfCached == cachedTemp.end() || iterOfCached->second.second == nullptr) { 724 return iterOfExpiring; 725 } else { 726 return iterOfCached; 727 } 728 } 729 OperateReload(std::map<int32_t,LazyForEachChild> & expiringTemp)730 void LazyForEachBuilder::OperateReload(std::map<int32_t, LazyForEachChild>& expiringTemp) 731 { 732 for (auto& [index, node] : expiringTemp) { 733 expiringItem_.emplace(node.first, LazyForEachCacheChild(index, node.second)); 734 } 735 operationList_.clear(); 736 OnDataReloaded(); 737 } 738 ThrowRepeatOperationError(int32_t index)739 void LazyForEachBuilder::ThrowRepeatOperationError(int32_t index) 740 { 741 TAG_LOGE(AceLogTag::ACE_LAZY_FOREACH, "Repeat Operation for index: %{public}d", index); 742 } 743 RecycleChildByIndex(int32_t index)744 void LazyForEachBuilder::RecycleChildByIndex(int32_t index) 745 { 746 auto iter = cachedItems_.find(index); 747 if (iter != cachedItems_.end()) { 748 if (!iter->second.second) { 749 return; 750 } 751 auto dummyNode = AceType::DynamicCast<RecycleDummyNode>(iter->second.second); 752 if (!dummyNode) { 753 return; 754 } 755 auto keyIter = expiringItem_.find(iter->second.first); 756 if (keyIter != expiringItem_.end() && keyIter->second.second) { 757 expiringItem_.erase(keyIter); 758 } 759 cachedItems_.erase(index); 760 } 761 } 762 PreBuild(int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)763 bool LazyForEachBuilder::PreBuild(int64_t deadline, const std::optional<LayoutConstraintF>& itemConstraint, 764 bool canRunLongPredictTask) 765 { 766 ACE_SYNTAX_SCOPED_TRACE("expiringItem_ count:[%zu]", expiringItem_.size()); 767 outOfBoundaryNodes_.clear(); 768 if (itemConstraint && !canRunLongPredictTask) { 769 return false; 770 } 771 auto count = OnGetTotalCount(); 772 std::unordered_map<std::string, LazyForEachCacheChild> cache; 773 std::set<int32_t> idleIndexes; 774 if (startIndex_ != -1 && endIndex_ != -1) { 775 CheckCacheIndex(idleIndexes, count); 776 } 777 778 ProcessCachedIndex(cache, idleIndexes); 779 780 bool result = true; 781 result = ProcessPreBuildingIndex(cache, deadline, itemConstraint, canRunLongPredictTask, idleIndexes); 782 if (!result) { 783 expiringItem_.swap(cache); 784 return result; 785 } 786 787 for (auto index : idleIndexes) { 788 result = PreBuildByIndex(index, cache, deadline, itemConstraint, canRunLongPredictTask); 789 if (!result) { 790 break; 791 } 792 } 793 expiringItem_.swap(cache); 794 return result; 795 } 796 RecordOutOfBoundaryNodes(int32_t index)797 void LazyForEachBuilder::RecordOutOfBoundaryNodes(int32_t index) 798 { 799 outOfBoundaryNodes_.emplace_back(index); 800 } 801 RecycleItemsOutOfBoundary()802 void LazyForEachBuilder::RecycleItemsOutOfBoundary() 803 { 804 for (const auto& i: outOfBoundaryNodes_) { 805 RecycleChildByIndex(i); 806 } 807 outOfBoundaryNodes_.clear(); 808 } 809 UpdateMoveFromTo(int32_t from,int32_t to)810 void LazyForEachBuilder::UpdateMoveFromTo(int32_t from, int32_t to) 811 { 812 if (moveFromTo_) { 813 moveFromTo_.value().second = to; 814 } else { 815 moveFromTo_ = { from, to }; 816 } 817 } 818 ResetMoveFromTo()819 void LazyForEachBuilder::ResetMoveFromTo() 820 { 821 moveFromTo_.reset(); 822 } 823 ConvertFromToIndex(int32_t index)824 int32_t LazyForEachBuilder::ConvertFromToIndex(int32_t index) 825 { 826 if (!moveFromTo_) { 827 return index; 828 } 829 if (moveFromTo_.value().second == index) { 830 return moveFromTo_.value().first; 831 } 832 if (moveFromTo_.value().first <= index && index < moveFromTo_.value().second) { 833 return index + 1; 834 } 835 if (moveFromTo_.value().second < index && index <= moveFromTo_.value().first) { 836 return index - 1; 837 } 838 return index; 839 } 840 RemoveAllChild()841 void LazyForEachBuilder::RemoveAllChild() 842 { 843 ACE_SYNTAX_SCOPED_TRACE("LazyForEach RemoveAllChild"); 844 for (auto& [index, node] : cachedItems_) { 845 if (!node.second) { 846 continue; 847 } 848 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 849 if (frameNode) { 850 frameNode->SetActive(false); 851 } 852 auto tempNode = node.second; 853 auto pair = expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second))); 854 if (!pair.second) { 855 TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index); 856 ProcessOffscreenNode(tempNode, true); 857 } 858 } 859 } 860 SetActiveChildRange(int32_t start,int32_t end)861 bool LazyForEachBuilder::SetActiveChildRange(int32_t start, int32_t end) 862 { 863 ACE_SYNTAX_SCOPED_TRACE("LazyForEach active range start[%d], end[%d]", start, end); 864 int32_t count = GetTotalCount(); 865 UpdateHistoricalTotalCount(count); 866 bool needBuild = false; 867 auto tempCachedItems = cachedItems_; 868 for (auto& [index, node] : tempCachedItems) { 869 bool isInRange = (index < count) && ((start <= end && start <= index && end >= index) || 870 (start > end && (index <= end || index >= start))); 871 if (!isInRange) { 872 if (!node.second) { 873 continue; 874 } 875 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 876 if (frameNode) { 877 frameNode->SetActive(false); 878 } 879 cachedItems_[index] = LazyForEachChild(node.first, nullptr); 880 auto tempNode = node.second; 881 auto pair = expiringItem_.try_emplace(node.first, LazyForEachCacheChild(index, std::move(node.second))); 882 if (!pair.second) { 883 TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index); 884 ProcessOffscreenNode(tempNode, true); 885 } 886 needBuild = true; 887 continue; 888 } 889 if (node.second) { 890 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 891 if (frameNode) { 892 frameNode->SetActive(true); 893 } 894 continue; 895 } 896 auto keyIter = expiringItem_.find(node.first); 897 if (keyIter != expiringItem_.end() && keyIter->second.second) { 898 node.second = keyIter->second.second; 899 expiringItem_.erase(keyIter); 900 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 901 if (frameNode) { 902 frameNode->SetActive(true); 903 } 904 } 905 needBuild = true; 906 } 907 return needBuild; 908 } 909 GetChildIndex(const RefPtr<FrameNode> & targetNode)910 int32_t LazyForEachBuilder::GetChildIndex(const RefPtr<FrameNode>& targetNode) 911 { 912 for (auto& [index, node] : cachedItems_) { 913 if (node.second) { 914 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 915 if (frameNode == targetNode) { 916 return index; 917 } 918 } 919 } 920 for (auto& [key, node] : expiringItem_) { 921 if (!node.second) { 922 continue; 923 } 924 auto frameNode = AceType::DynamicCast<FrameNode>(node.second->GetFrameChildByIndex(0, true)); 925 if (frameNode && frameNode == targetNode) { 926 return node.first; 927 } 928 } 929 return -1; 930 } 931 CacheItem(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,const std::optional<LayoutConstraintF> & itemConstraint,int64_t deadline,bool & isTimeout)932 RefPtr<UINode> LazyForEachBuilder::CacheItem(int32_t index, 933 std::unordered_map<std::string, LazyForEachCacheChild>& cache, 934 const std::optional<LayoutConstraintF>& itemConstraint, int64_t deadline, bool& isTimeout) 935 { 936 ACE_SCOPED_TRACE("Builder:BuildLazyItem [%d]", index); 937 auto itemInfo = OnGetChildByIndex(ConvertFromToIndex(index), expiringItem_); 938 CHECK_NULL_RETURN(itemInfo.second, nullptr); 939 auto pair = cache.try_emplace(itemInfo.first, LazyForEachCacheChild(index, itemInfo.second)); 940 auto context = itemInfo.second->GetContext(); 941 CHECK_NULL_RETURN(context, itemInfo.second); 942 auto frameNode = AceType::DynamicCast<FrameNode>(itemInfo.second->GetFrameChildByIndex(0, false, true)); 943 context->SetPredictNode(frameNode); 944 if (!itemInfo.second->RenderCustomChild(deadline)) { 945 isTimeout = true; 946 context->ResetPredictNode(); 947 return itemInfo.second; 948 } 949 if (pair.second) { 950 ProcessOffscreenNode(itemInfo.second, false); 951 } else { 952 TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Use repeat key for index: %{public}d", index); 953 } 954 955 itemInfo.second->Build(nullptr); 956 context->ResetPredictNode(); 957 itemInfo.second->SetJSViewActive(false, true); 958 cachedItems_[index] = LazyForEachChild(itemInfo.first, nullptr); 959 return itemInfo.second; 960 } 961 CheckCacheIndex(std::set<int32_t> & idleIndexes,int32_t count)962 void LazyForEachBuilder::CheckCacheIndex(std::set<int32_t>& idleIndexes, int32_t count) 963 { 964 if (count == 0) { 965 return; 966 } 967 for (int32_t i = 1; i <= cacheCount_ - endShowCached_; i++) { 968 if (isLoop_) { 969 if ((startIndex_ <= endIndex_ && endIndex_ + i < count) || 970 startIndex_ > endIndex_ + i) { 971 idleIndexes.emplace(endIndex_ + i); 972 } else if ((endIndex_ + i) % count < startIndex_) { 973 idleIndexes.emplace((endIndex_ + i) % count); 974 } 975 } else { 976 if (endIndex_ + i < count) { 977 idleIndexes.emplace(endIndex_ + i); 978 } 979 } 980 } 981 for (int32_t i = 1; i <= cacheCount_ - startShowCached_; i++) { 982 if (isLoop_) { 983 if ((startIndex_ <= endIndex_ && startIndex_ >= i) || 984 startIndex_ > endIndex_ + i) { 985 idleIndexes.emplace(startIndex_ - i); 986 } else if ((startIndex_ - i + count) % count > endIndex_) { 987 idleIndexes.emplace((startIndex_ - i + count) % count); 988 } 989 } else { 990 if (startIndex_ >= i) { 991 idleIndexes.emplace(startIndex_ - i); 992 } 993 } 994 } 995 } 996 PreBuildByIndex(int32_t index,std::unordered_map<std::string,LazyForEachCacheChild> & cache,int64_t deadline,const std::optional<LayoutConstraintF> & itemConstraint,bool canRunLongPredictTask)997 bool LazyForEachBuilder::PreBuildByIndex(int32_t index, 998 std::unordered_map<std::string, LazyForEachCacheChild>& cache, int64_t deadline, 999 const std::optional<LayoutConstraintF>& itemConstraint, bool canRunLongPredictTask) 1000 { 1001 if (!enablePreBuild_ || GetSysTimestamp() > deadline) { 1002 if (DeleteExpiringItemImmediately()) { 1003 return false; 1004 } 1005 for (const auto& [key, node] : expiringItem_) { 1006 if (node.first == -1) { 1007 cache.try_emplace(key, node); 1008 } 1009 } 1010 return false; 1011 } 1012 bool isTimeout = false; 1013 preBuildingIndex_ = -1; 1014 auto uiNode = CacheItem(index, cache, itemConstraint, deadline, isTimeout); 1015 if (isTimeout) { 1016 preBuildingIndex_ = index; 1017 return false; 1018 } 1019 if (!canRunLongPredictTask && itemConstraint) { 1020 return false; 1021 } 1022 if (canRunLongPredictTask && uiNode && itemConstraint) { 1023 RefPtr<FrameNode> frameNode = DynamicCast<FrameNode>(uiNode); 1024 while (!frameNode) { 1025 auto tempNode = uiNode; 1026 uiNode = tempNode->GetFirstChild(); 1027 if (!uiNode) { 1028 break; 1029 } 1030 frameNode = DynamicCast<FrameNode>(uiNode); 1031 } 1032 if (frameNode) { 1033 frameNode->GetGeometryNode()->SetParentLayoutConstraint(itemConstraint.value()); 1034 FrameNode::ProcessOffscreenNode(frameNode); 1035 } 1036 } 1037 return true; 1038 } 1039 ProcessCachedIndex(std::unordered_map<std::string,LazyForEachCacheChild> & cache,std::set<int32_t> & idleIndexes)1040 void LazyForEachBuilder::ProcessCachedIndex(std::unordered_map<std::string, LazyForEachCacheChild>& cache, 1041 std::set<int32_t>& idleIndexes) 1042 { 1043 auto expiringIter = expiringItem_.begin(); 1044 while (expiringIter != expiringItem_.end()) { 1045 const auto& key = expiringIter->first; 1046 const auto& node = expiringIter->second; 1047 auto iter = idleIndexes.find(node.first); 1048 if (iter != idleIndexes.end() && node.second) { 1049 LoadCacheByIndex(cache, idleIndexes, node, key, iter, expiringIter); 1050 } else { 1051 LoadCacheByKey(cache, idleIndexes, node, key, expiringIter); 1052 } 1053 } 1054 } 1055 ProcessOffscreenNode(RefPtr<UINode> uiNode,bool remove)1056 void LazyForEachBuilder::ProcessOffscreenNode(RefPtr<UINode> uiNode, bool remove) 1057 { 1058 if (uiNode) { 1059 auto frameNode = DynamicCast<FrameNode>(uiNode); 1060 while (!frameNode) { 1061 auto tempNode = uiNode; 1062 uiNode = tempNode->GetFirstChild(); 1063 if (!uiNode) { 1064 break; 1065 } 1066 frameNode = DynamicCast<FrameNode>(uiNode); 1067 } 1068 if (frameNode) { 1069 if (!remove) { 1070 Inspector::AddOffscreenNode(frameNode); 1071 } else { 1072 Inspector::RemoveOffscreenNode(frameNode); 1073 } 1074 } 1075 } 1076 } 1077 GetAllChildren()1078 const std::map<int32_t, LazyForEachChild>& LazyForEachBuilder::GetAllChildren() 1079 { 1080 if (!cachedItems_.empty()) { 1081 startIndex_ = cachedItems_.begin()->first; 1082 endIndex_ = cachedItems_.rbegin()->first; 1083 } 1084 if (isLoop_ && !cachedItems_.empty()) { 1085 int32_t lastIndex = -1; 1086 for (auto& [index, node] : cachedItems_) { 1087 if (lastIndex > -1 && index - lastIndex > 1) { 1088 startIndex_ = index; 1089 endIndex_ = lastIndex; 1090 break; 1091 } 1092 } 1093 } 1094 return cachedItems_; 1095 } 1096 1097 /** 1098 * Traverse nodes in cachedItems_, expiringItem_ and nodeList_, set the MeasureAnyway and Rerenderable properties 1099 * of all children the same value as LazyForEach node, and call NotifyColorModeChange. 1100 * When MeasureAnyway is true, perform measure and layout, force an update. 1101 * For cachedItems_, nodes are active and will measure and layout anyway, so skip SetMeasureAnyway. 1102 */ NotifyColorModeChange(uint32_t colorMode,bool rerenderable)1103 void LazyForEachBuilder::NotifyColorModeChange(uint32_t colorMode, bool rerenderable) 1104 { 1105 for (const auto& node : cachedItems_) { 1106 if (node.second.second == nullptr) { 1107 continue; 1108 } 1109 node.second.second->SetRerenderable(rerenderable); 1110 node.second.second->NotifyColorModeChange(colorMode); 1111 } 1112 for (const auto& node : expiringItem_) { 1113 if (node.second.second == nullptr) { 1114 continue; 1115 } 1116 node.second.second->SetMeasureAnyway(rerenderable); 1117 node.second.second->SetRerenderable(rerenderable); 1118 node.second.second->NotifyColorModeChange(colorMode); 1119 } 1120 for (const auto& node : nodeList_) { 1121 if (node.second == nullptr) { 1122 continue; 1123 } 1124 node.second->SetMeasureAnyway(rerenderable); 1125 node.second->SetRerenderable(rerenderable); 1126 node.second->NotifyColorModeChange(colorMode); 1127 } 1128 } 1129 SetJSViewActive(bool active)1130 void LazyForEachBuilder::SetJSViewActive(bool active) 1131 { 1132 for (const auto& node : cachedItems_) { 1133 if (node.second.second == nullptr) { 1134 continue; 1135 } 1136 node.second.second->SetJSViewActive(active, true); 1137 } 1138 for (const auto& node : expiringItem_) { 1139 if (node.second.second == nullptr) { 1140 continue; 1141 } 1142 node.second.second->SetJSViewActive(active, true); 1143 } 1144 } 1145 PaintDebugBoundaryTreeAll(bool flag)1146 void LazyForEachBuilder::PaintDebugBoundaryTreeAll(bool flag) 1147 { 1148 for (const auto& node : cachedItems_) { 1149 if (node.second.second == nullptr) { 1150 continue; 1151 } 1152 node.second.second->PaintDebugBoundaryTreeAll(flag); 1153 } 1154 for (const auto& node : expiringItem_) { 1155 if (node.second.second == nullptr) { 1156 continue; 1157 } 1158 node.second.second->PaintDebugBoundaryTreeAll(flag); 1159 } 1160 } SetDestroying(bool isDestroying,bool cleanStatus)1161 void LazyForEachBuilder::SetDestroying(bool isDestroying, bool cleanStatus) 1162 { 1163 for (const auto& node : cachedItems_) { 1164 if (node.second.second == nullptr) { 1165 continue; 1166 } 1167 if (node.second.second->IsReusableNode()) { 1168 node.second.second->SetDestroying(isDestroying, false); 1169 } else { 1170 node.second.second->SetDestroying(isDestroying, cleanStatus); 1171 } 1172 } 1173 1174 for (const auto& node : expiringItem_) { 1175 if (node.second.second == nullptr) { 1176 continue; 1177 } 1178 if (node.second.second->IsReusableNode()) { 1179 node.second.second->SetDestroying(isDestroying, false); 1180 } else { 1181 node.second.second->SetDestroying(isDestroying, cleanStatus); 1182 } 1183 } 1184 } 1185 } 1186