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 #include "core/components_ng/pattern/list/list_layout_algorithm.h" 17 18 #include <algorithm> 19 #include <cstdint> 20 #include <unordered_set> 21 #include <utility> 22 23 #include "base/geometry/axis.h" 24 #include "base/geometry/ng/offset_t.h" 25 #include "base/geometry/ng/size_t.h" 26 #include "base/log/ace_trace.h" 27 #include "base/memory/ace_type.h" 28 #include "base/utils/time_util.h" 29 #include "base/utils/utils.h" 30 #include "core/components/common/layout/layout_param.h" 31 #include "core/components_ng/base/frame_node.h" 32 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h" 33 #include "core/components_ng/pattern/list/list_item_group_pattern.h" 34 #include "core/components_ng/pattern/list/list_item_pattern.h" 35 #include "core/components_ng/pattern/list/list_layout_property.h" 36 #include "core/components_ng/pattern/list/list_pattern.h" 37 #include "core/components_ng/pattern/scrollable/scrollable_utils.h" 38 #include "core/components_ng/pattern/text/text_base.h" 39 #include "core/components_ng/pattern/text_field/text_field_manager.h" 40 #include "core/components_ng/property/layout_constraint.h" 41 #include "core/components_ng/property/measure_property.h" 42 #include "core/components_ng/property/measure_utils.h" 43 #include "core/components_ng/property/property.h" 44 #include "core/components_v2/inspector/inspector_constants.h" 45 #include "core/components_v2/list/list_properties.h" 46 #include "core/pipeline_ng/pipeline_context.h" 47 48 namespace OHOS::Ace::NG { 49 50 namespace { 51 constexpr Dimension RESERVE_BOTTOM_HEIGHT = 24.0_vp; 52 constexpr float SCROLL_SNAP_VELOCITY_TH = 780; 53 } // namespace 54 UpdateListItemConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)55 void ListLayoutAlgorithm::UpdateListItemConstraint( 56 Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint) 57 { 58 contentConstraint.parentIdealSize = selfIdealSize; 59 contentConstraint.maxSize.SetMainSize(Infinity<float>(), axis); 60 auto crossSize = selfIdealSize.CrossSize(axis); 61 if (crossSize.has_value()) { 62 contentConstraint.maxSize.SetCrossSize(crossSize.value(), axis); 63 contentConstraint.percentReference.SetCrossSize(crossSize.value(), axis); 64 } 65 } 66 ReviseSpace(const RefPtr<ListLayoutProperty> & listLayoutProperty)67 void ListLayoutAlgorithm::ReviseSpace(const RefPtr<ListLayoutProperty>& listLayoutProperty) 68 { 69 if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, contentMainSize_)) { 70 spaceWidth_ = 0.0f; 71 } 72 if (listLayoutProperty->GetDivider().has_value()) { 73 auto divider = listLayoutProperty->GetDivider().value(); 74 std::optional<float> dividerSpace = divider.strokeWidth.ConvertToPx(); 75 if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) { 76 dividerSpace.reset(); 77 } 78 if (dividerSpace.has_value()) { 79 spaceWidth_ = std::max(spaceWidth_, static_cast<float>(Round(dividerSpace.value()))); 80 } 81 } 82 spaceWidth_ += chainInterval_; 83 } 84 Measure(LayoutWrapper * layoutWrapper)85 void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) 86 { 87 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 88 CHECK_NULL_VOID(listLayoutProperty); 89 listLayoutProperty_ = listLayoutProperty; 90 91 axis_ = listLayoutProperty->GetListDirection().value_or(Axis::VERTICAL); 92 // Pre-recycle 93 ScrollableUtils::RecycleItemsOutOfBoundary(axis_, -currentDelta_, GetStartIndex(), GetEndIndex(), layoutWrapper); 94 95 const auto& layoutConstraint = listLayoutProperty->GetLayoutConstraint().value(); 96 97 // calculate idealSize and set FrameSize 98 auto startOffset = listLayoutProperty->GetContentStartOffset().value_or(0.0f); 99 contentStartOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(startOffset), 0.0); 100 auto endOffset = listLayoutProperty->GetContentEndOffset().value_or(0.0f); 101 contentEndOffset_ = std::max(PipelineBase::Vp2PxWithCurrentDensity(endOffset), 0.0); 102 103 // calculate main size. 104 auto contentConstraint = listLayoutProperty->GetContentLayoutConstraint().value(); 105 106 float expandHeight = ScrollableUtils::CheckHeightExpansion(listLayoutProperty, axis_); 107 contentEndOffset_ += expandHeight; 108 // expand contentSize 109 contentConstraint.MinusPadding(std::nullopt, std::nullopt, std::nullopt, -expandHeight); 110 auto&& safeAreaOpts = listLayoutProperty->GetSafeAreaExpandOpts(); 111 expandSafeArea_ = safeAreaOpts && safeAreaOpts->Expansive(); 112 113 auto contentIdealSize = CreateIdealSize( 114 contentConstraint, axis_, listLayoutProperty->GetMeasureType(MeasureType::MATCH_PARENT_CROSS_AXIS)); 115 116 const auto& padding = listLayoutProperty->CreatePaddingAndBorder(); 117 paddingBeforeContent_ = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0); 118 paddingAfterContent_ = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0); 119 contentMainSize_ = 0.0f; 120 totalItemCount_ = layoutWrapper->GetTotalChildCount(); 121 scrollSnapAlign_ = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE); 122 if (childrenSize_) { 123 childrenSize_->ResizeChildrenSize(totalItemCount_); 124 } 125 if (!GetMainAxisSize(contentIdealSize, axis_)) { 126 if (totalItemCount_ == 0) { 127 contentMainSize_ = 0.0f; 128 } else { 129 // use parent max size first. 130 auto parentMaxSize = contentConstraint.maxSize; 131 contentMainSize_ = GetMainAxisSize(parentMaxSize, axis_); 132 mainSizeIsDefined_ = false; 133 } 134 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) { 135 contentMainSize_ = std::max(contentMainSize_, GetMainAxisSize(contentConstraint.minSize, axis_)); 136 } 137 } else { 138 contentMainSize_ = GetMainAxisSize(contentIdealSize.ConvertToSizeT(), axis_); 139 mainSizeIsDefined_ = true; 140 } 141 if (GreatOrEqual(contentStartOffset_ + contentEndOffset_, contentMainSize_) || 142 IsScrollSnapAlignCenter(layoutWrapper)) { 143 contentStartOffset_ = 0; 144 contentEndOffset_ = 0; 145 } 146 147 if (totalItemCount_ > 0) { 148 OnSurfaceChanged(layoutWrapper); 149 150 stickyStyle_ = listLayoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE); 151 childLayoutConstraint_ = listLayoutProperty->CreateChildConstraint(); 152 auto mainPercentRefer = GetMainAxisSize(childLayoutConstraint_.percentReference, axis_); 153 auto space = listLayoutProperty->GetSpace().value_or(Dimension(0)); 154 spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0); 155 ReviseSpace(listLayoutProperty); 156 CheckJumpToIndex(); 157 currentOffset_ = currentDelta_; 158 startMainPos_ = currentOffset_; 159 endMainPos_ = currentOffset_ + contentMainSize_; 160 CalculateLanes(listLayoutProperty, layoutConstraint, contentIdealSize.CrossSize(axis_), axis_); 161 listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START); 162 // calculate child layout constraint. 163 UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_); 164 if (posMap_) { 165 posMap_->UpdatePosMap(layoutWrapper, GetLanes(), spaceWidth_, childrenSize_); 166 } 167 MeasureList(layoutWrapper); 168 } else { 169 itemPosition_.clear(); 170 if (posMap_) { 171 posMap_->ClearPosMap(); 172 } 173 } 174 175 // In the secondary layout scenario, the previous contentMainSize_ is used as the next prevContentMainSize_. 176 prevContentMainSize_ = contentMainSize_; 177 178 auto crossSize = contentIdealSize.CrossSize(axis_); 179 if (crossSize.has_value() && GreaterOrEqualToInfinity(crossSize.value())) { 180 contentIdealSize.SetCrossSize(GetChildMaxCrossSize(layoutWrapper, axis_), axis_); 181 crossMatchChild_ = true; 182 } 183 if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && !mainSizeIsDefined_) { 184 contentMainSize_ = std::max(contentMainSize_, GetMainAxisSize(contentConstraint.minSize, axis_)); 185 } 186 contentIdealSize.SetMainSize(contentMainSize_, axis_); 187 AddPaddingToSize(padding, contentIdealSize); 188 189 auto size = contentIdealSize.ConvertToSizeT(); 190 // Cancel frame size expansion, only expand content size here. 191 // Frame expansion will be determined after Layout. 192 size.MinusHeight(expandHeight); 193 layoutWrapper->GetGeometryNode()->SetFrameSize(size); 194 195 // set list cache info. 196 SetCacheCount(layoutWrapper, listLayoutProperty->GetCachedCountWithDefault()); 197 isLayouted_ = false; 198 } 199 SetCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount)200 void ListLayoutAlgorithm::SetCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount) 201 { 202 layoutWrapper->SetCacheCount(cacheCount); 203 } 204 SetActiveChildRange(LayoutWrapper * layoutWrapper,int32_t cacheStart,int32_t cacheEnd,bool show)205 void ListLayoutAlgorithm::SetActiveChildRange(LayoutWrapper* layoutWrapper, 206 int32_t cacheStart, int32_t cacheEnd, bool show) 207 { 208 if (itemPosition_.empty()) { 209 layoutWrapper->SetActiveChildRange(-1, -1); 210 return; 211 } 212 layoutWrapper->SetActiveChildRange( 213 itemPosition_.begin()->first, itemPosition_.rbegin()->first, cacheStart, cacheEnd, show); 214 } 215 CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper) const216 bool ListLayoutAlgorithm::CheckNeedMeasure(const RefPtr<LayoutWrapper>& layoutWrapper) const 217 { 218 if (layoutWrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(layoutWrapper)) { 219 return true; 220 } 221 return CheckLayoutConstraintChanged(layoutWrapper); 222 } 223 CheckLayoutConstraintChanged(const RefPtr<LayoutWrapper> & layoutWrapper) const224 bool ListLayoutAlgorithm::CheckLayoutConstraintChanged(const RefPtr<LayoutWrapper>& layoutWrapper) const 225 { 226 auto geometryNode = layoutWrapper->GetGeometryNode(); 227 CHECK_NULL_RETURN(geometryNode, true); 228 auto constraint = geometryNode->GetParentLayoutConstraint(); 229 CHECK_NULL_RETURN(constraint, true); 230 bool isGroup = layoutWrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 231 return isGroup ? constraint.value() != GetGroupLayoutConstraint() : constraint.value() != childLayoutConstraint_; 232 } 233 IsListLanesEqual(const RefPtr<LayoutWrapper> & wrapper) const234 bool ListLayoutAlgorithm::IsListLanesEqual(const RefPtr<LayoutWrapper>& wrapper) const 235 { 236 CHECK_NULL_RETURN(listLayoutProperty_, true); 237 auto groupProps = AceType::DynamicCast<ListItemGroupLayoutProperty>(wrapper->GetLayoutProperty()); 238 CHECK_NULL_RETURN(groupProps, true); 239 return groupProps->IsListLanesEqual(listLayoutProperty_->GetLanes(), 240 listLayoutProperty_->GetLaneMinLength(), listLayoutProperty_->GetLaneMaxLength()); 241 } 242 GetChildMaxCrossSize(LayoutWrapper * layoutWrapper,Axis axis) const243 float ListLayoutAlgorithm::GetChildMaxCrossSize(LayoutWrapper* layoutWrapper, Axis axis) const 244 { 245 if (GetItemPosition().empty()) { 246 return 0.0f; 247 } 248 float maxCrossSize = 0.0f; 249 float crossSize = -laneGutter_; 250 float prevPos = GetItemPosition().begin()->second.startPos; 251 for (const auto& pos : GetItemPosition()) { 252 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first, false); 253 if (!wrapper) { 254 continue; 255 } 256 auto getGeometryNode = wrapper->GetGeometryNode(); 257 if (!getGeometryNode) { 258 continue; 259 } 260 if (NearEqual(prevPos, pos.second.startPos)) { 261 crossSize = crossSize + getGeometryNode->GetMarginFrameSize().CrossSize(axis) + laneGutter_; 262 } else { 263 crossSize = getGeometryNode->GetMarginFrameSize().CrossSize(axis); 264 } 265 prevPos = pos.second.startPos; 266 maxCrossSize = std::max(crossSize, maxCrossSize); 267 } 268 return maxCrossSize; 269 } 270 ClearAllItemPosition(LayoutWrapper * layoutWrapper)271 void ListLayoutAlgorithm::ClearAllItemPosition(LayoutWrapper* layoutWrapper) 272 { 273 for (auto& pos : itemPosition_) { 274 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first); 275 if (!wrapper) { 276 continue; 277 } 278 auto node = wrapper->GetHostNode(); 279 if (!node) { 280 continue; 281 } 282 auto listItemGroup = node->GetPattern<ListItemGroupPattern>(); 283 if (!listItemGroup) { 284 continue; 285 } 286 listItemGroup->ClearItemPosition(); 287 listItemGroup->ClearCachedItemPosition(); 288 } 289 itemPosition_.clear(); 290 } 291 GetStartPositionWithChainOffset() const292 float ListLayoutAlgorithm::GetStartPositionWithChainOffset() const 293 { 294 if (itemPosition_.empty()) { 295 return 0.0f; 296 } 297 int32_t startIndex = itemPosition_.begin()->first; 298 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(startIndex) : 0.0f; 299 if (startIndex == 0) { 300 return itemPosition_.begin()->second.startPos + chainOffset; 301 } 302 return itemPosition_.begin()->second.startPos + chainOffset - spaceWidth_; 303 } 304 BeginLayoutForward(float startPos,LayoutWrapper * layoutWrapper)305 void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layoutWrapper) 306 { 307 jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value()); 308 LayoutForward(layoutWrapper, jumpIndex_.value(), startPos); 309 if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) { 310 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition()); 311 if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) { 312 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition()); 313 } 314 } 315 } 316 BeginLayoutBackward(float startPos,LayoutWrapper * layoutWrapper)317 void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper) 318 { 319 jumpIndex_ = GetLanesCeil(layoutWrapper, jumpIndex_.value()); 320 LayoutBackward(layoutWrapper, jumpIndex_.value(), startPos); 321 if (LessOrEqual(GetEndIndex(), totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) { 322 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition()); 323 if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) { 324 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition()); 325 } 326 } 327 } 328 HandleJumpAuto(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t endIndex)329 void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t endIndex) 330 { 331 int32_t jumpIndex = jumpIndex_.has_value() ? jumpIndex_.value() : targetIndex_.value(); 332 jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex); 333 startIndex = GetLanesFloor(layoutWrapper, startIndex); 334 endIndex = GetLanesFloor(layoutWrapper, endIndex); 335 float contentStartOffset = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_; 336 float contentEndOffset = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_; 337 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex); 338 CHECK_NULL_VOID(wrapper); 339 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 340 if (isGroup && jumpIndexInGroup_) { 341 if (scrollAutoType_ == ScrollAutoType::START) { 342 scrollAlign_ = ScrollAlign::START; 343 HandleJumpStart(layoutWrapper); 344 } else if (scrollAutoType_ == ScrollAutoType::END) { 345 scrollAlign_ = ScrollAlign::END; 346 HandleJumpEnd(layoutWrapper); 347 } 348 } else if (jumpIndex <= startIndex) { 349 float mainLen = childrenSize_ ? 350 GetChildHeight(layoutWrapper, jumpIndex) : MeasureAndGetChildHeight(layoutWrapper, jumpIndex, false); 351 if (GreatNotEqual(contentMainSize_ - contentStartOffset - contentEndOffset, mainLen)) { 352 scrollAutoType_ = ScrollAutoType::START; 353 if (jumpIndex_.has_value()) { 354 BeginLayoutForward(contentStartOffset, layoutWrapper); 355 } 356 } else { 357 scrollAutoType_ = ScrollAutoType::END; 358 if (jumpIndex_.has_value()) { 359 BeginLayoutBackward(contentMainSize_ - contentEndOffset, layoutWrapper); 360 } 361 } 362 } else if (jumpIndex >= endIndex) { 363 float mainLen = childrenSize_ ? 364 GetChildHeight(layoutWrapper, jumpIndex) : MeasureAndGetChildHeight(layoutWrapper, jumpIndex, false); 365 if (GreatOrEqual(mainLen, contentMainSize_ - contentStartOffset - contentEndOffset)) { 366 scrollAutoType_ = ScrollAutoType::START; 367 if (jumpIndex_.has_value()) { 368 BeginLayoutForward(contentStartOffset, layoutWrapper); 369 } 370 } else { 371 scrollAutoType_ = ScrollAutoType::END; 372 if (jumpIndex_.has_value()) { 373 BeginLayoutBackward(contentMainSize_ - contentEndOffset, layoutWrapper); 374 } 375 } 376 } 377 } 378 HandleJumpCenter(LayoutWrapper * layoutWrapper)379 void ListLayoutAlgorithm::HandleJumpCenter(LayoutWrapper* layoutWrapper) 380 { 381 int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value()); 382 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index); 383 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 384 if (isGroup && jumpIndexInGroup_.has_value()) { 385 int32_t indexInGroup = jumpIndexInGroup_.value(); 386 auto listLayoutProperty = 387 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 388 SetListItemGroupParam(wrapper, index, 0.0f, true, listLayoutProperty, false); 389 wrapper->Measure(GetGroupLayoutConstraint()); 390 itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup); 391 if (LessNotEqual(GetEndPosition(), endMainPos_)) { 392 LayoutForward(layoutWrapper, index + 1, GetEndPosition()); 393 } 394 } else { 395 float mainLen = childrenSize_ ? 396 GetChildHeight(layoutWrapper, index) : MeasureAndGetChildHeight(layoutWrapper, index); 397 float startPos = (contentMainSize_ - mainLen) / 2.0f; 398 if (LessNotEqual(startPos, endMainPos_)) { 399 LayoutForward(layoutWrapper, index, startPos); 400 } 401 } 402 if (GreatNotEqual(GetStartPosition(), startMainPos_)) { 403 LayoutBackward(layoutWrapper, index - 1, GetStartPosition()); 404 } 405 if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_ - contentEndOffset_)) { 406 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition()); 407 } 408 } 409 HandleJumpStart(LayoutWrapper * layoutWrapper)410 void ListLayoutAlgorithm::HandleJumpStart(LayoutWrapper* layoutWrapper) 411 { 412 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value()); 413 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 414 if (isGroup && jumpIndexInGroup_.has_value()) { 415 int32_t indexInGroup = jumpIndexInGroup_.value(); 416 auto listLayoutProperty = 417 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 418 SetListItemGroupParam(wrapper, jumpIndex_.value(), 0.0f, true, listLayoutProperty, false); 419 wrapper->Measure(GetGroupLayoutConstraint()); 420 itemPosition_[jumpIndex_.value()] = GetListItemGroupPosition(wrapper, indexInGroup); 421 if (LessNotEqual(GetEndPosition(), endMainPos_)) { 422 LayoutForward(layoutWrapper, jumpIndex_.value() + 1, GetEndPosition()); 423 } 424 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) { 425 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition()); 426 } 427 } else { 428 BeginLayoutForward(IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_, layoutWrapper); 429 } 430 } 431 HandleJumpEnd(LayoutWrapper * layoutWrapper)432 void ListLayoutAlgorithm::HandleJumpEnd(LayoutWrapper* layoutWrapper) 433 { 434 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value()); 435 bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 436 if (isGroup && jumpIndexInGroup_.has_value()) { 437 int32_t indexInGroup = jumpIndexInGroup_.value(); 438 auto listLayoutProperty = 439 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 440 SetListItemGroupParam(wrapper, jumpIndex_.value(), contentMainSize_, true, listLayoutProperty, false); 441 wrapper->Measure(GetGroupLayoutConstraint()); 442 itemPosition_[jumpIndex_.value()] = GetListItemGroupPosition(wrapper, indexInGroup); 443 if (GreatNotEqual(GetStartPosition(), startMainPos_)) { 444 LayoutBackward(layoutWrapper, jumpIndex_.value() - 1, GetStartPosition()); 445 } 446 if (GetEndIndex() <= totalItemCount_ -1 && LessNotEqual(GetEndPosition(), endMainPos_)) { 447 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition()); 448 } 449 } else { 450 BeginLayoutBackward(contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_), 451 layoutWrapper); 452 } 453 } 454 CheckNoNeedJumpListItem(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex)455 bool ListLayoutAlgorithm::CheckNoNeedJumpListItem(LayoutWrapper* layoutWrapper, 456 float startPos, float endPos, int32_t startIndex, int32_t endIndex, int32_t jumpIndex) 457 { 458 int32_t tempJumpIndex = jumpIndex; 459 int32_t tempStartIndex = startIndex; 460 int32_t tempEndIndex = endIndex; 461 if (GreatNotEqual(GetLanes(), 1)) { 462 tempJumpIndex = GetLanesFloor(layoutWrapper, jumpIndex); 463 tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex); 464 tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex); 465 } 466 if (tempJumpIndex > tempStartIndex && tempJumpIndex < tempEndIndex) { 467 return true; 468 } 469 if (tempJumpIndex == tempStartIndex && tempJumpIndex == tempEndIndex) { 470 return true; 471 } 472 if ((tempJumpIndex == tempStartIndex) && 473 GreatOrEqual(startPos, IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_)) { 474 return true; 475 } 476 if ((tempJumpIndex == tempEndIndex) && 477 LessOrEqual(endPos, contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_))) { 478 return true; 479 } 480 return false; 481 } 482 CheckNoNeedJumpListItemGroup(LayoutWrapper * layoutWrapper,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)483 bool ListLayoutAlgorithm::CheckNoNeedJumpListItemGroup(LayoutWrapper* layoutWrapper, 484 int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos) 485 { 486 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex); 487 CHECK_NULL_RETURN(wrapper, true); 488 if (wrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) { 489 return true; 490 } 491 int32_t jumpIndexInGroup = 0; 492 if (jumpIndexInGroup_.has_value()) { 493 jumpIndexInGroup = jumpIndexInGroup_.value(); 494 } else { 495 return false; 496 } 497 498 auto layoutAlgorithm = wrapper->GetLayoutAlgorithm(); 499 CHECK_NULL_RETURN(layoutAlgorithm, true); 500 auto groupLayoutAlgorithm = 501 AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithm->GetLayoutAlgorithm()); 502 CHECK_NULL_RETURN(groupLayoutAlgorithm, true); 503 auto groupItemPosition = groupLayoutAlgorithm->GetItemPosition(); 504 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 505 CHECK_NULL_RETURN(listLayoutProperty, false); 506 507 if (jumpIndex >= startIndex && jumpIndex <= endIndex) { 508 auto it = groupItemPosition.find(jumpIndexInGroup); 509 if (it != groupItemPosition.end()) { 510 auto topPos = jumpIndexStartPos + it->second.startPos - 511 (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_); 512 auto bottomPos = jumpIndexStartPos + it->second.endPos + 513 (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_); 514 if (JudgeInOfScreenScrollAutoType(wrapper, listLayoutProperty, topPos, bottomPos)) { 515 return true; 516 } 517 } else if (groupItemPosition.size() > 0) { 518 JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndexInGroup, 519 groupItemPosition.begin()->first, groupItemPosition.rbegin()->first); 520 } else { 521 scrollAutoType_ = ScrollAutoType::NOT_CHANGE; 522 return true; 523 } 524 } else { 525 JudgeOutOfScreenScrollAutoType(wrapper, jumpIndex, listLayoutProperty, jumpIndexInGroup, jumpIndex, 526 startIndex, endIndex); 527 } 528 return false; 529 } 530 JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,const RefPtr<ListLayoutProperty> & layoutProperty,float topPos,float bottomPos)531 bool ListLayoutAlgorithm::JudgeInOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper, 532 const RefPtr<ListLayoutProperty>& layoutProperty, float topPos, float bottomPos) 533 { 534 auto stickyStyle = layoutProperty->GetStickyStyle().value_or(V2::StickyStyle::NONE); 535 536 auto groupNode = layoutWrapper->GetHostNode(); 537 CHECK_NULL_RETURN(groupNode, true); 538 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>(); 539 CHECK_NULL_RETURN(groupPattern, true); 540 541 float headerMainSize = 0.0f; 542 float footerMainSize = 0.0f; 543 if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::HEADER) { 544 headerMainSize = groupPattern->GetHeaderMainSize(); 545 } 546 if (stickyStyle == V2::StickyStyle::BOTH || stickyStyle == V2::StickyStyle::FOOTER) { 547 footerMainSize = groupPattern->GetFooterMainSize(); 548 } 549 550 if (GreatOrEqual(topPos, startMainPos_ + headerMainSize) && 551 LessOrEqual(bottomPos, endMainPos_ - footerMainSize)) { 552 scrollAutoType_ = ScrollAutoType::NOT_CHANGE; 553 return true; 554 } else if (NearEqual(topPos, startMainPos_ + headerMainSize) || 555 NearEqual(bottomPos, endMainPos_ - footerMainSize)) { 556 scrollAutoType_ = ScrollAutoType::NOT_CHANGE; 557 return true; 558 } else if (GreatOrEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) { 559 scrollAutoType_ = ScrollAutoType::END; 560 } else if (LessNotEqual(std::abs(topPos - startMainPos_), std::abs(endMainPos_ - bottomPos))) { 561 scrollAutoType_ = ScrollAutoType::START; 562 } 563 564 return false; 565 } 566 JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,const RefPtr<ListLayoutProperty> & layoutProperty,int32_t indexInGroup,int32_t judgeIndex,int32_t startIndex,int32_t endIndex)567 void ListLayoutAlgorithm::JudgeOutOfScreenScrollAutoType(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index, 568 const RefPtr<ListLayoutProperty>& layoutProperty, int32_t indexInGroup, int32_t judgeIndex, 569 int32_t startIndex, int32_t endIndex) 570 { 571 SetListItemGroupParam(layoutWrapper, index, 0.0f, true, layoutProperty, false); 572 layoutWrapper->Measure(childLayoutConstraint_); 573 auto jumpItemHeight = GetListGroupItemHeight(layoutWrapper, indexInGroup); 574 jumpIndexInGroup_ = indexInGroup; 575 576 if (judgeIndex < startIndex) { 577 if (jumpItemHeight > contentMainSize_) { 578 scrollAutoType_ = ScrollAutoType::END; 579 } else { 580 scrollAutoType_ = ScrollAutoType::START; 581 } 582 } else if (judgeIndex > endIndex) { 583 if (jumpItemHeight > contentMainSize_) { 584 scrollAutoType_ = ScrollAutoType::START; 585 } else { 586 scrollAutoType_ = ScrollAutoType::END; 587 } 588 } 589 } 590 NoNeedJump(LayoutWrapper * layoutWrapper,float startPos,float endPos,int32_t startIndex,int32_t endIndex,int32_t jumpIndex,float jumpIndexStartPos)591 bool ListLayoutAlgorithm::NoNeedJump(LayoutWrapper* layoutWrapper, float startPos, float endPos, 592 int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos) 593 { 594 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex); 595 CHECK_NULL_RETURN(wrapper, true); 596 if (wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG && jumpIndexInGroup_.has_value()) { 597 if (CheckNoNeedJumpListItemGroup(layoutWrapper, startIndex, endIndex, jumpIndex, jumpIndexStartPos)) { 598 return true; 599 } 600 } else { 601 if (CheckNoNeedJumpListItem(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex)) { 602 return true; 603 } 604 } 605 return false; 606 } 607 MeasureAndGetChildHeight(LayoutWrapper * layoutWrapper,int32_t childIndex,bool groupLayoutAll)608 float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex, 609 bool groupLayoutAll) 610 { 611 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex); 612 CHECK_NULL_RETURN(wrapper, 0.0f); 613 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 614 if (isGroup) { 615 auto listLayoutProperty = 616 AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 617 // true: layout forward, 0.0f: layout start position. 618 SetListItemGroupParam(wrapper, childIndex, 0.0f, true, listLayoutProperty, groupLayoutAll); 619 } 620 wrapper->Measure(childLayoutConstraint_); 621 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); 622 return mainLen; 623 } 624 CheckJumpToIndex()625 void ListLayoutAlgorithm::CheckJumpToIndex() 626 { 627 if (jumpIndex_.has_value() || !isNeedCheckOffset_ || childrenSize_) { 628 return; 629 } 630 if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) { 631 return; 632 } 633 for (const auto& pos : itemPosition_) { 634 if (pos.second.isGroup) { 635 return; 636 } 637 } 638 float totalHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_; 639 float averageHeight = totalHeight / itemPosition_.size(); 640 int32_t targetIndex = itemPosition_.begin()->first; 641 currentDelta_ -= itemPosition_.begin()->second.startPos; 642 if (NonNegative(currentDelta_)) { 643 int32_t items = currentDelta_ / averageHeight; 644 targetIndex += items; 645 currentDelta_ -= items * averageHeight; 646 } else { 647 int32_t items = -currentDelta_ / averageHeight; 648 targetIndex -= items; 649 currentDelta_ += items * averageHeight; 650 if (targetIndex <= 0) { 651 currentDelta_ = 0; 652 } 653 } 654 jumpIndex_ = std::clamp(targetIndex, 0, totalItemCount_ - 1); 655 } 656 UpdateSnapCenterContentOffset(LayoutWrapper * layoutWrapper)657 void ListLayoutAlgorithm::UpdateSnapCenterContentOffset(LayoutWrapper* layoutWrapper) 658 { 659 if (IsScrollSnapAlignCenter(layoutWrapper) && !itemPosition_.empty()) { 660 float itemHeight = 0.0f; 661 if (GetStartIndex() == 0) { 662 itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos; 663 contentStartOffset_ = std::max((contentMainSize_ - itemHeight) / 2.0f, 0.0f); 664 } 665 if (GetEndIndex() == totalItemCount_ - 1) { 666 itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos; 667 contentEndOffset_ = std::max((contentMainSize_ - itemHeight) / 2.0f, 0.0f); 668 } 669 } 670 } 671 CheckJumpValid(LayoutWrapper * layoutWrapper)672 bool ListLayoutAlgorithm::CheckJumpValid(LayoutWrapper* layoutWrapper) 673 { 674 if (jumpIndex_.value() == LAST_ITEM) { 675 jumpIndex_ = totalItemCount_ - 1; 676 } else if ((jumpIndex_.value() < 0) || (jumpIndex_.value() >= totalItemCount_)) { 677 return false; 678 } 679 if (jumpIndex_ && jumpIndexInGroup_) { 680 auto groupWrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value()); 681 CHECK_NULL_RETURN(groupWrapper, false); 682 if (groupWrapper->GetHostTag() != V2::LIST_ITEM_GROUP_ETS_TAG) { 683 return false; 684 } 685 auto groupNode = groupWrapper->GetHostNode(); 686 CHECK_NULL_RETURN(groupNode, false); 687 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>(); 688 CHECK_NULL_RETURN(groupPattern, false); 689 690 auto groupItemCount = groupWrapper->GetTotalChildCount() - groupPattern->GetItemStartIndex(); 691 692 if (jumpIndexInGroup_.value() == LAST_ITEM) { 693 jumpIndexInGroup_ = groupItemCount - 1; 694 } else if ((jumpIndexInGroup_.value() < 0) || (jumpIndexInGroup_.value() >= groupItemCount)) { 695 return false; 696 } 697 } 698 return true; 699 } 700 CheckAndMeasureStartItem(LayoutWrapper * layoutWrapper,int32_t startIndex,float & startPos,bool isGroup,bool forwardLayout)701 void ListLayoutAlgorithm::CheckAndMeasureStartItem(LayoutWrapper* layoutWrapper, int32_t startIndex, 702 float& startPos, bool isGroup, bool forwardLayout) 703 { 704 if (!isGroup || IsScrollSnapAlignCenter(layoutWrapper) || 705 (forwardLayout && NonNegative(startPos)) || (!forwardLayout && LessOrEqual(startPos, prevContentMainSize_))) { 706 return; 707 } 708 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(startIndex); 709 CHECK_NULL_VOID(wrapper); 710 int32_t id = wrapper->GetHostNode()->GetId(); 711 isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 712 if (!isGroup) { 713 return; 714 } 715 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 716 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d", startIndex); 717 SetListItemGroupParam(wrapper, startIndex, startPos, forwardLayout, listLayoutProperty, false, true); 718 wrapper->Measure(GetGroupLayoutConstraint()); 719 auto algorithmWrapper = wrapper->GetLayoutAlgorithm(); 720 CHECK_NULL_VOID(algorithmWrapper); 721 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm()); 722 CHECK_NULL_VOID(itemGroup); 723 startPos = itemGroup->GetRefPos(); 724 ListItemInfo itemInfo; 725 if (forwardLayout) { 726 itemInfo = { id, startPos, startPos + childrenSize_->GetChildSize(startIndex), isGroup }; 727 } else { 728 itemInfo = { id, startPos - childrenSize_->GetChildSize(startIndex), startPos, isGroup }; 729 } 730 firstItemInfo_ = std::make_pair(startIndex, itemInfo); 731 } 732 MeasureList(LayoutWrapper * layoutWrapper)733 void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper) 734 { 735 bool startItemIsGroup = false; 736 bool endItemIsGroup = false; 737 int32_t startIndex = 0; 738 int32_t endIndex = 0; 739 int32_t midIndex = 0; 740 float midItemMidPos = contentMainSize_ / 2.0f; 741 float startPos = contentStartOffset_; 742 float endPos = 0.0f; 743 float itemTotalSize = 0.0f; 744 float jumpIndexStartPos = 0.0f; 745 bool needLayoutBackward = false; 746 auto host = layoutWrapper->GetHostNode(); 747 CHECK_NULL_VOID(host); 748 auto pattern = host->GetPattern<ListPattern>(); 749 CHECK_NULL_VOID(pattern); 750 if (!isLayouted_) { 751 itemPosition_ = pattern->GetItemPosition(); 752 } 753 preStartIndex_ = pattern->GetStartIndex(); 754 if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO) { 755 auto it = itemPosition_.find(jumpIndex_.value()); 756 if (it != itemPosition_.end()) { 757 jumpIndexStartPos = it->second.startPos; 758 } 759 } 760 761 if (jumpIndex_) { 762 if (!CheckJumpValid(layoutWrapper)) { 763 jumpIndex_.reset(); 764 jumpIndexInGroup_.reset(); 765 } else { 766 if (jumpIndex_ && scrollAlign_ != ScrollAlign::AUTO) { 767 ClearAllItemPosition(layoutWrapper); 768 } 769 } 770 } 771 if (targetIndex_) { 772 if (targetIndex_.value() == LAST_ITEM) { 773 targetIndex_ = totalItemCount_ - 1; 774 } else if ((targetIndex_.value() < 0) || (targetIndex_.value() >= totalItemCount_)) { 775 targetIndex_.reset(); 776 } 777 targetIndexStaged_ = targetIndex_; 778 } 779 if (!itemPosition_.empty()) { 780 startItemIsGroup = itemPosition_.begin()->second.isGroup; 781 endItemIsGroup = itemPosition_.rbegin()->second.isGroup; 782 startPos = itemPosition_.begin()->second.startPos; 783 endPos = itemPosition_.rbegin()->second.endPos; 784 itemTotalSize = GetEndPosition() - GetStartPosition(); 785 startIndex = std::min(GetStartIndex(), totalItemCount_ - 1); 786 endIndex = std::min(GetEndIndex(), totalItemCount_ - 1); 787 if (GetStartIndex() > totalItemCount_ - 1 && !jumpIndex_.has_value()) { 788 jumpIndex_ = totalItemCount_ - 1; 789 scrollAlign_ = ScrollAlign::END; 790 } 791 UpdateSnapCenterContentOffset(layoutWrapper); 792 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 793 CHECK_NULL_VOID(listLayoutProperty); 794 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE); 795 if (IsScrollSnapAlignCenter(layoutWrapper)) { 796 midIndex = GetMidIndex(layoutWrapper, true); 797 midItemMidPos = (itemPosition_[midIndex].startPos + itemPosition_[midIndex].endPos) / 2.0f - 798 prevContentMainSize_ / 2.0f + contentMainSize_ / 2.0f; 799 midIndex = std::min(midIndex, totalItemCount_ - 1); 800 } else if (scrollSnapAlign == V2::ScrollSnapAlign::START && pattern->GetScrollState() == ScrollState::IDLE) { 801 auto res = GetSnapStartIndexAndPos(); 802 startIndex = res.first; 803 startPos = res.second; 804 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END && pattern->GetScrollState() == ScrollState::IDLE) { 805 auto res = GetSnapEndIndexAndPos(); 806 needLayoutBackward = res.first != -1; 807 endIndex = needLayoutBackward ? res.first : endIndex; 808 endPos = needLayoutBackward ? res.second : endPos; 809 } 810 OffScreenLayoutDirection(layoutWrapper); 811 itemPosition_.clear(); 812 } 813 if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO && 814 NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex_.value(), jumpIndexStartPos)) { 815 jumpIndex_.reset(); 816 jumpIndexInGroup_.reset(); 817 } 818 if (jumpIndex_) { 819 switch (scrollAlign_) { 820 case ScrollAlign::START: 821 case ScrollAlign::NONE: 822 HandleJumpStart(layoutWrapper); 823 break; 824 case ScrollAlign::CENTER: 825 HandleJumpCenter(layoutWrapper); 826 break; 827 case ScrollAlign::END: 828 HandleJumpEnd(layoutWrapper); 829 break; 830 case ScrollAlign::AUTO: 831 HandleJumpAuto(layoutWrapper, startIndex, endIndex); 832 break; 833 } 834 needEstimateOffset_ = true; 835 } else if (targetIndex_.has_value()) { 836 auto layoutDirection = LayoutDirectionForTargetIndex(layoutWrapper, preStartIndex_); 837 if (layoutDirection == LayoutDirection::BACKWARD) { 838 LayoutBackward(layoutWrapper, endIndex, endPos); 839 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) { 840 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition()); 841 } 842 } else { 843 LayoutForward(layoutWrapper, startIndex, startPos); 844 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) { 845 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition()); 846 } 847 } 848 } else { 849 jumpIndexInGroup_.reset(); 850 bool overScrollTop = startIndex == 0 && GreatNotEqual(startPos + GetChainOffset(0), contentStartOffset_); 851 float midItemHeight = 0.0f; 852 if (IsScrollSnapAlignCenter(layoutWrapper)) { 853 midItemHeight = childrenSize_ ? 854 GetChildHeight(layoutWrapper, midIndex) : MeasureAndGetChildHeight(layoutWrapper, midIndex); 855 startIndex = midIndex; 856 endIndex = midIndex; 857 } 858 if ((NonNegative(currentOffset_) || overScrollFeature_ || (canOverScroll_ && 859 LessOrEqual(itemTotalSize, contentMainSize_ - contentStartOffset_ - contentEndOffset_))) && 860 !needLayoutBackward) { 861 startIndex = GetLanesFloor(layoutWrapper, startIndex); 862 if (overScrollTop && !canOverScroll_ && !overScrollFeature_) { 863 startPos = startMainPos_ + contentStartOffset_; 864 } 865 if (IsScrollSnapAlignCenter(layoutWrapper)) { 866 startPos = midItemMidPos - midItemHeight / 2.0f; 867 } 868 if (overScrollFeature_ && !overScrollTop && GreatNotEqual(contentMainSize_, prevContentMainSize_) && 869 GreatNotEqual(itemTotalSize, contentMainSize_)) { 870 startPos += contentMainSize_ - prevContentMainSize_; 871 } 872 if (childrenSize_) { 873 CheckAndMeasureStartItem(layoutWrapper, startIndex, startPos, startItemIsGroup, true); 874 posMap_->OptimizeBeforeMeasure(startIndex, startPos, currentOffset_, contentMainSize_); 875 } 876 LayoutForward(layoutWrapper, startIndex, startPos); 877 if (GetStartIndex() > 0 && GreatNotEqual(GetStartPositionWithChainOffset(), startMainPos_)) { 878 LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition()); 879 } 880 } else { 881 endIndex = GetLanesCeil(layoutWrapper, endIndex); 882 if (needLayoutBackward) { 883 endPos += contentMainSize_ - prevContentMainSize_; 884 } 885 if (IsScrollSnapAlignCenter(layoutWrapper)) { 886 endPos = midItemMidPos + midItemHeight / 2.0f; 887 } 888 if (childrenSize_) { 889 CheckAndMeasureStartItem(layoutWrapper, endIndex, endPos, endItemIsGroup, false); 890 posMap_->OptimizeBeforeMeasure(endIndex, endPos, currentOffset_, contentMainSize_); 891 } 892 LayoutBackward(layoutWrapper, endIndex, endPos); 893 if (GetEndIndex() < (totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) { 894 LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition()); 895 } 896 } 897 } 898 RecycleGroupItem(layoutWrapper); 899 } 900 LayoutDirectionForTargetIndex(LayoutWrapper * layoutWrapper,int startIndex)901 LayoutDirection ListLayoutAlgorithm::LayoutDirectionForTargetIndex(LayoutWrapper* layoutWrapper, int startIndex) 902 { 903 CHECK_NULL_RETURN(targetIndex_, LayoutDirection::NONE); 904 if (startIndex < targetIndex_.value()) { 905 return LayoutDirection::FORWARD; 906 } else if (startIndex > targetIndex_.value()) { 907 return LayoutDirection::BACKWARD; 908 } else if (targetIndexInGroup_.has_value()) { 909 auto groupWrapper = layoutWrapper->GetOrCreateChildByIndex(targetIndex_.value()); 910 CHECK_NULL_RETURN(groupWrapper, LayoutDirection::NONE); 911 auto groupHost = groupWrapper->GetHostNode(); 912 CHECK_NULL_RETURN(groupHost, LayoutDirection::NONE); 913 auto groupPattern = groupHost->GetPattern<ListItemGroupPattern>(); 914 CHECK_NULL_RETURN(groupPattern, LayoutDirection::NONE); 915 auto startIndexInGroup = groupPattern->GetDisplayStartIndexInGroup(); 916 auto endIndexInGroup = groupPattern->GetDisplayEndIndexInGroup(); 917 auto isTargetGroupEmpty = groupPattern->GetItemPosition().empty(); 918 auto targetGroupPosition = itemPosition_[targetIndex_.value()].startPos; 919 if (targetIndexInGroup_.value() < startIndexInGroup || (isTargetGroupEmpty && Negative(targetGroupPosition))) { 920 return LayoutDirection::BACKWARD; 921 } else if (targetIndexInGroup_.value() > endIndexInGroup || 922 (isTargetGroupEmpty && !Negative(targetGroupPosition))) { 923 return LayoutDirection::FORWARD; 924 } 925 } 926 return LayoutDirection::NONE; 927 } 928 RecycleGroupItem(LayoutWrapper * layoutWrapper) const929 void ListLayoutAlgorithm::RecycleGroupItem(LayoutWrapper* layoutWrapper) const 930 { 931 if (scrollSnapAlign_ != V2::ScrollSnapAlign::CENTER || childrenSize_) { 932 return; 933 } 934 auto startChild = itemPosition_.begin(); 935 auto endChild = itemPosition_.rbegin(); 936 if (startChild != itemPosition_.end() && startChild->second.isGroup) { 937 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(startChild->first) : 0.0f; 938 CheckListItemGroupRecycle(layoutWrapper, startChild->first, startChild->second.startPos + chainOffset, true); 939 } 940 if (endChild != itemPosition_.rend() && endChild->second.isGroup) { 941 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(endChild->first) : 0.0f; 942 CheckListItemGroupRecycle(layoutWrapper, endChild->first, endChild->second.endPos + chainOffset, false); 943 } 944 } 945 AdjustStartPosition(const RefPtr<LayoutWrapper> & layoutWrapper,float & startPos)946 void ListLayoutAlgorithm::AdjustStartPosition(const RefPtr<LayoutWrapper>& layoutWrapper, float& startPos) 947 { 948 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true); 949 CHECK_NULL_VOID(layoutAlgorithmWrapper); 950 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm()); 951 CHECK_NULL_VOID(itemGroup); 952 startPos += itemGroup->GetAdjustReferenceDelta(); 953 } 954 LayoutALineForward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float startPos,float & endPos)955 int32_t ListLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper, 956 int32_t& currentIndex, float startPos, float& endPos) 957 { 958 if (currentIndex + 1 >= totalItemCount_) { 959 return 0; 960 } 961 if (!firstItemInfo_ || firstItemInfo_.value().first != currentIndex + 1) { 962 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex + 1); 963 CHECK_NULL_RETURN(wrapper, 0); 964 int32_t id = wrapper->GetHostNode()->GetId(); 965 ++currentIndex; 966 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 967 if (isGroup) { 968 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 969 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d, %f", currentIndex, startPos); 970 SetListItemGroupParam(wrapper, currentIndex, startPos, true, listLayoutProperty, false); 971 wrapper->Measure(childLayoutConstraint_); 972 if (LessOrEqual(startPos, 0.0f)) { 973 AdjustStartPosition(wrapper, startPos); 974 } 975 } else if (expandSafeArea_ || CheckNeedMeasure(wrapper)) { 976 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d, %f", currentIndex, startPos); 977 wrapper->Measure(childLayoutConstraint_); 978 } 979 float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex) : 980 GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); 981 endPos = startPos + mainLen; 982 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup }; 983 } else { 984 ++currentIndex; 985 itemPosition_[currentIndex] = firstItemInfo_.value().second; 986 endPos = itemPosition_[currentIndex].endPos; 987 } 988 if (firstItemInfo_) { 989 firstItemInfo_.reset(); 990 } 991 OnItemPositionAddOrUpdate(layoutWrapper, currentIndex); 992 return 1; 993 } 994 LayoutALineBackward(LayoutWrapper * layoutWrapper,int32_t & currentIndex,float endPos,float & startPos)995 int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper, 996 int32_t& currentIndex, float endPos, float& startPos) 997 { 998 if (currentIndex - 1 < 0) { 999 return 0; 1000 } 1001 if (!firstItemInfo_ || firstItemInfo_.value().first != currentIndex - 1) { 1002 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex - 1); 1003 CHECK_NULL_RETURN(wrapper, 0); 1004 int32_t id = wrapper->GetHostNode()->GetId(); 1005 --currentIndex; 1006 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 1007 if (isGroup) { 1008 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 1009 SetListItemGroupParam(wrapper, currentIndex, endPos, false, listLayoutProperty, false); 1010 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItemGroup:%d, %f", currentIndex, endPos); 1011 wrapper->Measure(childLayoutConstraint_); 1012 } else if (expandSafeArea_ || CheckNeedMeasure(wrapper)) { 1013 ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d, %f", currentIndex, endPos); 1014 wrapper->Measure(childLayoutConstraint_); 1015 } 1016 float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex) : 1017 GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); 1018 startPos = endPos - mainLen; 1019 itemPosition_[currentIndex] = { id, startPos, endPos, isGroup }; 1020 } else { 1021 --currentIndex; 1022 itemPosition_[currentIndex] = firstItemInfo_.value().second; 1023 startPos = itemPosition_[currentIndex].startPos; 1024 } 1025 if (firstItemInfo_) { 1026 firstItemInfo_.reset(); 1027 } 1028 OnItemPositionAddOrUpdate(layoutWrapper, currentIndex); 1029 return 1; 1030 } 1031 LayoutForward(LayoutWrapper * layoutWrapper,int32_t startIndex,float startPos)1032 void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos) 1033 { 1034 float currentEndPos = startPos; 1035 float currentStartPos = 0.0f; 1036 float endMainPos = (overScrollFeature_ && startIndex == 0) ? 1037 std::max(startPos + contentMainSize_ - contentStartOffset_, endMainPos_) : endMainPos_; 1038 layoutEndMainPos_ = endMainPos; 1039 if (forwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) { 1040 endMainPos = Infinity<float>(); 1041 } 1042 1043 auto currentIndex = startIndex - 1; 1044 auto chainOffset = 0.0f; 1045 do { 1046 currentStartPos = currentEndPos; 1047 int32_t count = LayoutALineForward(layoutWrapper, currentIndex, currentStartPos, currentEndPos); 1048 if (count == 0) { 1049 break; 1050 } 1051 if (currentIndex >= 0 && currentIndex < (totalItemCount_ - 1)) { 1052 currentEndPos += spaceWidth_; 1053 } 1054 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f; 1055 // reach the valid target index 1056 if (forwardFeature_ && targetIndex_ && currentIndex >= targetIndex_.value()) { 1057 endMainPos = layoutEndMainPos_.value_or(endMainPos_); 1058 forwardFeature_ = false; 1059 } 1060 } while (LessOrEqual(currentEndPos + chainOffset, endMainPos)); 1061 currentEndPos += chainOffset; 1062 1063 while (itemPosition_.size() > 1 && !targetIndex_) { 1064 auto pos = itemPosition_.rbegin(); 1065 float chainDelta = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f; 1066 if ((GreatNotEqual(pos->second.endPos + chainDelta, endMainPos) && 1067 GreatOrEqual(pos->second.startPos + chainDelta, endMainPos))) { 1068 recycledItemPosition_.emplace(pos->first, pos->second); 1069 itemPosition_.erase(pos->first); 1070 } else { 1071 break; 1072 } 1073 } 1074 // adjust offset. 1075 UpdateSnapCenterContentOffset(layoutWrapper); 1076 if (LessNotEqual(currentEndPos, endMainPos_ - contentEndOffset_) && !itemPosition_.empty()) { 1077 endMainPos_ = currentEndPos + contentEndOffset_; 1078 startMainPos_ = endMainPos_ - contentMainSize_; 1079 ReMeasureListItemGroup(layoutWrapper, true); 1080 auto firstItemTop = itemPosition_.begin()->second.startPos; 1081 auto itemTotalSize = currentEndPos - firstItemTop + contentEndOffset_ + contentStartOffset_; 1082 if (LessOrEqual(itemTotalSize, contentMainSize_) && (itemPosition_.begin()->first == 0)) { 1083 // all items size is less than list. 1084 if (!canOverScroll_) { 1085 currentOffset_ = firstItemTop - contentStartOffset_; 1086 startMainPos_ = currentOffset_; 1087 endMainPos_ = startMainPos_ + contentMainSize_; 1088 } 1089 if (!mainSizeIsDefined_) { 1090 // adapt child size. 1091 contentMainSize_ = itemTotalSize; 1092 } 1093 } else { 1094 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary 1095 if (!canOverScroll_ || jumpIndex_.has_value()) { 1096 currentOffset_ = currentEndPos + contentEndOffset_ - contentMainSize_; 1097 } 1098 } 1099 } 1100 if (overScrollFeature_ && canOverScroll_) { 1101 return; 1102 } 1103 // Mark inactive in wrapper. 1104 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end();) { 1105 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f; 1106 // Don't recycle When the head item is Visibility.None. 1107 if (GreatNotEqual(pos->second.endPos + chainOffset, startMainPos_) || 1108 GreatOrEqual(pos->second.startPos + chainOffset, startMainPos_)) { 1109 if (pos->second.isGroup) { 1110 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.startPos + chainOffset, true); 1111 } 1112 break; 1113 } 1114 recycledItemPosition_.emplace(pos->first, pos->second); 1115 pos = itemPosition_.erase(pos); 1116 } 1117 } 1118 LayoutBackward(LayoutWrapper * layoutWrapper,int32_t endIndex,float endPos)1119 void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos) 1120 { 1121 float currentStartPos = endPos; 1122 float currentEndPos = 0.0f; 1123 float startMainPos = (overScrollFeature_ && endIndex == totalItemCount_ - 1) ? 1124 std::min(endPos - contentMainSize_ + contentEndOffset_, startMainPos_) : startMainPos_; 1125 layoutStartMainPos_ = startMainPos; 1126 if (backwardFeature_ && targetIndex_ && NonNegative(targetIndex_.value())) { 1127 startMainPos = -Infinity<float>(); 1128 } 1129 auto currentIndex = endIndex + 1; 1130 auto chainOffset = 0.0f; 1131 do { 1132 currentEndPos = currentStartPos; 1133 int32_t count = LayoutALineBackward(layoutWrapper, currentIndex, currentEndPos, currentStartPos); 1134 if (count == 0) { 1135 break; 1136 } 1137 if (currentIndex > 0) { 1138 currentStartPos = currentStartPos - spaceWidth_; 1139 } 1140 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(currentIndex) : 0.0f; 1141 // reach the valid target index 1142 if (backwardFeature_ && targetIndex_ && LessOrEqual(currentIndex, targetIndex_.value())) { 1143 startMainPos = layoutStartMainPos_.value_or(startMainPos_); 1144 backwardFeature_ = false; 1145 } 1146 } while (GreatNotEqual(currentStartPos + chainOffset, startMainPos)); 1147 1148 currentStartPos += chainOffset; 1149 // adjust offset. If edgeEffect is SPRING, jump adjust to allow list scroll through boundary 1150 UpdateSnapCenterContentOffset(layoutWrapper); 1151 if (GreatNotEqual(currentStartPos, startMainPos_ + contentStartOffset_) && !itemPosition_.empty()) { 1152 auto itemTotalSize = GetEndPosition() - currentStartPos + contentEndOffset_ + contentStartOffset_; 1153 bool overBottom = (GetEndIndex() == totalItemCount_ - 1) && (LessNotEqual(itemTotalSize, contentMainSize_)); 1154 if (overBottom && !mainSizeIsDefined_ && GreatNotEqual(contentMainSize_, itemTotalSize)) { 1155 if (overScrollFeature_ && !NearZero(prevContentMainSize_)) { 1156 currentOffset_ += contentMainSize_ - prevContentMainSize_; 1157 } 1158 contentMainSize_ = itemTotalSize; 1159 } 1160 if (!canOverScroll_ || jumpIndex_.has_value()) { 1161 currentOffset_ = currentStartPos - contentStartOffset_; 1162 } 1163 endMainPos_ = currentStartPos - contentStartOffset_ + contentMainSize_; 1164 startMainPos_ = currentStartPos - contentStartOffset_; 1165 ReMeasureListItemGroup(layoutWrapper, false); 1166 } 1167 1168 if (overScrollFeature_) { 1169 return; 1170 } 1171 1172 // Mark inactive in wrapper. 1173 std::list<int32_t> removeIndexes; 1174 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) { 1175 chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f; 1176 // Don't recycle When the tail item is Visibility.None. 1177 if (LessNotEqual(pos->second.startPos + chainOffset, endMainPos_) || 1178 LessOrEqual(pos->second.endPos + chainOffset, endMainPos_)) { 1179 if (pos->second.isGroup) { 1180 CheckListItemGroupRecycle(layoutWrapper, pos->first, pos->second.endPos + chainOffset, false); 1181 } 1182 break; 1183 } 1184 recycledItemPosition_.emplace(pos->first, pos->second); 1185 removeIndexes.emplace_back(pos->first); 1186 } 1187 for (const auto& index : removeIndexes) { 1188 itemPosition_.erase(index); 1189 } 1190 } 1191 ReMeasureListItemGroup(LayoutWrapper * layoutWrapper,bool forwardLayout)1192 void ListLayoutAlgorithm::ReMeasureListItemGroup(LayoutWrapper* layoutWrapper, bool forwardLayout) 1193 { 1194 if (forwardFeature_ || backwardFeature_) { 1195 return; 1196 } 1197 if (forwardLayout) { 1198 if (itemPosition_.begin()->second.isGroup) { 1199 AdjustPostionForListItemGroup(layoutWrapper, axis_, GetStartIndex(), forwardLayout); 1200 } 1201 return; 1202 } 1203 for (auto pos = itemPosition_.begin(); pos != itemPosition_.end(); pos++) { 1204 float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(pos->first) : 0.0f; 1205 if (GreatOrEqual(pos->second.startPos + chainOffset, endMainPos_)) { 1206 break; 1207 } else if (!pos->second.isGroup) { 1208 continue; 1209 } 1210 AdjustPostionForListItemGroup(layoutWrapper, axis_, pos->first, forwardLayout); 1211 } 1212 } 1213 FixPredictSnapOffset(const RefPtr<ListLayoutProperty> & listLayoutProperty)1214 void ListLayoutAlgorithm::FixPredictSnapOffset(const RefPtr<ListLayoutProperty>& listLayoutProperty) 1215 { 1216 if (!predictSnapOffset_.has_value() || itemPosition_.empty()) { 1217 return; 1218 } 1219 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE); 1220 if ((scrollSnapAlign != V2::ScrollSnapAlign::START) && (scrollSnapAlign != V2::ScrollSnapAlign::CENTER) && 1221 (scrollSnapAlign != V2::ScrollSnapAlign::END)) { 1222 predictSnapOffset_.reset(); 1223 predictSnapEndPos_.reset(); 1224 return; 1225 } 1226 1227 auto predictEndPos = totalOffset_ - predictSnapOffset_.value(); 1228 int32_t endIndex = FindPredictSnapEndIndexInItemPositions(predictEndPos, scrollSnapAlign); 1229 if (GetStartIndex() <= endIndex && endIndex <= GetEndIndex()) { 1230 predictEndPos = CalculatePredictSnapEndPositionByIndex(endIndex, scrollSnapAlign); 1231 predictSnapOffset_ = totalOffset_ - predictEndPos + currentOffset_; 1232 predictSnapEndPos_.reset(); 1233 } else { 1234 if (IsUniformHeightProbably()) { 1235 if (scrollSnapAlign == V2::ScrollSnapAlign::START) { 1236 FixPredictSnapOffsetAlignStart(); 1237 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) { 1238 FixPredictSnapOffsetAlignCenter(); 1239 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) { 1240 FixPredictSnapOffsetAlignEnd(); 1241 } 1242 } else { 1243 predictSnapEndPos_ = predictEndPos; 1244 } 1245 } 1246 1247 return; 1248 } 1249 IsScrollSnapAlignCenter(LayoutWrapper * layoutWrapper)1250 bool ListLayoutAlgorithm::IsScrollSnapAlignCenter(LayoutWrapper* layoutWrapper) 1251 { 1252 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 1253 CHECK_NULL_RETURN(listLayoutProperty, false); 1254 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE); 1255 if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) { 1256 return true; 1257 } 1258 1259 return false; 1260 } 1261 FixPredictSnapOffsetAlignStart()1262 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignStart() 1263 { 1264 if (itemPosition_.empty()) { 1265 return; 1266 } 1267 auto predictEndPos = totalOffset_ - predictSnapOffset_.value(); 1268 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_; 1269 float startPos = contentStartOffset_; 1270 float endPos = contentMainSize_ - contentEndOffset_; 1271 float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos; 1272 1273 if (LessNotEqual(predictEndPos, -startPos)) { 1274 if (isSpringEffect_) { 1275 return; 1276 } 1277 predictEndPos = -startPos; 1278 } else if (GreatNotEqual(predictEndPos, maxPos)) { 1279 if (isSpringEffect_) { 1280 return; 1281 } 1282 predictEndPos = maxPos; 1283 } else { 1284 int32_t index; 1285 for (index = 0; index <= GetMaxListItemIndex(); index++) { 1286 if (std::abs(predictEndPos - index * itemHeight) < itemHeight / 2.0f) { 1287 break; 1288 } 1289 } 1290 predictEndPos = index * itemHeight - startPos; 1291 if (LessNotEqual(predictEndPos, -startPos)) { 1292 predictEndPos = -startPos; 1293 } else if (GreatNotEqual(predictEndPos, maxPos)) { 1294 predictEndPos = maxPos; 1295 } 1296 } 1297 1298 predictSnapOffset_ = totalOffset_ - predictEndPos; 1299 predictSnapEndPos_ = predictEndPos; 1300 } 1301 FixPredictSnapOffsetAlignCenter()1302 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignCenter() 1303 { 1304 if (itemPosition_.empty()) { 1305 return; 1306 } 1307 auto predictEndPos = totalOffset_ - predictSnapOffset_.value(); 1308 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_; 1309 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f)) { 1310 if (isSpringEffect_) { 1311 return; 1312 } 1313 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f; 1314 } else if (GreatNotEqual( 1315 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) { 1316 if (isSpringEffect_) { 1317 return; 1318 } 1319 predictEndPos = itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f; 1320 } else { 1321 int32_t index; 1322 for (index = 0; index <= GetMaxListItemIndex(); index++) { 1323 if (std::abs(predictEndPos + contentMainSize_ / 2.0f - index * itemHeight - itemHeight / 2.0f) < 1324 itemHeight / 2.0f) { 1325 break; 1326 } 1327 } 1328 predictEndPos = index * itemHeight + itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f; 1329 if (LessNotEqual(predictEndPos, itemHeight / 2.0f - contentMainSize_ / 2.0f)) { 1330 predictEndPos = itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f; 1331 } else if (GreatNotEqual( 1332 predictEndPos + contentMainSize_ / 2.0f, itemHeight * totalItemCount_ - itemHeight / 2.0f)) { 1333 predictEndPos = 1334 itemHeight * totalItemCount_ - itemHeight / 2.0f - contentMainSize_ / 2.0f - spaceWidth_ / 2.0f; 1335 } 1336 } 1337 1338 predictSnapOffset_ = totalOffset_ - predictEndPos; 1339 predictSnapEndPos_ = predictEndPos; 1340 } 1341 FixPredictSnapOffsetAlignEnd()1342 void ListLayoutAlgorithm::FixPredictSnapOffsetAlignEnd() 1343 { 1344 if (itemPosition_.empty()) { 1345 return; 1346 } 1347 auto predictEndPos = totalOffset_ - predictSnapOffset_.value(); 1348 auto itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos + spaceWidth_; 1349 float startPos = contentStartOffset_; 1350 float endPos = contentMainSize_ - contentEndOffset_; 1351 float maxPos = itemHeight * totalItemCount_ - spaceWidth_ - endPos; 1352 1353 if (LessNotEqual(predictEndPos, -startPos)) { 1354 if (isSpringEffect_) { 1355 return; 1356 } 1357 predictEndPos = -startPos; 1358 } else if (GreatNotEqual(predictEndPos, maxPos)) { 1359 if (isSpringEffect_) { 1360 return; 1361 } 1362 predictEndPos = maxPos; 1363 } else { 1364 int32_t index; 1365 for (index = 0; index <= GetMaxListItemIndex(); index++) { 1366 if (std::abs(predictEndPos + endPos - index * itemHeight) < itemHeight / 2.0f) { 1367 break; 1368 } 1369 } 1370 predictEndPos = index * itemHeight - endPos - spaceWidth_; 1371 if (LessNotEqual(predictEndPos, -startPos)) { 1372 predictEndPos = -startPos; 1373 } else if (GreatNotEqual(predictEndPos, maxPos)) { 1374 predictEndPos = maxPos; 1375 } 1376 } 1377 1378 predictSnapOffset_ = totalOffset_ - predictEndPos; 1379 predictSnapEndPos_ = predictEndPos; 1380 } 1381 LayoutItem(RefPtr<LayoutWrapper> & wrapper,int32_t index,const ListItemInfo & pos,int32_t & startIndex,float crossSize)1382 void ListLayoutAlgorithm::LayoutItem(RefPtr<LayoutWrapper>& wrapper, int32_t index, const ListItemInfo& pos, 1383 int32_t& startIndex, float crossSize) 1384 { 1385 CHECK_NULL_VOID(wrapper); 1386 auto offset = paddingOffset_; 1387 float childCrossSize = GetCrossAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); 1388 float crossOffset = 0.0f; 1389 if (GetLanes() > 1) { 1390 int32_t laneIndex = 0; 1391 if (pos.isGroup) { 1392 startIndex = index + 1; 1393 } else { 1394 laneIndex = (index - startIndex) % GetLanes(); 1395 } 1396 1397 float laneGutter = GetLaneGutter(); 1398 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize * GetLanes()); 1399 crossOffset += ((crossSize + laneGutter) / GetLanes()) * laneIndex; 1400 } else { 1401 crossOffset = CalculateLaneCrossOffset(crossSize, childCrossSize); 1402 } 1403 auto chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(index) : 0.0f; 1404 if (isReverse_) { 1405 if (axis_ == Axis::VERTICAL) { 1406 auto size = wrapper->GetGeometryNode()->GetMarginFrameSize(); 1407 offset = offset + OffsetF(crossSize - crossOffset - size.Width(), pos.startPos + chainOffset); 1408 } else { 1409 offset = offset + OffsetF(contentMainSize_ - pos.endPos - chainOffset, crossOffset); 1410 } 1411 } else { 1412 if (axis_ == Axis::VERTICAL) { 1413 offset = offset + OffsetF(crossOffset, pos.startPos + chainOffset); 1414 } else { 1415 offset = offset + OffsetF(pos.startPos + chainOffset, crossOffset); 1416 } 1417 } 1418 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset); 1419 SetListItemIndex(wrapper, index); 1420 } 1421 ProcessCacheCount(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)1422 void ListLayoutAlgorithm::ProcessCacheCount(LayoutWrapper* layoutWrapper, int32_t cacheCount, bool show) 1423 { 1424 if (!itemPosition_.empty() && cacheCount > 0) { 1425 auto items = LayoutCachedItemV2(layoutWrapper, cacheCount, show); 1426 auto host = layoutWrapper->GetHostNode(); 1427 CHECK_NULL_VOID(host); 1428 if (!items.empty()) { 1429 ListMainSizeValues value = { startMainPos_, endMainPos_, jumpIndexInGroup_, prevContentMainSize_, 1430 scrollAlign_, layoutStartMainPos_, layoutEndMainPos_ }; 1431 PostIdleTaskV2(host, { items, childLayoutConstraint_, GetGroupLayoutConstraint() }, value, show); 1432 } else { 1433 auto pattern = host->GetPattern<ListPattern>(); 1434 CHECK_NULL_VOID(pattern); 1435 pattern->SetPredictLayoutParamV2(std::nullopt); 1436 } 1437 } else { 1438 ResetLayoutItem(layoutWrapper); 1439 SetActiveChildRange(layoutWrapper, cacheCount, cacheCount, show); 1440 } 1441 } 1442 GetListItemGroupLayoutInfo(const RefPtr<LayoutWrapper> & wrapper) const1443 std::optional<ListItemGroupLayoutInfo> ListLayoutAlgorithm::GetListItemGroupLayoutInfo( 1444 const RefPtr<LayoutWrapper>& wrapper) const 1445 { 1446 CHECK_NULL_RETURN(wrapper, std::nullopt); 1447 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true); 1448 CHECK_NULL_RETURN(layoutAlgorithmWrapper, std::nullopt); 1449 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm()); 1450 CHECK_NULL_RETURN(itemGroup, std::nullopt); 1451 return itemGroup->GetLayoutInfo(); 1452 } 1453 GetListItemGroupItemCount(const RefPtr<LayoutWrapper> & wrapper) const1454 int32_t ListLayoutAlgorithm::GetListItemGroupItemCount(const RefPtr<LayoutWrapper>& wrapper) const 1455 { 1456 CHECK_NULL_RETURN(wrapper, 0); 1457 auto layoutAlgorithmWrapper = wrapper->GetLayoutAlgorithm(true); 1458 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0); 1459 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm()); 1460 CHECK_NULL_RETURN(itemGroup, 0); 1461 int32_t itemCount = itemGroup->GetListItemCount(); 1462 return itemCount == 0 ? 1 : itemCount; 1463 } 1464 ResetLayoutItem(LayoutWrapper * layoutWrapper)1465 void ListLayoutAlgorithm::ResetLayoutItem(LayoutWrapper* layoutWrapper) 1466 { 1467 for (auto& pos : recycledItemPosition_) { 1468 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first); 1469 pos.second.startPos -= currentOffset_; 1470 pos.second.endPos -= currentOffset_; 1471 if (pos.second.isGroup) { 1472 pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper); 1473 if (wrapper && wrapper->GetHostNode() && wrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) { 1474 auto groupPattern = wrapper->GetHostNode()->GetPattern<ListItemGroupPattern>(); 1475 groupPattern->ClearItemPosition(); 1476 } 1477 } else { 1478 pos.second.groupInfo.reset(); 1479 } 1480 auto wrapperFrameNode = AceType::DynamicCast<FrameNode>(wrapper); 1481 if (wrapperFrameNode) { 1482 wrapperFrameNode->ClearSubtreeLayoutAlgorithm(); 1483 } 1484 } 1485 } 1486 Layout(LayoutWrapper * layoutWrapper)1487 void ListLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) 1488 { 1489 auto listProps = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 1490 CHECK_NULL_VOID(listProps); 1491 auto axis_ = listProps->GetListDirection().value_or(Axis::VERTICAL); 1492 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize(); 1493 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder(); 1494 MinusPaddingToSize(padding, size); 1495 paddingOffset_ = padding.Offset(); 1496 float crossSize = GetCrossAxisSize(size, axis_); 1497 totalItemCount_ = layoutWrapper->GetTotalChildCount(); 1498 listItemAlign_ = listProps->GetListItemAlign().value_or(V2::ListItemAlign::START); 1499 int32_t startIndex = GetStartIndex(); 1500 isReverse_ = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL; 1501 1502 totalOffset_ += currentOffset_; 1503 FixPredictSnapOffset(listProps); 1504 // layout items. 1505 int32_t itemCount = 0; 1506 for (auto& pos : itemPosition_) { 1507 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(pos.first); 1508 if (!wrapper) { 1509 LOGI("wrapper is out of boundary"); 1510 continue; 1511 } 1512 pos.second.startPos -= currentOffset_; 1513 pos.second.endPos -= currentOffset_; 1514 if (pos.second.isGroup) { 1515 pos.second.groupInfo = GetListItemGroupLayoutInfo(wrapper); 1516 itemCount += GetListItemGroupItemCount(wrapper); 1517 } else { 1518 pos.second.groupInfo.reset(); 1519 itemCount++; 1520 } 1521 LayoutItem(wrapper, pos.first, pos.second, startIndex, crossSize); 1522 if (expandSafeArea_ || wrapper->CheckNeedForceMeasureAndLayout()) { 1523 wrapper->Layout(); 1524 } else { 1525 SyncGeometry(wrapper); 1526 } 1527 auto frameNode = AceType::DynamicCast<FrameNode>(wrapper); 1528 if (frameNode) { 1529 frameNode->MarkAndCheckNewOpIncNode(); 1530 } 1531 } 1532 auto cacheCount = listProps->GetCachedCountWithDefault(); 1533 if (!listProps->HasCachedCount()) { 1534 int32_t newCacheCount = UpdateDefaultCachedCount(cacheCount, itemCount); 1535 listProps->SetDefaultCachedCount(newCacheCount); 1536 } 1537 ProcessCacheCount(layoutWrapper, cacheCount, listProps->GetShowCachedItemsValue(false)); 1538 isLayouted_ = true; 1539 UpdateOverlay(layoutWrapper); 1540 } 1541 UpdateOverlay(LayoutWrapper * layoutWrapper)1542 void ListLayoutAlgorithm::UpdateOverlay(LayoutWrapper* layoutWrapper) 1543 { 1544 auto frameNode = layoutWrapper->GetHostNode(); 1545 CHECK_NULL_VOID(frameNode); 1546 auto paintProperty = frameNode->GetPaintProperty<ScrollablePaintProperty>(); 1547 CHECK_NULL_VOID(paintProperty); 1548 if (!paintProperty->GetFadingEdge().value_or(false)) { 1549 return; 1550 } 1551 auto overlayNode = frameNode->GetOverlayNode(); 1552 CHECK_NULL_VOID(overlayNode); 1553 auto geometryNode = frameNode->GetGeometryNode(); 1554 CHECK_NULL_VOID(geometryNode); 1555 auto listFrameSize = geometryNode->GetFrameSize(); 1556 auto overlayGeometryNode = overlayNode->GetGeometryNode(); 1557 CHECK_NULL_VOID(overlayGeometryNode); 1558 overlayGeometryNode->SetFrameSize(listFrameSize); 1559 } 1560 CalculateLaneCrossOffset(float crossSize,float childCrossSize)1561 float ListLayoutAlgorithm::CalculateLaneCrossOffset(float crossSize, float childCrossSize) 1562 { 1563 float delta = crossSize - GetLaneGutter() - childCrossSize; 1564 if (LessOrEqual(delta, 0)) { 1565 return 0.0f; 1566 } 1567 switch (listItemAlign_) { 1568 case OHOS::Ace::V2::ListItemAlign::START: 1569 return 0.0f; 1570 case OHOS::Ace::V2::ListItemAlign::CENTER: 1571 return delta / 2.0f; 1572 case OHOS::Ace::V2::ListItemAlign::END: 1573 return delta; 1574 default: 1575 LOGW("Invalid ListItemAlign: %{public}d", listItemAlign_); 1576 return 0.0f; 1577 } 1578 } 1579 OnSurfaceChanged(LayoutWrapper * layoutWrapper)1580 void ListLayoutAlgorithm::OnSurfaceChanged(LayoutWrapper* layoutWrapper) 1581 { 1582 if (GreatOrEqual(contentMainSize_, prevContentMainSize_)) { 1583 return; 1584 } 1585 auto host = layoutWrapper->GetHostNode(); 1586 CHECK_NULL_VOID(host); 1587 auto focusHub = host->GetFocusHub(); 1588 CHECK_NULL_VOID(focusHub); 1589 // textField not in List 1590 if (!focusHub->IsCurrentFocus()) { 1591 return; 1592 } 1593 auto context = PipelineContext::GetCurrentContext(); 1594 CHECK_NULL_VOID(context); 1595 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager()); 1596 CHECK_NULL_VOID(textFieldManager); 1597 // only when textField is onFocus 1598 auto textField = textFieldManager->GetOnFocusTextField().Upgrade(); 1599 CHECK_NULL_VOID(textField); 1600 auto textFieldHost = textField->GetHost(); 1601 CHECK_NULL_VOID(textFieldHost); 1602 auto textBase = DynamicCast<TextBase>(textField); 1603 CHECK_NULL_VOID(textBase); 1604 auto caretPos = textFieldHost->GetTransformRelativeOffset().GetY() + textBase->GetCaretRect().Bottom(); 1605 auto globalOffset = host->GetTransformRelativeOffset(); 1606 auto offset = contentMainSize_ + globalOffset.GetY() - caretPos - RESERVE_BOTTOM_HEIGHT.ConvertToPx(); 1607 if (LessOrEqual(offset, 0.0)) { 1608 // negative offset to scroll down 1609 currentDelta_ -= static_cast<float>(offset); 1610 } 1611 } 1612 SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm> & itemGroup,bool forwardLayout,int32_t index)1613 void ListLayoutAlgorithm::SetListItemGroupJumpIndex(const RefPtr<ListItemGroupLayoutAlgorithm>& itemGroup, 1614 bool forwardLayout, int32_t index) 1615 { 1616 if (jumpIndex_.has_value() && jumpIndex_.value() == index) { 1617 if (!jumpIndexInGroup_.has_value()) { 1618 if (forwardLayout && (scrollAlign_ == ScrollAlign::START || 1619 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::START))) { 1620 jumpIndexInGroup_ = 0; 1621 } else if (!forwardLayout && (scrollAlign_ == ScrollAlign::END || 1622 (scrollAlign_ == ScrollAlign::AUTO && scrollAutoType_ == ScrollAutoType::END))) { 1623 jumpIndexInGroup_ = LAST_ITEM; 1624 } 1625 } 1626 1627 if (jumpIndexInGroup_.has_value()) { 1628 itemGroup->SetJumpIndex(jumpIndexInGroup_.value()); 1629 itemGroup->SetScrollAlign(scrollAlign_); 1630 jumpIndexInGroup_.reset(); 1631 } 1632 } 1633 } 1634 SetListItemGroupParam(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index,float referencePos,bool forwardLayout,const RefPtr<ListLayoutProperty> & layoutProperty,bool groupNeedAllLayout,bool needAdjustRefPos)1635 void ListLayoutAlgorithm::SetListItemGroupParam(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index, 1636 float referencePos, bool forwardLayout, const RefPtr<ListLayoutProperty>& layoutProperty, bool groupNeedAllLayout, 1637 bool needAdjustRefPos) 1638 { 1639 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true); 1640 CHECK_NULL_VOID(layoutAlgorithmWrapper); 1641 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm()); 1642 CHECK_NULL_VOID(itemGroup); 1643 if (jumpIndexInGroup_.has_value() && scrollAlign_ == ScrollAlign::CENTER) { 1644 referencePos = (startMainPos_ + endMainPos_) / 2; // 2:average 1645 } 1646 if (jumpIndex_) { 1647 itemGroup->ClearItemPosition(); 1648 } 1649 if (forwardLayout) { 1650 float endPos = layoutEndMainPos_.value_or(endMainPos_); 1651 float startPos = endPos - contentMainSize_; 1652 itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout); 1653 } else { 1654 float startPos = layoutStartMainPos_.value_or(startMainPos_); 1655 float endPos = startPos + contentMainSize_; 1656 itemGroup->SetListMainSize(startPos, endPos, referencePos, prevContentMainSize_, forwardLayout); 1657 } 1658 bool needMeasureFormLastItem = index < preStartIndex_; 1659 itemGroup->SetNeedMeasureFormLastItem(needMeasureFormLastItem); 1660 itemGroup->SetNeedAdjustRefPos(needAdjustRefPos); 1661 itemGroup->SetListLayoutProperty(layoutProperty); 1662 itemGroup->SetNeedCheckOffset(isNeedCheckOffset_); 1663 if (scrollSnapAlign_ != V2::ScrollSnapAlign::CENTER) { 1664 itemGroup->SetContentOffset(contentStartOffset_, contentEndOffset_); 1665 } 1666 SetListItemGroupJumpIndex(itemGroup, forwardLayout, index); 1667 1668 if (groupNeedAllLayout || (targetIndex_ && targetIndex_.value() == index) || 1669 (scrollSnapAlign_ != V2::ScrollSnapAlign::NONE && !childrenSize_)) { 1670 itemGroup->SetNeedAllLayout(); 1671 } else if (forwardFeature_ || backwardFeature_) { 1672 itemGroup->CheckNeedAllLayout(layoutWrapper, forwardLayout); 1673 } 1674 if (CheckNeedMeasure(layoutWrapper)) { 1675 itemGroup->ResetCachedItemPosition(); 1676 itemGroup->ResetCachedIndex(); 1677 if (layoutWrapper->GetHostNode() && layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>()) { 1678 auto groupPattern = layoutWrapper->GetHostNode()->GetPattern<ListItemGroupPattern>(); 1679 groupPattern->SetRecache(true); 1680 } 1681 } 1682 layoutWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE_SELF); 1683 } 1684 GetListItemGroupPosition(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1685 ListItemInfo ListLayoutAlgorithm::GetListItemGroupPosition(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index) 1686 { 1687 int32_t id = layoutWrapper->GetHostNode()->GetId(); 1688 ListItemInfo pos = { id, 0, 0, true }; 1689 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true); 1690 CHECK_NULL_RETURN(layoutAlgorithmWrapper, pos); 1691 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm()); 1692 CHECK_NULL_RETURN(itemGroup, pos); 1693 auto res = itemGroup->GetItemGroupPosition(index); 1694 return { id, res.first, res.second, true }; 1695 } 1696 GetListGroupItemHeight(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1697 float ListLayoutAlgorithm::GetListGroupItemHeight(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index) 1698 { 1699 auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm(true); 1700 CHECK_NULL_RETURN(layoutAlgorithmWrapper, 0.0f); 1701 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm()); 1702 CHECK_NULL_RETURN(itemGroup, 0.0f); 1703 return itemGroup->GetItemHeight(index); 1704 } 1705 SetListItemIndex(const RefPtr<LayoutWrapper> & layoutWrapper,int32_t index)1706 void ListLayoutAlgorithm::SetListItemIndex(const RefPtr<LayoutWrapper>& layoutWrapper, int32_t index) 1707 { 1708 auto host = layoutWrapper->GetHostNode(); 1709 CHECK_NULL_VOID(host); 1710 auto listItem = host->GetPattern<ListItemPattern>(); 1711 if (listItem) { 1712 listItem->SetIndexInList(index); 1713 return; 1714 } 1715 auto listItemGroup = host->GetPattern<ListItemGroupPattern>(); 1716 CHECK_NULL_VOID(listItemGroup); 1717 listItemGroup->SetIndexInList(index); 1718 } 1719 CheckListItemGroupRecycle(LayoutWrapper * layoutWrapper,int32_t index,float referencePos,bool forwardLayout) const1720 void ListLayoutAlgorithm::CheckListItemGroupRecycle(LayoutWrapper* layoutWrapper, int32_t index, 1721 float referencePos, bool forwardLayout) const 1722 { 1723 if (targetIndex_.has_value()) { 1724 return; 1725 } 1726 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index); 1727 CHECK_NULL_VOID(wrapper); 1728 auto algorithmWrapper = wrapper->GetLayoutAlgorithm(); 1729 CHECK_NULL_VOID(algorithmWrapper); 1730 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm()); 1731 CHECK_NULL_VOID(itemGroup); 1732 itemGroup->CheckRecycle(wrapper, startMainPos_, endMainPos_, referencePos, forwardLayout); 1733 } 1734 AdjustPostionForListItemGroup(LayoutWrapper * layoutWrapper,Axis axis,int32_t index,bool forwardLayout)1735 void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWrapper, Axis axis, int32_t index, 1736 bool forwardLayout) 1737 { 1738 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index); 1739 CHECK_NULL_VOID(wrapper); 1740 auto algorithmWrapper = wrapper->GetLayoutAlgorithm(true); 1741 CHECK_NULL_VOID(algorithmWrapper); 1742 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(algorithmWrapper->GetLayoutAlgorithm()); 1743 CHECK_NULL_VOID(itemGroup); 1744 if (forwardLayout) { 1745 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].endPos, prevContentMainSize_, 1746 !forwardLayout); 1747 } else { 1748 itemGroup->SetListMainSize(startMainPos_, endMainPos_, itemPosition_[index].startPos, prevContentMainSize_, 1749 !forwardLayout); 1750 } 1751 itemGroup->SetScrollAlign(ScrollAlign::NONE); 1752 wrapper->Measure(GetGroupLayoutConstraint()); 1753 if (childrenSize_) { 1754 return; 1755 } 1756 float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis); 1757 auto& pos = itemPosition_[index]; 1758 if (forwardLayout) { 1759 pos.startPos = pos.endPos - mainLen; 1760 } else { 1761 pos.endPos = pos.startPos + mainLen; 1762 } 1763 } 1764 OffScreenLayoutDirection(LayoutWrapper * layoutWrapper)1765 void ListLayoutAlgorithm::OffScreenLayoutDirection(LayoutWrapper* layoutWrapper) 1766 { 1767 if (!targetIndex_ || itemPosition_.empty()) { 1768 forwardFeature_ = false; 1769 backwardFeature_ = false; 1770 return; 1771 } 1772 auto layoutDirection = LayoutDirectionForTargetIndex(layoutWrapper, preStartIndex_); 1773 if (layoutDirection == LayoutDirection::BACKWARD) { 1774 forwardFeature_ = false; 1775 backwardFeature_ = true; 1776 } else { 1777 forwardFeature_ = true; 1778 backwardFeature_ = false; 1779 } 1780 } 1781 GetMidIndex(LayoutWrapper * layoutWrapper,bool usePreContentMainSize)1782 int32_t ListLayoutAlgorithm::GetMidIndex(LayoutWrapper* layoutWrapper, bool usePreContentMainSize) 1783 { 1784 float contentSize = usePreContentMainSize ? prevContentMainSize_ : contentMainSize_; 1785 float midPos = contentSize / 2.0f; 1786 if (GetStartIndex() == 0 && !IsScrollSnapAlignCenter(layoutWrapper) && 1787 GreatNotEqual(GetStartPosition(), contentStartOffset_)) { 1788 midPos = GetStartPosition() + contentSize / 2.0f - contentStartOffset_; 1789 } else if (GetEndIndex() == totalItemCount_ - 1 && !IsScrollSnapAlignCenter(layoutWrapper) && 1790 LessNotEqual(GetEndPosition(), contentMainSize_ - contentEndOffset_) && 1791 (GetStartIndex() != 0 || !NearEqual(GetStartPosition(), startMainPos_))) { 1792 midPos = GetEndPosition() - contentSize / 2.0f + contentEndOffset_; 1793 } 1794 for (auto& pos : itemPosition_) { 1795 if (midPos <= pos.second.endPos + spaceWidth_ / 2) { /* 2:half */ 1796 return pos.first; 1797 } 1798 } 1799 return totalItemCount_ - 1; 1800 } 1801 SyncGeometry(RefPtr<LayoutWrapper> & wrapper)1802 void ListLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper) 1803 { 1804 CHECK_NULL_VOID(wrapper); 1805 auto host = wrapper->GetHostNode(); 1806 CHECK_NULL_VOID(host); 1807 host->ForceSyncGeometryNode(); 1808 host->ResetLayoutAlgorithm(); 1809 host->RebuildRenderContextTree(); 1810 } 1811 LayoutCachedALine(LayoutWrapper * layoutWrapper,int32_t index,bool forward,float & currPos,float crossSize)1812 bool ListLayoutAlgorithm::LayoutCachedALine(LayoutWrapper* layoutWrapper, int32_t index, 1813 bool forward, float &currPos, float crossSize) 1814 { 1815 auto wrapper = layoutWrapper->GetChildByIndex(index, true); 1816 if (!wrapper) { 1817 return true; 1818 } 1819 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 1820 if (CheckNeedMeasure(wrapper)) { 1821 return !isGroup; 1822 } 1823 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize(); 1824 int32_t id = wrapper->GetHostNode()->GetId(); 1825 ListItemInfo pos; 1826 if (forward) { 1827 auto endPos = currPos + GetMainAxisSize(childSize, axis_); 1828 pos = { id, currPos, endPos, isGroup }; 1829 currPos = endPos + spaceWidth_; 1830 } else { 1831 auto startPos = currPos - GetMainAxisSize(childSize, axis_); 1832 pos = { id, startPos, currPos, isGroup }; 1833 currPos = startPos - spaceWidth_; 1834 } 1835 auto startIndex = index; 1836 LayoutItem(wrapper, index, pos, startIndex, crossSize); 1837 SyncGeometry(wrapper); 1838 wrapper->SetActive(false); 1839 return false; 1840 } 1841 LayoutCachedItem(LayoutWrapper * layoutWrapper,int32_t cacheCount)1842 std::list<int32_t> ListLayoutAlgorithm::LayoutCachedItem(LayoutWrapper* layoutWrapper, int32_t cacheCount) 1843 { 1844 std::list<int32_t> predictBuildList; 1845 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize(); 1846 float crossSize = GetCrossAxisSize(size, axis_); 1847 1848 auto currIndex = itemPosition_.rbegin()->first + 1; 1849 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_; 1850 for (int32_t i = 0; i < cacheCount && currIndex + i < totalItemCount_; i++) { 1851 int32_t index = currIndex + i; 1852 if (LayoutCachedALine(layoutWrapper, index, true, currPos, crossSize)) { 1853 predictBuildList.emplace_back(index); 1854 } 1855 } 1856 1857 currIndex = itemPosition_.begin()->first - 1; 1858 currPos = itemPosition_.begin()->second.startPos - spaceWidth_; 1859 for (int32_t i = 0; i < cacheCount && currIndex - i >= 0; i++) { 1860 int32_t index = currIndex - i; 1861 if (LayoutCachedALine(layoutWrapper, index, false, currPos, crossSize)) { 1862 predictBuildList.emplace_back(index); 1863 } 1864 } 1865 return predictBuildList; 1866 } 1867 PredictBuildItem(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint)1868 bool ListLayoutAlgorithm::PredictBuildItem(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint) 1869 { 1870 CHECK_NULL_RETURN(wrapper, false); 1871 wrapper->SetActive(false); 1872 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 1873 if (!isGroup) { 1874 auto frameNode = wrapper->GetHostNode(); 1875 CHECK_NULL_RETURN(frameNode, false); 1876 frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint); 1877 FrameNode::ProcessOffscreenNode(frameNode); 1878 return true; 1879 } 1880 return false; 1881 } 1882 PostIdleTask(RefPtr<FrameNode> frameNode,const ListPredictLayoutParam & param)1883 void ListLayoutAlgorithm::PostIdleTask(RefPtr<FrameNode> frameNode, const ListPredictLayoutParam& param) 1884 { 1885 CHECK_NULL_VOID(frameNode); 1886 auto pattern = frameNode->GetPattern<ListPattern>(); 1887 CHECK_NULL_VOID(pattern); 1888 if (pattern->GetPredictLayoutParam()) { 1889 pattern->SetPredictLayoutParam(param); 1890 return; 1891 } 1892 pattern->SetPredictLayoutParam(param); 1893 auto context = PipelineContext::GetCurrentContext(); 1894 CHECK_NULL_VOID(context); 1895 context->AddPredictTask([weak = WeakClaim(RawPtr(frameNode))](int64_t deadline, bool canUseLongPredictTask) { 1896 ACE_SCOPED_TRACE("List predict"); 1897 auto frameNode = weak.Upgrade(); 1898 CHECK_NULL_VOID(frameNode); 1899 auto pattern = frameNode->GetPattern<ListPattern>(); 1900 CHECK_NULL_VOID(pattern); 1901 if (!pattern->GetPredictLayoutParam().has_value()) { 1902 return; 1903 } 1904 bool needMarkDirty = false; 1905 auto param = pattern->GetPredictLayoutParam().value(); 1906 for (auto it = param.items.begin(); it != param.items.end();) { 1907 if (GetSysTimestamp() > deadline) { 1908 break; 1909 } 1910 auto wrapper = frameNode->GetOrCreateChildByIndex(*it, false, true); 1911 if (wrapper && wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) { 1912 break; 1913 } 1914 needMarkDirty = PredictBuildItem(wrapper, param.layoutConstraint) || needMarkDirty; 1915 it = param.items.erase(it); 1916 } 1917 if (needMarkDirty) { 1918 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT); 1919 } 1920 pattern->SetPredictLayoutParam(std::nullopt); 1921 if (!param.items.empty()) { 1922 ListLayoutAlgorithm::PostIdleTask(frameNode, param); 1923 pattern->SetPredictLayoutParam(param); 1924 } 1925 }); 1926 } 1927 1928 // return current CachedCount and max CacheCount GetLayoutGroupCachedCount(LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & wrapper,int32_t forwardCache,int32_t backwardCache,int32_t index,bool outOfView)1929 CachedIndexInfo ListLayoutAlgorithm::GetLayoutGroupCachedCount(LayoutWrapper* layoutWrapper, 1930 const RefPtr<LayoutWrapper>& wrapper, int32_t forwardCache, int32_t backwardCache, int32_t index, bool outOfView) 1931 { 1932 CachedIndexInfo res; 1933 auto groupNode = AceType::DynamicCast<FrameNode>(wrapper); 1934 CHECK_NULL_RETURN(groupNode, res); 1935 auto group = groupNode->GetPattern<ListItemGroupPattern>(); 1936 CHECK_NULL_RETURN(group, res); 1937 const auto& itemPos = group->GetItemPosition(); 1938 bool reCache = false; 1939 if (outOfView && recycledItemPosition_.count(index) == 0) { 1940 reCache = CheckNeedMeasure(wrapper); 1941 } else if (outOfView) { 1942 wrapper->SetActive(true); 1943 wrapper->Layout(); 1944 group->SyncItemsToCachedItemPosition(); 1945 } 1946 bool forward = forwardCache > -1; 1947 bool backward = backwardCache > -1; 1948 if (forward && backward && itemPos.empty()) { 1949 forward = group->NeedCacheForward(layoutWrapper); 1950 backward = !forward; 1951 forwardCache = forward ? forwardCache : -1; 1952 backwardCache = backward ? backwardCache : -1; 1953 } 1954 res = group->UpdateCachedIndex(outOfView, reCache, forwardCache, backwardCache); 1955 if (group->GetTotalItemCount() == 0 && outOfView) { 1956 if (groupNode->CheckNeedForceMeasureAndLayout()) { 1957 res = {0, 0, 1, 1}; 1958 } else { 1959 res = {1, 1, 1, 1}; 1960 } 1961 } 1962 ACE_SCOPED_TRACE("GetLayoutGroupCachedCount forward:%d, %d, backward:%d, %d", 1963 res.forwardCachedCount, res.forwardCacheMax, res.backwardCachedCount, res.backwardCacheMax); 1964 return res; 1965 } 1966 GetLayoutCrossAxisSize(LayoutWrapper * layoutWrapper)1967 float ListLayoutAlgorithm::GetLayoutCrossAxisSize(LayoutWrapper* layoutWrapper) 1968 { 1969 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize(); 1970 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder(); 1971 MinusPaddingToSize(padding, size); 1972 return GetCrossAxisSize(size, axis_); 1973 } 1974 LayoutCachedForward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)1975 int32_t ListLayoutAlgorithm::LayoutCachedForward(LayoutWrapper* layoutWrapper, 1976 int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show) 1977 { 1978 float crossSize = GetLayoutCrossAxisSize(layoutWrapper); 1979 curIndex = itemPosition_.rbegin()->first + 1; 1980 auto currPos = itemPosition_.rbegin()->second.endPos + spaceWidth_; 1981 while (cachedCount < cacheCount && curIndex < totalItemCount_) { 1982 auto wrapper = layoutWrapper->GetChildByIndex(curIndex, !show); 1983 if (!wrapper) { 1984 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 }); 1985 return curIndex - 1; 1986 } 1987 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 1988 bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper); 1989 if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) { 1990 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 }); 1991 } 1992 if (!isGroup && isDirty) { 1993 return curIndex - 1; 1994 } 1995 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize(); 1996 auto endPos = currPos + GetMainAxisSize(childSize, axis_); 1997 int32_t id = wrapper->GetHostNode()->GetId(); 1998 ListItemInfo pos = { id, currPos, endPos, isGroup }; 1999 currPos = endPos + spaceWidth_; 2000 auto startIndex = curIndex; 2001 LayoutItem(wrapper, curIndex, pos, startIndex, crossSize); 2002 cachedItemPosition_[curIndex] = pos; 2003 if (isGroup) { 2004 auto res = GetLayoutGroupCachedCount( 2005 layoutWrapper, wrapper, cacheCount - cachedCount, -1, curIndex, true); 2006 if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount - cachedCount) { 2007 predictList.emplace_back(PredictLayoutItem { curIndex, cachedCount, -1 }); 2008 return res.forwardCachedCount > 0 ? curIndex : curIndex - 1; 2009 } 2010 cachedCount += std::max(res.forwardCacheMax, 1); 2011 } else { 2012 cachedCount++; 2013 } 2014 SyncGeometry(wrapper); 2015 wrapper->SetActive(false); 2016 curIndex++; 2017 } 2018 return curIndex - 1; 2019 } 2020 LayoutCachedBackward(LayoutWrapper * layoutWrapper,int32_t cacheCount,int32_t & cachedCount,int32_t curIndex,std::list<PredictLayoutItem> & predictList,bool show)2021 int32_t ListLayoutAlgorithm::LayoutCachedBackward(LayoutWrapper* layoutWrapper, 2022 int32_t cacheCount, int32_t& cachedCount, int32_t curIndex, std::list<PredictLayoutItem>& predictList, bool show) 2023 { 2024 float crossSize = GetLayoutCrossAxisSize(layoutWrapper); 2025 curIndex = itemPosition_.begin()->first - 1; 2026 auto currPos = itemPosition_.begin()->second.startPos - spaceWidth_; 2027 while (cachedCount < cacheCount && curIndex >= 0) { 2028 auto wrapper = layoutWrapper->GetChildByIndex(curIndex, !show); 2029 if (!wrapper) { 2030 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount }); 2031 return curIndex + 1; 2032 } 2033 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 2034 bool isDirty = wrapper->CheckNeedForceMeasureAndLayout() || !IsListLanesEqual(wrapper); 2035 if (!isGroup && (isDirty || CheckLayoutConstraintChanged(wrapper))) { 2036 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount }); 2037 } 2038 if (!isGroup && isDirty) { 2039 return curIndex + 1; 2040 } 2041 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize(); 2042 auto startPos = currPos - GetMainAxisSize(childSize, axis_); 2043 int32_t id = wrapper->GetHostNode()->GetId(); 2044 ListItemInfo pos = { id, startPos, currPos, isGroup }; 2045 currPos = startPos - spaceWidth_; 2046 auto startIndex = curIndex; 2047 LayoutItem(wrapper, curIndex, pos, startIndex, crossSize); 2048 cachedItemPosition_[curIndex] = pos; 2049 if (isGroup) { 2050 auto res = GetLayoutGroupCachedCount( 2051 layoutWrapper, wrapper, -1, cacheCount - cachedCount, curIndex, true); 2052 if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount - cachedCount) { 2053 predictList.emplace_back(PredictLayoutItem { curIndex, -1, cachedCount }); 2054 return res.backwardCachedCount > 0 ? curIndex : curIndex + 1; 2055 } 2056 cachedCount += std::max(res.backwardCacheMax, 1); 2057 } else { 2058 cachedCount++; 2059 } 2060 SyncGeometry(wrapper); 2061 wrapper->SetActive(false); 2062 curIndex--; 2063 } 2064 return curIndex + 1; 2065 } 2066 LayoutCachedItemInEdgeGroup(LayoutWrapper * layoutWrapper,int32_t cacheCount,std::list<PredictLayoutItem> & predictList)2067 std::tuple<int32_t, int32_t, int32_t, int32_t> ListLayoutAlgorithm::LayoutCachedItemInEdgeGroup( 2068 LayoutWrapper* layoutWrapper, int32_t cacheCount, std::list<PredictLayoutItem>& predictList) 2069 { 2070 int32_t startIndex = GetStartIndex(); 2071 int32_t endIndex = GetEndIndex(); 2072 int32_t cachedForward = 0; 2073 int32_t cachedBackward = 0; 2074 if (startIndex == endIndex && itemPosition_.begin()->second.isGroup) { 2075 auto wrapper = layoutWrapper->GetChildByIndex(startIndex); 2076 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, cacheCount, startIndex, false); 2077 if ((res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) || 2078 (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount)) { 2079 int32_t forwardCached = res.forwardCacheMax > 0 ? cachedForward : -1; 2080 int32_t backwardCached = res.backwardCacheMax > 0 ? cachedBackward : -1; 2081 predictList.emplace_back(PredictLayoutItem { startIndex, forwardCached, backwardCached }); 2082 } 2083 cachedForward += res.forwardCacheMax; 2084 cachedBackward += res.backwardCacheMax; 2085 } else { 2086 if (itemPosition_.rbegin()->second.isGroup) { 2087 auto wrapper = layoutWrapper->GetChildByIndex(endIndex); 2088 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, cacheCount, -1, endIndex, false); 2089 if (res.forwardCachedCount < res.forwardCacheMax && res.forwardCachedCount < cacheCount) { 2090 predictList.emplace_back(PredictLayoutItem { endIndex, cachedForward, -1 }); 2091 } 2092 cachedForward += res.forwardCacheMax; 2093 } 2094 if (itemPosition_.begin()->second.isGroup) { 2095 auto wrapper = layoutWrapper->GetChildByIndex(startIndex); 2096 auto res = GetLayoutGroupCachedCount(layoutWrapper, wrapper, -1, cacheCount, startIndex, false); 2097 if (res.backwardCachedCount < res.backwardCacheMax && res.backwardCachedCount < cacheCount) { 2098 predictList.emplace_back(PredictLayoutItem { startIndex, -1, cachedBackward }); 2099 } 2100 cachedBackward += res.backwardCacheMax; 2101 } 2102 } 2103 return { startIndex, endIndex, cachedForward, cachedBackward }; 2104 } 2105 LayoutCachedItemV2(LayoutWrapper * layoutWrapper,int32_t cacheCount,bool show)2106 std::list<PredictLayoutItem> ListLayoutAlgorithm::LayoutCachedItemV2(LayoutWrapper* layoutWrapper, int32_t cacheCount, 2107 bool show) 2108 { 2109 ACE_SCOPED_TRACE("LayoutCachedItemV2"); 2110 std::list<PredictLayoutItem> predictBuildList; 2111 auto [startIndex, endIndex, cachedForward, cachedBackward] = 2112 LayoutCachedItemInEdgeGroup(layoutWrapper, cacheCount, predictBuildList); 2113 if (cachedForward < cacheCount && endIndex < totalItemCount_ - 1) { 2114 endIndex = LayoutCachedForward(layoutWrapper, cacheCount, cachedForward, endIndex, predictBuildList, show); 2115 } 2116 if (cachedBackward < cacheCount && startIndex > 0) { 2117 startIndex = 2118 LayoutCachedBackward(layoutWrapper, cacheCount, cachedBackward, startIndex, predictBuildList, show); 2119 } 2120 int32_t cacheStart = itemPosition_.begin()->first - startIndex; 2121 int32_t cacheEnd = endIndex - itemPosition_.rbegin()->first; 2122 ResetLayoutItem(layoutWrapper); 2123 SetActiveChildRange(layoutWrapper, cacheStart, cacheEnd, show); 2124 return predictBuildList; 2125 } 2126 PredictBuildGroup(RefPtr<LayoutWrapper> wrapper,const LayoutConstraintF & constraint,int64_t deadline,int32_t forwardCached,int32_t backwardCached,const ListMainSizeValues & listMainSizeValues)2127 bool ListLayoutAlgorithm::PredictBuildGroup(RefPtr<LayoutWrapper> wrapper, const LayoutConstraintF& constraint, 2128 int64_t deadline, int32_t forwardCached, int32_t backwardCached, const ListMainSizeValues& listMainSizeValues) 2129 { 2130 CHECK_NULL_RETURN(wrapper, false); 2131 auto groupNode = AceType::DynamicCast<FrameNode>(wrapper); 2132 CHECK_NULL_RETURN(groupNode, false); 2133 auto groupPattern = groupNode->GetPattern<ListItemGroupPattern>(); 2134 CHECK_NULL_RETURN(groupPattern, false); 2135 float referencePos = 0.0f; 2136 if (listMainSizeValues.jumpIndexInGroup.has_value() && listMainSizeValues.scrollAlign == ScrollAlign::CENTER) { 2137 referencePos = (listMainSizeValues.startPos + listMainSizeValues.endPos) / 2; // 2:average 2138 } 2139 float endPos = 0.0f; 2140 float startPos = 0.0f; 2141 if (listMainSizeValues.forward) { 2142 startPos = listMainSizeValues.startPos; 2143 endPos = listMainSizeValues.layoutEndMainPos.value_or(listMainSizeValues.endPos); 2144 } else { 2145 startPos = listMainSizeValues.layoutStartMainPos.value_or(listMainSizeValues.startPos); 2146 endPos = listMainSizeValues.endPos; 2147 } 2148 ListMainSizeValues values; 2149 values.startPos = startPos; 2150 values.endPos = endPos; 2151 values.referencePos = referencePos; 2152 values.prevContentMainSize = listMainSizeValues.prevContentMainSize; 2153 values.forward = listMainSizeValues.forward; 2154 values.backward = listMainSizeValues.backward; 2155 groupPattern->LayoutCache(constraint, deadline, forwardCached, backwardCached, values); 2156 return true; 2157 } 2158 PredictBuildV2(RefPtr<FrameNode> frameNode,int64_t deadline,ListMainSizeValues listMainSizeValues,bool show)2159 void ListLayoutAlgorithm::PredictBuildV2( 2160 RefPtr<FrameNode> frameNode, int64_t deadline, ListMainSizeValues listMainSizeValues, bool show) 2161 { 2162 ACE_SCOPED_TRACE("List predict v2"); 2163 CHECK_NULL_VOID(frameNode); 2164 auto pattern = frameNode->GetPattern<ListPattern>(); 2165 CHECK_NULL_VOID(pattern); 2166 if (!pattern->GetPredictLayoutParamV2().has_value()) { 2167 return; 2168 } 2169 bool needMarkDirty = false; 2170 auto param = pattern->GetPredictLayoutParamV2().value(); 2171 for (auto it = param.items.begin(); it != param.items.end();) { 2172 if (GetSysTimestamp() > deadline) { 2173 break; 2174 } 2175 ACE_SCOPED_TRACE("predict Item:%d", (*it).index); 2176 auto wrapper = frameNode->GetOrCreateChildByIndex((*it).index, show, true); 2177 if (!wrapper) { 2178 it = param.items.erase(it); 2179 continue; 2180 } 2181 if (wrapper->GetHostNode() && !wrapper->GetHostNode()->RenderCustomChild(deadline)) { 2182 break; 2183 } 2184 bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; 2185 if (!isGroup) { 2186 auto frameNode = wrapper->GetHostNode(); 2187 CHECK_NULL_VOID(frameNode); 2188 frameNode->GetGeometryNode()->SetParentLayoutConstraint(param.layoutConstraint); 2189 FrameNode::ProcessOffscreenNode(frameNode); 2190 } else { 2191 listMainSizeValues.forward = (*it).forwardCacheCount > -1; 2192 listMainSizeValues.backward = (*it).backwardCacheCount > -1; 2193 PredictBuildGroup(wrapper, param.groupLayoutConstraint, deadline, (*it).forwardCacheCount, 2194 (*it).backwardCacheCount, listMainSizeValues); 2195 } 2196 needMarkDirty = true; 2197 it = param.items.erase(it); 2198 } 2199 if (needMarkDirty) { 2200 frameNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT); 2201 } 2202 pattern->SetPredictLayoutParamV2(std::nullopt); 2203 if (!param.items.empty()) { 2204 ListLayoutAlgorithm::PostIdleTaskV2(frameNode, param, listMainSizeValues, show); 2205 } 2206 } 2207 PostIdleTaskV2(RefPtr<FrameNode> frameNode,const ListPredictLayoutParamV2 & param,ListMainSizeValues listMainSizeValues,bool show)2208 void ListLayoutAlgorithm::PostIdleTaskV2(RefPtr<FrameNode> frameNode, 2209 const ListPredictLayoutParamV2& param, ListMainSizeValues listMainSizeValues, bool show) 2210 { 2211 ACE_SCOPED_TRACE("PostIdleTaskV2"); 2212 CHECK_NULL_VOID(frameNode); 2213 auto pattern = frameNode->GetPattern<ListPattern>(); 2214 CHECK_NULL_VOID(pattern); 2215 if (pattern->GetPredictLayoutParamV2()) { 2216 pattern->SetPredictLayoutParamV2(param); 2217 return; 2218 } 2219 pattern->SetPredictLayoutParamV2(param); 2220 auto context = PipelineContext::GetCurrentContext(); 2221 CHECK_NULL_VOID(context); 2222 context->AddPredictTask( 2223 [weak = WeakClaim(RawPtr(frameNode)), value = listMainSizeValues, show = show](int64_t deadline, 2224 bool canUseLongPredictTask) { ListLayoutAlgorithm::PredictBuildV2(weak.Upgrade(), deadline, value, show); }); 2225 } 2226 GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign) const2227 float ListLayoutAlgorithm::GetStopOnScreenOffset(V2::ScrollSnapAlign scrollSnapAlign) const 2228 { 2229 float stopOnScreen = 0; 2230 if (scrollSnapAlign == V2::ScrollSnapAlign::START) { 2231 stopOnScreen = contentStartOffset_; 2232 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) { 2233 stopOnScreen = contentMainSize_ / 2.0f; 2234 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) { 2235 stopOnScreen = contentMainSize_ - contentEndOffset_; 2236 } 2237 return stopOnScreen; 2238 } 2239 FindPredictSnapIndexInItemPositionsStart(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2240 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsStart( 2241 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const 2242 { 2243 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::START); 2244 float itemHeight = itemPosition_.begin()->second.endPos - itemPosition_.begin()->second.startPos; 2245 for (const auto& positionInfo : itemPosition_) { 2246 auto startPos = positionInfo.second.startPos - itemHeight / 2.0f - spaceWidth_; 2247 float itemHeight = positionInfo.second.endPos - positionInfo.second.startPos; 2248 auto endPos = positionInfo.second.startPos + itemHeight / 2.0f; 2249 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) && 2250 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) { 2251 endIndex = positionInfo.first; 2252 } 2253 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) { 2254 currIndex = positionInfo.first; 2255 } 2256 if (endIndex >= 0 && currIndex >= 0) { 2257 break; 2258 } 2259 } 2260 } 2261 FindPredictSnapIndexInItemPositionsCenter(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2262 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsCenter( 2263 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const 2264 { 2265 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::CENTER); 2266 for (const auto& positionInfo : itemPosition_) { 2267 auto startPos = positionInfo.second.startPos - spaceWidth_ / 2.0f; 2268 auto endPos = positionInfo.second.endPos + spaceWidth_ / 2.0f; 2269 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) && 2270 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) { 2271 endIndex = positionInfo.first; 2272 } 2273 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) { 2274 currIndex = positionInfo.first; 2275 } 2276 if (endIndex >= 0 && currIndex >= 0) { 2277 break; 2278 } 2279 } 2280 } 2281 FindPredictSnapIndexInItemPositionsEnd(float predictEndPos,int32_t & endIndex,int32_t & currIndex) const2282 void ListLayoutAlgorithm::FindPredictSnapIndexInItemPositionsEnd( 2283 float predictEndPos, int32_t& endIndex, int32_t& currIndex) const 2284 { 2285 float stopOnScreen = GetStopOnScreenOffset(V2::ScrollSnapAlign::END); 2286 float itemHeight = itemPosition_.rbegin()->second.endPos - itemPosition_.rbegin()->second.startPos; 2287 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) { 2288 auto endPos = pos->second.endPos + itemHeight / 2.0f + spaceWidth_; 2289 itemHeight = pos->second.endPos - pos->second.startPos; 2290 auto startPos = pos->second.endPos - itemHeight / 2.0f; 2291 if (GreatOrEqual(predictEndPos + stopOnScreen, totalOffset_ + startPos) && 2292 LessNotEqual(predictEndPos + stopOnScreen, totalOffset_ + endPos)) { 2293 endIndex = pos->first; 2294 } 2295 if (GreatOrEqual(stopOnScreen, startPos) && LessNotEqual(stopOnScreen, endPos)) { 2296 currIndex = pos->first; 2297 } 2298 if (endIndex >= 0 && currIndex >= 0) { 2299 break; 2300 } 2301 } 2302 } 2303 FindPredictSnapEndIndexInItemPositions(float predictEndPos,V2::ScrollSnapAlign scrollSnapAlign)2304 int32_t ListLayoutAlgorithm::FindPredictSnapEndIndexInItemPositions( 2305 float predictEndPos, V2::ScrollSnapAlign scrollSnapAlign) 2306 { 2307 int32_t endIndex = -1; 2308 int32_t currIndex = -1; 2309 2310 if (scrollSnapAlign == V2::ScrollSnapAlign::START) { 2311 FindPredictSnapIndexInItemPositionsStart(predictEndPos, endIndex, currIndex); 2312 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) { 2313 FindPredictSnapIndexInItemPositionsCenter(predictEndPos, endIndex, currIndex); 2314 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) { 2315 FindPredictSnapIndexInItemPositionsEnd(predictEndPos, endIndex, currIndex); 2316 } 2317 if (endIndex == currIndex && currIndex >= 0) { 2318 if (scrollSnapVelocity_ < -SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) { 2319 endIndex = std::min(GetEndIndex(), endIndex + 1); 2320 } else if (scrollSnapVelocity_ > SCROLL_SNAP_VELOCITY_TH * Scrollable::GetVelocityScale()) { 2321 endIndex = std::min(GetStartIndex(), endIndex - 1); 2322 } 2323 } 2324 return endIndex; 2325 } 2326 IsUniformHeightProbably()2327 bool ListLayoutAlgorithm::IsUniformHeightProbably() 2328 { 2329 bool isUniformHeightProbably = true; 2330 float itemHeight = 0.0f; 2331 float currentItemHeight = 0.0f; 2332 for (const auto& positionInfo : itemPosition_) { 2333 currentItemHeight = positionInfo.second.endPos - positionInfo.second.startPos; 2334 if (NearZero(itemHeight)) { 2335 itemHeight = currentItemHeight; 2336 } else if (!NearEqual(currentItemHeight, itemHeight)) { 2337 isUniformHeightProbably = false; 2338 break; 2339 } 2340 } 2341 return isUniformHeightProbably; 2342 } 2343 CalculatePredictSnapEndPositionByIndex(int32_t index,V2::ScrollSnapAlign scrollSnapAlign)2344 float ListLayoutAlgorithm::CalculatePredictSnapEndPositionByIndex(int32_t index, V2::ScrollSnapAlign scrollSnapAlign) 2345 { 2346 float predictSnapEndPos = 0; 2347 if (scrollSnapAlign == V2::ScrollSnapAlign::START) { 2348 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos - contentStartOffset_; 2349 float endPos = GetEndPosition(); 2350 float itemTotalSize = endPos - GetStartPosition(); 2351 float contentSize = contentMainSize_ - contentEndOffset_ - contentStartOffset_; 2352 if ((GetEndIndex() == totalItemCount_ - 1) && GreatNotEqual(itemTotalSize, contentSize) && 2353 GreatNotEqual(predictSnapEndPos + contentMainSize_ - contentEndOffset_, totalOffset_ + endPos)) { 2354 predictSnapEndPos = totalOffset_ + endPos - contentMainSize_ + contentEndOffset_; 2355 } 2356 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) { 2357 float itemHeight = itemPosition_[index].endPos - itemPosition_[index].startPos; 2358 predictSnapEndPos = totalOffset_ + itemPosition_[index].startPos + itemHeight / 2.0f - contentMainSize_ / 2.0f; 2359 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) { 2360 predictSnapEndPos = totalOffset_ + itemPosition_[index].endPos - contentMainSize_ + contentEndOffset_; 2361 if (GetStartIndex() == 0 && LessNotEqual(predictSnapEndPos, totalOffset_ + GetStartPosition())) { 2362 predictSnapEndPos = totalOffset_ + GetStartPosition() - contentStartOffset_; 2363 } 2364 } 2365 return predictSnapEndPos; 2366 } 2367 OnItemPositionAddOrUpdate(LayoutWrapper * layoutWrapper,int32_t index)2368 void ListLayoutAlgorithm::OnItemPositionAddOrUpdate(LayoutWrapper* layoutWrapper, int32_t index) 2369 { 2370 if (!predictSnapEndPos_.has_value()) { 2371 return; 2372 } 2373 auto listLayoutProperty = AceType::DynamicCast<ListLayoutProperty>(layoutWrapper->GetLayoutProperty()); 2374 CHECK_NULL_VOID(listLayoutProperty); 2375 auto scrollSnapAlign = listLayoutProperty->GetScrollSnapAlign().value_or(V2::ScrollSnapAlign::NONE); 2376 float startPos = 0.0f; 2377 float endPos = 0.0f; 2378 if (scrollSnapAlign == V2::ScrollSnapAlign::START) { 2379 startPos = totalOffset_ + itemPosition_[index].startPos - spaceWidth_; 2380 endPos = totalOffset_ + itemPosition_[index].endPos; 2381 } else if (scrollSnapAlign == V2::ScrollSnapAlign::CENTER) { 2382 startPos = totalOffset_ + itemPosition_[index].startPos - spaceWidth_ / 2.0f; 2383 endPos = totalOffset_ + itemPosition_[index].endPos + spaceWidth_ / 2.0f; 2384 } else if (scrollSnapAlign == V2::ScrollSnapAlign::END) { 2385 startPos = totalOffset_ + itemPosition_[index].startPos; 2386 endPos = totalOffset_ + itemPosition_[index].endPos + spaceWidth_; 2387 } else { 2388 return; 2389 } 2390 2391 float predictSnapEndPos = predictSnapEndPos_.value(); 2392 float stopOnScreen = GetStopOnScreenOffset(scrollSnapAlign); 2393 if (GreatOrEqual(predictSnapEndPos + stopOnScreen, startPos) && 2394 LessNotEqual(predictSnapEndPos + stopOnScreen, endPos)) { 2395 predictSnapEndPos = CalculatePredictSnapEndPositionByIndex(index, scrollSnapAlign); 2396 } else { 2397 return; 2398 } 2399 2400 if (!NearEqual(predictSnapEndPos, predictSnapEndPos_.value())) { 2401 predictSnapEndPos_ = predictSnapEndPos; 2402 } 2403 } 2404 GetSnapStartIndexAndPos()2405 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapStartIndexAndPos() 2406 { 2407 int32_t startIndex = std::min(GetStartIndex(), totalItemCount_ - 1); 2408 float startPos = itemPosition_.begin()->second.startPos; 2409 for (auto& pos : itemPosition_) { 2410 if (NearEqual(pos.second.startPos, prevContentStartOffset_)) { 2411 startIndex = pos.first; 2412 startPos = itemPosition_[startIndex].startPos + contentStartOffset_ - prevContentStartOffset_; 2413 break; 2414 } else if (GreatNotEqual(pos.second.startPos, prevContentStartOffset_)) { 2415 if ((GetEndIndex() == totalItemCount_ - 1) && 2416 NearEqual(GetEndPosition(), prevContentMainSize_ - prevContentEndOffset_) && !canOverScroll_) { 2417 startIndex = pos.first; 2418 startPos = contentStartOffset_; 2419 adjustOffset_ = pos.second.startPos - prevContentStartOffset_; 2420 } 2421 break; 2422 } 2423 } 2424 return std::make_pair(std::min(startIndex, totalItemCount_ -1), startPos); 2425 } 2426 GetSnapEndIndexAndPos()2427 std::pair<int32_t, float> ListLayoutAlgorithm::GetSnapEndIndexAndPos() 2428 { 2429 int32_t endIndex = -1; 2430 float endPos = 0.0f; 2431 for (auto pos = itemPosition_.rbegin(); pos != itemPosition_.rend(); ++pos) { 2432 if (NearEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) { 2433 endIndex = pos->first; 2434 endPos = itemPosition_[endIndex].endPos - contentEndOffset_ + prevContentEndOffset_; 2435 break; 2436 } else if (GreatNotEqual(prevContentMainSize_ - pos->second.endPos, prevContentEndOffset_)) { 2437 if ((GetStartIndex() == 0) && NearEqual(GetStartPosition(), prevContentStartOffset_) && !canOverScroll_) { 2438 endIndex = pos->first; 2439 endPos = prevContentMainSize_ - contentEndOffset_; 2440 adjustOffset_ = pos->second.endPos + prevContentEndOffset_ - prevContentMainSize_; 2441 } 2442 break; 2443 } 2444 } 2445 return std::make_pair(std::min(endIndex, totalItemCount_ -1), endPos); 2446 } 2447 UpdateDefaultCachedCount(const int32_t oldCacheCount,const int32_t itemCount)2448 int32_t ListLayoutAlgorithm::UpdateDefaultCachedCount(const int32_t oldCacheCount, const int32_t itemCount) 2449 { 2450 if (itemCount <= 0) { 2451 return oldCacheCount; 2452 } 2453 static float pageCount = SystemProperties::GetPageCount(); 2454 if (pageCount <= 0.0f) { 2455 return oldCacheCount; 2456 } 2457 constexpr int32_t MAX_DEFAULT_CACHED_COUNT = 16; 2458 int32_t newCachedCount = static_cast<int32_t>(ceil(pageCount * itemCount)); 2459 if (newCachedCount > MAX_DEFAULT_CACHED_COUNT) { 2460 TAG_LOGI(AceLogTag::ACE_LIST, "Default cachedCount exceed 16"); 2461 return MAX_DEFAULT_CACHED_COUNT; 2462 } else { 2463 return std::max(newCachedCount, oldCacheCount); 2464 } 2465 } 2466 } // namespace OHOS::Ace::NG 2467