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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_POSITION_MAP_H 17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_POSITION_MAP_H 18 19 20 #include <cstddef> 21 #include <cstdint> 22 #include <functional> 23 #include <optional> 24 #include <tuple> 25 #include <queue> 26 27 #include "base/geometry/dimension.h" 28 #include "base/memory/referenced.h" 29 #include "base/utils/utils.h" 30 #include "core/components_ng/base/ui_node.h" 31 #include "core/components_ng/syntax/lazy_for_each_node.h" 32 #include "core/components_ng/syntax/repeat_virtual_scroll_node.h" 33 #include "core/components_ng/pattern/list/list_children_main_size.h" 34 #include "core/components_ng/pattern/list/list_item_group_pattern.h" 35 #include "core/components_ng/property/measure_property.h" 36 37 38 namespace OHOS::Ace::NG { 39 class ListItemGroupPattern; 40 namespace { 41 42 struct PositionInfo { 43 float mainPos; 44 float mainSize; 45 }; 46 47 enum class ListPosMapUpdate { 48 NO_CHANGE = 0, 49 UPDATE_ALL_SIZE, 50 RE_CALCULATE, 51 }; 52 } 53 54 class ListPositionMap : public virtual AceType { 55 DECLARE_ACE_TYPE(ListPositionMap, AceType) 56 public: 57 ListPositionMap() = default; 58 ~ListPositionMap() override = default; 59 UpdatePos(int32_t index,PositionInfo posInfo)60 void UpdatePos(int32_t index, PositionInfo posInfo) 61 { 62 posMap_[index] = { posInfo.mainPos, posInfo.mainSize }; 63 } 64 UpdatePosWithCheck(int32_t index,PositionInfo posInfo)65 void UpdatePosWithCheck(int32_t index, PositionInfo posInfo) 66 { 67 auto iter = posMap_.find(index); 68 if (iter == posMap_.end()) { 69 posMap_[index] = posInfo; 70 return; 71 } 72 if (LessNotEqual(iter->second.mainSize, posInfo.mainSize)) { 73 iter->second.mainSize = posInfo.mainSize; 74 } 75 } 76 ClearPosMap()77 void ClearPosMap() 78 { 79 posMap_.clear(); 80 } 81 MarkDirty(ListChangeFlag flag)82 void MarkDirty(ListChangeFlag flag) 83 { 84 dirty_ = dirty_ | flag; 85 } 86 ClearDirty()87 void ClearDirty() 88 { 89 dirty_ = LIST_NO_CHANGE; 90 } 91 GetTotalHeight()92 float GetTotalHeight() const 93 { 94 return totalHeight_; 95 } 96 GetPrevTotalHeight()97 float GetPrevTotalHeight() const 98 { 99 return prevTotalHeight_; 100 } 101 CheckPosMapUpdateRule()102 ListPosMapUpdate CheckPosMapUpdateRule() 103 { 104 ListPosMapUpdate flag; 105 if (dirty_ == LIST_NO_CHANGE) { 106 flag = ListPosMapUpdate::NO_CHANGE; 107 } else if (0 == (dirty_ & (LIST_UPDATE_CHILD_SIZE | LIST_UPDATE_LANES | LIST_GROUP_UPDATE_HEADER_FOOTER))) { 108 flag = ListPosMapUpdate::UPDATE_ALL_SIZE; 109 } else { 110 flag = ListPosMapUpdate::RE_CALCULATE; 111 } 112 return flag; 113 } 114 UpdatePosMapStart(float delta,float & listCurrentPos,float space,int32_t startIndex,float startPos,bool groupAtStart)115 void UpdatePosMapStart(float delta, float& listCurrentPos, float space, 116 int32_t startIndex, float startPos, bool groupAtStart) 117 { 118 listCurrentPos += delta; 119 auto it = posMap_.find(startIndex); 120 if (it == posMap_.begin() || it == posMap_.end()) { 121 return; 122 } 123 startPos = it->second.mainPos; 124 it--; 125 float prevPos = it->second.mainPos + it->second.mainSize + space; 126 int32_t prevIndex = it->first; 127 if (prevIndex + 1 >= startIndex && groupAtStart) { 128 if (NearEqual(prevPos, startPos)) { 129 return; 130 } 131 } else { 132 if (LessNotEqual(prevPos, startPos)) { 133 return; 134 } 135 } 136 listCurrentPos += prevPos - startPos; 137 } 138 UpdatePosMapEnd(int32_t prevEndIndex,float space,bool groupAtEnd)139 void UpdatePosMapEnd(int32_t prevEndIndex, float space, bool groupAtEnd) 140 { 141 auto it = posMap_.find(prevEndIndex); 142 if (it == posMap_.end()) { 143 return; 144 } 145 float prevPos = it->second.mainPos + it->second.mainSize + space; 146 it++; 147 if (it == posMap_.end()) { 148 return; 149 } 150 if (prevEndIndex + 1 >= it->first && groupAtEnd) { 151 if (NearEqual(prevPos, it->second.mainPos)) { 152 return; 153 } 154 } else { 155 if (LessNotEqual(prevPos, it->second.mainPos)) { 156 return; 157 } 158 } 159 float delta = prevPos - it->second.mainPos; 160 while (it != posMap_.end()) { 161 it->second.mainPos += delta; 162 it++; 163 } 164 } 165 CalculateUINode(RefPtr<UINode> node)166 void CalculateUINode(RefPtr<UINode> node) 167 { 168 CHECK_NULL_VOID(node); 169 auto children = node->GetChildren(); 170 for (const auto& child : children) { 171 if (AceType::InstanceOf<FrameNode>(child)) { 172 auto frameNode = AceType::DynamicCast<FrameNode>(child); 173 CalculateFrameNode(frameNode); 174 } else if (AceType::InstanceOf<LazyForEachNode>(child) || 175 AceType::InstanceOf<RepeatVirtualScrollNode>(child)) { 176 // Rules: only one type node(ListItem or ListItemGroup) can exist in LazyForEach. 177 CalculateLazyForEachNode(child); 178 } else { 179 CalculateUINode(child); 180 } 181 } 182 } 183 GetLazyForEachChildIsGroup(RefPtr<UINode> node)184 std::optional<bool> GetLazyForEachChildIsGroup(RefPtr<UINode> node) 185 { 186 std::optional<bool> isGroup; 187 auto children = node->GetChildren(); 188 if (children.size() > 0) { 189 auto child = children.begin(); 190 while (child != children.end() && !((*child)->GetFrameChildByIndex(0, false))) { 191 child++; 192 } 193 auto frameNode = AceType::DynamicCast<FrameNode>((*child)->GetFrameChildByIndex(0, false)); 194 if (frameNode) { 195 isGroup = frameNode->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 196 } 197 } 198 if (!(isGroup.has_value())) { 199 auto listNode = node->GetParentFrameNode(); 200 CHECK_NULL_RETURN(listNode, isGroup); 201 auto wrapper = listNode->GetOrCreateChildByIndex(curIndex_); 202 CHECK_NULL_RETURN(wrapper, isGroup); 203 isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 204 } 205 return isGroup; 206 } 207 CalculateLazyForEachNode(RefPtr<UINode> node)208 void CalculateLazyForEachNode(RefPtr<UINode> node) 209 { 210 int32_t count = node->FrameCount(); 211 if (count <= 0) { 212 return; 213 } 214 std::optional<bool> isGroup = GetLazyForEachChildIsGroup(node); 215 if (!(isGroup.has_value())) { 216 TAG_LOGW(AceLogTag::ACE_LIST, 217 "ListPositionMap Conflict: LazyForEach FrameCount > 0, but get child type failed."); 218 isGroup = false; 219 } 220 while (count--) { 221 isGroup.value() ? CalculateGroupNode() : CalculateListItemNode(); 222 } 223 return; 224 } 225 CalculateFrameNode(RefPtr<FrameNode> frameNode)226 void CalculateFrameNode(RefPtr<FrameNode> frameNode) 227 { 228 CHECK_NULL_VOID(frameNode); 229 auto listItemGroupPatten = frameNode->GetPattern<ListItemGroupPattern>(); 230 if (listItemGroupPatten) { 231 CalculateGroupNode(); 232 } else { 233 CalculateListItemNode(); 234 } 235 } 236 CalculateListItemNode()237 void CalculateListItemNode() 238 { 239 curRowHeight_ = std::max(curRowHeight_, childrenSize_->GetChildSize(curIndex_)); 240 curLine_++; 241 if (curLine_ == lanes_ || curIndex_ == totalItemCount_ - 1) { 242 while (curLine_) { 243 curLine_--; 244 posMap_[curIndex_ - curLine_] = { totalHeight_, curRowHeight_ }; 245 } 246 totalHeight_ += (curRowHeight_ + space_); 247 curRowHeight_ = 0.0f; 248 } 249 curIndex_++; 250 } 251 CalculateGroupNode()252 void CalculateGroupNode() 253 { 254 if (curLine_ > 0) { 255 while (curLine_) { 256 curLine_--; 257 posMap_[curIndex_ - 1 - curLine_] = { totalHeight_, curRowHeight_ }; 258 } 259 totalHeight_ += (curRowHeight_ + space_); 260 curRowHeight_ = 0.0f; 261 } 262 curRowHeight_ = childrenSize_->GetChildSize(curIndex_); 263 posMap_[curIndex_] = { totalHeight_, curRowHeight_ }; 264 totalHeight_ += (curRowHeight_ + space_); 265 curLine_ = 0; 266 curRowHeight_ = 0.0f; 267 curIndex_++; 268 } 269 PosMapRecalculate(LayoutWrapper * layoutWrapper)270 void PosMapRecalculate(LayoutWrapper* layoutWrapper) 271 { 272 curIndex_ = 0; 273 curLine_ = 0; 274 totalHeight_ = 0.0f; 275 curRowHeight_ = 0.0f; 276 if (lanes_ == 1) { 277 for (int32_t index = 0; index < totalItemCount_; index++) { 278 curRowHeight_ = childrenSize_->GetChildSize(index); 279 posMap_[index] = { totalHeight_, curRowHeight_ }; 280 totalHeight_ += (curRowHeight_ + space_); 281 } 282 totalHeight_ -= space_; 283 } else { 284 auto listNode = layoutWrapper->GetHostNode(); 285 CHECK_NULL_VOID(listNode); 286 CalculateUINode(listNode); 287 totalHeight_ -= space_; 288 } 289 TAG_LOGI(AceLogTag::ACE_LIST, "List PosMapRecalculate totalHeight:%{public}f prevTotalHeight:%{public}f, " 290 "lanes:%{public}d, space:%{public}f, totalItemCount:%{public}d", 291 totalHeight_, prevTotalHeight_, lanes_, space_, totalItemCount_); 292 } 293 GroupPosMapRecalculate()294 void GroupPosMapRecalculate() 295 { 296 curIndex_ = 0; 297 totalHeight_ = headerSize_; 298 curRowHeight_ = 0.0f; 299 curLine_ = 0; 300 301 for (int32_t index = 0; index < totalItemCount_;) { 302 while (curLine_ < lanes_) { 303 curRowHeight_ = std::max(curRowHeight_, childrenSize_->GetChildSize(index + curLine_)); 304 curLine_++; 305 } 306 curLine_ = 0; 307 int32_t curRowEndIndex = std::min(index + lanes_ - 1, totalItemCount_ - 1); 308 while (index <= curRowEndIndex) { 309 posMap_[index++] = { totalHeight_, curRowHeight_ }; 310 } 311 totalHeight_ += (curRowHeight_ + space_); 312 curRowHeight_ = 0.0f; 313 } 314 totalHeight_ = totalHeight_ - space_ + footerSize_; 315 } 316 PosMapUpdateAllSize()317 void PosMapUpdateAllSize() 318 { 319 float curPos = 0.0f; 320 for (int32_t index = 0; index < totalItemCount_; index++) { 321 posMap_[index] = { curPos, curRowHeight_ }; 322 } 323 } 324 UpdatePosMap(LayoutWrapper * layoutWrapper,int32_t lanes,float space,RefPtr<ListChildrenMainSize> & childrenSize)325 void UpdatePosMap(LayoutWrapper* layoutWrapper, int32_t lanes, float space, 326 RefPtr<ListChildrenMainSize>& childrenSize) 327 { 328 childrenSize_ = childrenSize; 329 if (totalItemCount_ != layoutWrapper->GetTotalChildCount()) { 330 dirty_ |= LIST_UPDATE_ITEM_COUNT; 331 totalItemCount_ = layoutWrapper->GetTotalChildCount(); 332 } 333 if (lanes != lanes_) { 334 dirty_ |= LIST_UPDATE_LANES; 335 lanes_ = lanes; 336 } 337 if (!NearEqual(space, space_)) { 338 dirty_ |= LIST_UPDATE_SPACE; 339 space_ = space; 340 } 341 switch (CheckPosMapUpdateRule()) { 342 case ListPosMapUpdate::NO_CHANGE: 343 break; 344 case ListPosMapUpdate::UPDATE_ALL_SIZE: 345 PosMapRecalculate(layoutWrapper); 346 break; 347 case ListPosMapUpdate::RE_CALCULATE: 348 default: 349 PosMapRecalculate(layoutWrapper); 350 } 351 ClearDirty(); 352 } 353 UpdateGroupPosMap(int32_t totalCount,int32_t lanes,float space,RefPtr<ListChildrenMainSize> & childrenSize,float headerSize,float footerSize)354 void UpdateGroupPosMap(int32_t totalCount, int32_t lanes, float space, 355 RefPtr<ListChildrenMainSize>& childrenSize, float headerSize, float footerSize) 356 { 357 childrenSize_ = childrenSize; 358 prevTotalHeight_ = totalHeight_; 359 if (totalCount != totalItemCount_) { 360 dirty_ |= LIST_UPDATE_ITEM_COUNT; 361 totalItemCount_ = totalCount; 362 } 363 if (lanes != lanes_) { 364 dirty_ |= LIST_UPDATE_LANES; 365 lanes_ = lanes; 366 } 367 if (!NearEqual(space, space_)) { 368 dirty_ |= LIST_UPDATE_SPACE; 369 space_ = space; 370 } 371 if (!NearEqual(headerSize, headerSize_) || !NearEqual(footerSize, footerSize_)) { 372 dirty_ |= LIST_GROUP_UPDATE_HEADER_FOOTER; 373 headerSize_ = headerSize; 374 footerSize_ = footerSize; 375 } 376 if (totalItemCount_ <= 0) { 377 totalHeight_ = 0; 378 ClearPosMap(); 379 ClearDirty(); 380 return; 381 } 382 switch (CheckPosMapUpdateRule()) { 383 case ListPosMapUpdate::NO_CHANGE: 384 break; 385 case ListPosMapUpdate::UPDATE_ALL_SIZE: 386 GroupPosMapRecalculate(); 387 break; 388 case ListPosMapUpdate::RE_CALCULATE: 389 default: 390 GroupPosMapRecalculate(); 391 } 392 ClearDirty(); 393 } 394 UpdateTotalCount(int32_t totalCount)395 void UpdateTotalCount(int32_t totalCount) 396 { 397 totalItemCount_ = totalCount; 398 auto iter = posMap_.lower_bound(totalCount); 399 posMap_.erase(iter, posMap_.end()); 400 } 401 402 float GetPos(int32_t index, float offset = 0.0f) 403 { 404 return posMap_[index].mainPos - offset; 405 } 406 GetGroupLayoutOffset(int32_t startIndex,float startPos)407 float GetGroupLayoutOffset(int32_t startIndex, float startPos) 408 { 409 return posMap_[startIndex].mainPos - startPos; 410 } 411 GetPositionInfo(int32_t index)412 PositionInfo GetPositionInfo(int32_t index) const 413 { 414 auto it = posMap_.find(index); 415 if (it == posMap_.end()) { 416 return { -1.0f, -1.0f }; 417 } 418 return it->second; 419 } 420 GetStartIndexAndPos()421 std::pair<int32_t, float> GetStartIndexAndPos() const 422 { 423 if (posMap_.empty()) { 424 return { -1, 0.0f }; 425 } 426 const auto& start = posMap_.begin(); 427 return { start->first, start->second.mainPos }; 428 } 429 GetEndIndexAndPos()430 std::pair<int32_t, float> GetEndIndexAndPos() const 431 { 432 if (posMap_.empty()) { 433 return { -1, 0.0f }; 434 } 435 const auto& end = posMap_.rbegin(); 436 return { end->first, end->second.mainPos + end->second.mainSize }; 437 } 438 OptimizeBeforeMeasure(int32_t & beginIndex,float & beginPos,const float offset,const float contentSize)439 void OptimizeBeforeMeasure(int32_t& beginIndex, float& beginPos, const float offset, const float contentSize) 440 { 441 if (NearZero(offset) || GreatOrEqual(contentSize, totalHeight_)) { 442 return; 443 } 444 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f; 445 if (Positive(offset)) { 446 float criticalPos = offset; 447 std::pair<int32_t, float> rowInfo = GetRowEndIndexAndHeight(beginIndex); 448 while (!NearEqual(posMap_[beginIndex].mainPos + rowInfo.second, totalHeight_) && 449 LessNotEqual(beginPos + rowInfo.second + chainOffset, criticalPos)) { 450 beginIndex = rowInfo.first + 1; 451 beginPos += (rowInfo.second + space_); 452 rowInfo = GetRowEndIndexAndHeight(beginIndex); 453 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f; 454 } 455 } else { 456 float criticalPos = offset + contentSize; 457 std::pair<int32_t, float> rowInfo = GetRowEndIndexAndHeight(beginIndex); 458 while (Positive(posMap_[beginIndex].mainPos) && 459 GreatNotEqual(beginPos - rowInfo.second + chainOffset, criticalPos)) { 460 beginIndex = GetRowStartIndex(beginIndex) - 1; 461 beginPos -= (rowInfo.second + space_); 462 rowInfo = GetRowEndIndexAndHeight(beginIndex); 463 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f; 464 } 465 } 466 } 467 SetChainOffsetCallback(std::function<float (int32_t)> func)468 void SetChainOffsetCallback(std::function<float(int32_t)> func) 469 { 470 chainOffsetFunc_ = std::move(func); 471 } 472 GetRowStartIndex(const int32_t input)473 int32_t GetRowStartIndex(const int32_t input) 474 { 475 int32_t startIndex = input; 476 while (startIndex > 0 && NearEqual(posMap_[startIndex].mainPos, posMap_[startIndex - 1].mainPos)) { 477 startIndex--; 478 } 479 return startIndex; 480 } 481 GetRowEndIndex(const int32_t input)482 int32_t GetRowEndIndex(const int32_t input) 483 { 484 std::pair<int32_t, float> rowInfo = GetRowEndIndexAndHeight(input); 485 return rowInfo.first; 486 } 487 GetRowHeight(int32_t input)488 float GetRowHeight(int32_t input) 489 { 490 std::pair<int32_t, float> rowInfo = GetRowEndIndexAndHeight(input); 491 return rowInfo.second; 492 } 493 GetRowEndIndexAndHeight(const int32_t input)494 std::pair<int32_t, float> GetRowEndIndexAndHeight(const int32_t input) 495 { 496 int32_t endIndex = input; 497 float rowHeight = 0.0f; 498 while (endIndex < (totalItemCount_ - 1) && 499 NearEqual(posMap_[endIndex].mainPos, posMap_[endIndex + 1].mainPos)) { 500 endIndex++; 501 } 502 if (endIndex == totalItemCount_ - 1) { 503 rowHeight = totalHeight_ - posMap_[endIndex].mainPos - footerSize_; 504 } else { 505 rowHeight = posMap_[endIndex + 1].mainPos - posMap_[endIndex].mainPos - space_; 506 } 507 return { endIndex, rowHeight }; 508 } 509 private: 510 std::map<int32_t, PositionInfo> posMap_; 511 RefPtr<ListChildrenMainSize> childrenSize_; 512 std::function<float(int32_t)> chainOffsetFunc_; 513 ListChangeFlag dirty_ = LIST_NO_CHANGE; 514 int32_t totalItemCount_ = 0; 515 int32_t lanes_ = -1; 516 int32_t curLine_ = 0; 517 int32_t curIndex_ = 0; 518 float totalHeight_ = 0.0f; 519 float prevTotalHeight_ = 0.0f; 520 float curRowHeight_ = 0.0f; 521 float space_ = 0.0f; 522 float headerSize_ = 0.0f; 523 float footerSize_ = 0.0f; 524 }; 525 526 } // namespace OHOS::Ace::NG 527 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_POSITION_MAP_H