diff --git a/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js b/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js index 97a60d87c377517afdd8014d06b91e15f3e39d69..9bb6438cfd09d003c0dfd2d035b6823cd6b0d0d0 100644 --- a/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js +++ b/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js @@ -2310,6 +2310,70 @@ class WaterFlowSections { } } +class ChildrenMainSizeParamError extends Error { + constructor(message, code) { + super(message); + this.code = code; + } +} + +class ChildrenMainSize { + + constructor(childDefaultSize) { + if (this.isInvalid(childDefaultSize)) { + throw new ChildrenMainSizeParamError('The parameter check failed.', '401'); + } + this.defaultMainSize = childDefaultSize; + this.changeFlag = true; + this.changeArray = []; + } + + set childDefaultSize(value) { + if (this.isInvalid(value)) { + throw new ChildrenMainSizeParamError('The parameter check failed.', '401'); + } + this.defaultMainSize = value; + } + + get childDefaultSize() { + return this.defaultMainSize; + } + + // splice(start: number, deleteCount?: number, childrenSize?: Array); + splice(start, deleteCount, childrenSize) { + let paramCount = arguments.length; + if (this.isInvalid(start)) { + throw new ChildrenMainSizeParamError('The parameter check failed.', '401'); + } + let startValue = Math.trunc(start); + let deleteCountValue = deleteCount && !(this.isInvalid(deleteCount)) ? Math.trunc(deleteCount) : 0; + if (paramCount == 1) { + this.changeArray.push({ start: startValue }); + } else if (paramCount == 2) { + this.changeArray.push({ start: startValue, deleteCount: deleteCountValue }); + } else if (paramCount == 3) { + this.changeArray.push({ start: startValue, deleteCount: deleteCountValue, childrenSize: childrenSize }); + } + this.changeFlag = !this.changeFlag; + } + + update(index, childSize) { + if (this.isInvalid(index)) { + throw new ChildrenMainSizeParamError('The parameter check failed.', '401'); + } + this.changeArray.push({ start: Math.trunc(index), deleteCount: 1, childrenSize: [childSize] }); + this.changeFlag = !this.changeFlag; + } + + isInvalid(input) { + return !(Number.isFinite(input) && (Math.sign(input) == 1 || Math.sign(input) == 0)); + } + + clearChanges() { + this.changeArray = []; + } +} + var ImageSpanAlignment; (function (ImageSpanAlignment) { ImageSpanAlignment[ImageSpanAlignment["NONE"] = 0] = "NONE"; diff --git a/frameworks/bridge/declarative_frontend/jsview/js_list.cpp b/frameworks/bridge/declarative_frontend/jsview/js_list.cpp index 72ed2a45b0458671cbf25e1f4f896b238c22f964..c5508a45df73d5dce04e8dcbefeb64556696f97a 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_list.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_list.cpp @@ -71,6 +71,37 @@ constexpr ScrollAlign ALIGN_TABLE[] = { }; } +namespace { +void ParseChange(const JSRef& changeObject, const float defaultSize, int32_t& start, + int32_t& deleteCount, std::vector& newChildrenSize) +{ + if (!JSViewAbstract::ParseJsInteger(changeObject->GetProperty("start"), start) || start < 0) { + LOGW("JSList input parameter start check failed."); + return; + } + if (!(changeObject->HasProperty("deleteCount"))) { + // If only input one parameter, set -1 to deleteCount for deleting elements after index 'start' in the array. + deleteCount = -1; + } else if (!JSViewAbstract::ParseJsInteger(changeObject->GetProperty("deleteCount"), deleteCount) || + deleteCount < 0) { + deleteCount = 0; + } + auto childrenSizeValue = changeObject->GetProperty("childrenSize"); + if (childrenSizeValue->IsArray()) { + auto childrenSize = JSRef::Cast(childrenSizeValue); + auto childrenSizeCount = childrenSize->Length(); + for (size_t j = 0; j < childrenSizeCount; ++j) { + double childSize = 0.0f; + if (!JSViewAbstract::ParseJsDouble(childrenSize->GetValueAt(j), childSize) || + !NonNegative(childSize) || std::isinf(childSize)) { + childSize = defaultSize; + } + newChildrenSize.emplace_back(Dimension(childSize, DimensionUnit::VP).ConvertToPx()); + } + } +} +} // namespace + void JSList::SetDirection(int32_t direction) { ListModel::GetInstance()->SetListDirection(static_cast(direction)); @@ -165,6 +196,45 @@ void JSList::Create(const JSCallbackInfo& args) args.ReturnSelf(); } +void JSList::SetChildrenMainSize(const JSCallbackInfo& args) +{ + if (args.Length() != 1 || !(args[0]->IsObject())) { + return; + } + JSRef childrenSizeObj = JSRef::Cast(args[0]); + double defaultSize = 0.0f; + if (!ParseJsDouble(childrenSizeObj->GetProperty("defaultMainSize"), defaultSize) || !NonNegative(defaultSize)) { + LOGW("JSList input parameter defaultSize check failed."); + return; + } + auto listChildrenMainSize = ListModel::GetInstance()->GetOrCreateListChildrenMainSize(); + CHECK_NULL_VOID(listChildrenMainSize); + listChildrenMainSize->UpdateDefaultSize(Dimension(defaultSize, DimensionUnit::VP).ConvertToPx()); + + auto changes = childrenSizeObj->GetProperty("changeArray"); + if (!changes->IsArray()) { + return; + } + auto changeArray = JSRef::Cast(changes); + auto length = changeArray->Length(); + for (size_t i = 0; i < length; ++i) { + auto change = changeArray->GetValueAt(i); + auto changeObject = JSRef::Cast(change); + int32_t start = 0; + int32_t deleteCount = 0; + std::vector newChildrenSize; + ParseChange(changeObject, defaultSize, start, deleteCount, newChildrenSize); + listChildrenMainSize->ChangeData(start, deleteCount, newChildrenSize); + } + + auto clearFunc = childrenSizeObj->GetProperty("clearChanges"); + if (!clearFunc->IsFunction()) { + return; + } + auto func = JSRef::Cast(clearFunc); + JSRef::Cast(func->Call(childrenSizeObj)); +} + void JSList::SetChainAnimation(bool enableChainAnimation) { ListModel::GetInstance()->SetChainAnimation(enableChainAnimation); @@ -701,6 +771,7 @@ void JSList::JSBind(BindingTarget globalObj) JSClass::StaticMethod("cachedCount", &JSList::SetCachedCount); JSClass::StaticMethod("chainAnimation", &JSList::SetChainAnimation); JSClass::StaticMethod("chainAnimationOptions", &JSList::SetChainAnimationOptions); + JSClass::StaticMethod("childrenMainSize", &JSList::SetChildrenMainSize); JSClass::StaticMethod("multiSelectable", &JSList::SetMultiSelectable); JSClass::StaticMethod("alignListItem", &JSList::SetListItemAlign); JSClass::StaticMethod("lanes", &JSList::SetLanes); diff --git a/frameworks/bridge/declarative_frontend/jsview/js_list.h b/frameworks/bridge/declarative_frontend/jsview/js_list.h index 35b3c34380e43ccea7f73eca2f267f0783184005..2a60a20799ae9ca53ec37fd2746a1830368ee98e 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_list.h +++ b/frameworks/bridge/declarative_frontend/jsview/js_list.h @@ -64,6 +64,7 @@ public: static void SetCachedCount(const JSCallbackInfo& info); static void SetChainAnimation(bool enableChainAnimation); static void SetChainAnimationOptions(const JSCallbackInfo& info); + static void SetChildrenMainSize(const JSCallbackInfo& args); static void SetMultiSelectable(bool multiSelectable); static void SetListItemAlign(int32_t itemAlignment); static void SetLanes(const JSCallbackInfo& info); diff --git a/frameworks/bridge/declarative_frontend/jsview/js_list_item_group.cpp b/frameworks/bridge/declarative_frontend/jsview/js_list_item_group.cpp index 0db06b1af98e9d8a838ba28da02387c4136282ff..f090253062a58a3fab70ae521efeb62e4f4edfb3 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_list_item_group.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_list_item_group.cpp @@ -50,6 +50,76 @@ ListItemGroupModel* ListItemGroupModel::GetInstance() } // namespace OHOS::Ace namespace OHOS::Ace::Framework { +namespace { +void ParseChange(const JSRef& changeObject, const float defaultSize, int32_t& start, + int32_t& deleteCount, std::vector& newChildrenSize) +{ + if (!JSViewAbstract::ParseJsInteger(changeObject->GetProperty("start"), start) || start < 0) { + LOGW("JSListItemGroup input parameter start check failed."); + return; + } + if (!(changeObject->HasProperty("deleteCount"))) { + // If only input one parameter, set -1 to deleteCount for deleting elements after index 'start' in the array. + deleteCount = -1; + } else if (!JSViewAbstract::ParseJsInteger(changeObject->GetProperty("deleteCount"), deleteCount) || + deleteCount < 0) { + deleteCount = 0; + } + auto childrenSizeValue = changeObject->GetProperty("childrenSize"); + if (childrenSizeValue->IsArray()) { + auto childrenSize = JSRef::Cast(childrenSizeValue); + auto childrenSizeCount = childrenSize->Length(); + for (size_t j = 0; j < childrenSizeCount; ++j) { + double childSize = 0.0f; + if (!JSViewAbstract::ParseJsDouble(childrenSize->GetValueAt(j), childSize) || + !NonNegative(childSize) || std::isinf(childSize)) { + childSize = defaultSize; + } + newChildrenSize.emplace_back(Dimension(childSize, DimensionUnit::VP).ConvertToPx()); + } + } +} +} // namespace + +void JSListItemGroup::SetChildrenMainSize(const JSCallbackInfo& args) +{ + if (args.Length() != 1 || !(args[0]->IsObject())) { + return; + } + JSRef childrenSizeObj = JSRef::Cast(args[0]); + double defaultSize = 0.0f; + if (!ParseJsDouble(childrenSizeObj->GetProperty("defaultMainSize"), defaultSize) || !NonNegative(defaultSize)) { + LOGW("JSListItemGroup input parameter defaultSize check failed."); + return; + } + auto listChildrenMainSize = ListItemGroupModel::GetInstance()->GetOrCreateListChildrenMainSize(); + CHECK_NULL_VOID(listChildrenMainSize); + listChildrenMainSize->UpdateDefaultSize(Dimension(defaultSize, DimensionUnit::VP).ConvertToPx()); + + auto changes = childrenSizeObj->GetProperty("changeArray"); + if (!changes->IsArray()) { + return; + } + auto changeArray = JSRef::Cast(changes); + auto length = changeArray->Length(); + for (size_t i = 0; i < length; ++i) { + auto change = changeArray->GetValueAt(i); + auto changeObject = JSRef::Cast(change); + int32_t start = 0; + int32_t deleteCount = 0; + std::vector newChildrenSize; + ParseChange(changeObject, defaultSize, start, deleteCount, newChildrenSize); + listChildrenMainSize->ChangeData(start, deleteCount, newChildrenSize); + } + + auto clearFunc = childrenSizeObj->GetProperty("clearChanges"); + if (!clearFunc->IsFunction()) { + return; + } + auto func = JSRef::Cast(clearFunc); + JSRef::Cast(func->Call(childrenSizeObj)); +} + void JSListItemGroup::Create(const JSCallbackInfo& args) { V2::ListItemGroupStyle listItemGroupStyle = V2::ListItemGroupStyle::NONE; @@ -118,8 +188,9 @@ void JSListItemGroup::JSBind(BindingTarget globalObj) JSClass::Declare("ListItemGroup"); JSClass::StaticMethod("create", &JSListItemGroup::Create); - JSClass::StaticMethod("divider", &JSListItemGroup::SetDivider); JSClass::StaticMethod("aspectRatio", &JSListItemGroup::SetAspectRatio); + JSClass::StaticMethod("childrenMainSize", &JSListItemGroup::SetChildrenMainSize); + JSClass::StaticMethod("divider", &JSListItemGroup::SetDivider); JSClass::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onTouch", &JSInteractableView::JsOnTouch); diff --git a/frameworks/bridge/declarative_frontend/jsview/js_list_item_group.h b/frameworks/bridge/declarative_frontend/jsview/js_list_item_group.h index 027373fed3a7d06db42344c5d7ca2a9eaa9f20c0..f243346907d460875c1dec93ef15b8283cad39d7 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_list_item_group.h +++ b/frameworks/bridge/declarative_frontend/jsview/js_list_item_group.h @@ -28,6 +28,7 @@ public: static void Create(const JSCallbackInfo& args); static void SetDivider(const JSCallbackInfo& args); static void SetAspectRatio(const JSCallbackInfo& args); + static void SetChildrenMainSize(const JSCallbackInfo& args); }; } // namespace OHOS::Ace::Framework diff --git a/frameworks/core/components_ng/pattern/list/list_children_main_size.h b/frameworks/core/components_ng/pattern/list/list_children_main_size.h new file mode 100644 index 0000000000000000000000000000000000000000..5bf634e9967d658f8092a3d9036cd96c9b73b5d3 --- /dev/null +++ b/frameworks/core/components_ng/pattern/list/list_children_main_size.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_CHILDREN_MAIN_SIZE_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_CHILDREN_MAIN_SIZE_H + + +#include +#include +#include +#include +#include + +#include "base/geometry/dimension.h" +#include "core/components_ng/property/measure_property.h" + +namespace OHOS::Ace::NG { + +using ListChangeFlag = std::uint8_t; + +inline constexpr ListChangeFlag LIST_NO_CHANGE = 0; +inline constexpr ListChangeFlag LIST_UPDATE_DEFAULT_SIZE = 1; +inline constexpr ListChangeFlag LIST_UPDATE_CHILD_SIZE = 1 << 1; +inline constexpr ListChangeFlag LIST_UPDATE_LANES = 1 << 2; +inline constexpr ListChangeFlag LIST_UPDATE_SPACE = 1 << 3; +inline constexpr ListChangeFlag LIST_GROUP_UPDATE_HEADER_FOOTER = 1 << 4; + +namespace { +constexpr float DEFAULT_SIZE = -1.0f; +} + +class ListChildrenMainSize : public virtual AceType { + DECLARE_ACE_TYPE(ListChildrenMainSize, AceType) +public: + ListChildrenMainSize() = default; + ~ListChildrenMainSize() override = default; + + void SetOnDataChange(std::function, ListChangeFlag)>&& func) + { + onChildrenSizeChange_ = func; + } + + void ChangeData(const int32_t start, const int32_t deleteCount, const std::vector& newChildrenSize) + { + if (deleteCount == -1) { + childrenSize_.resize(start, DEFAULT_SIZE); + if (onChildrenSizeChange_) { + onChildrenSizeChange_(std::make_tuple(start, -1, -1), LIST_UPDATE_CHILD_SIZE); + } + return; + } + int32_t newChildrenSizeSize = static_cast(newChildrenSize.size()); + int32_t replaceCount = std::min(deleteCount, newChildrenSizeSize); + int32_t cursor = 0; + if (static_cast(childrenSize_.size()) < start + replaceCount) { + childrenSize_.resize(start + replaceCount, DEFAULT_SIZE); + } + for (; cursor < replaceCount; cursor++) { + childrenSize_[start + cursor] = newChildrenSize[cursor]; + } + if (deleteCount > newChildrenSizeSize) { + auto deleteStartPos = childrenSize_.begin() + start + cursor; + auto deleteEndPos = deleteStartPos; + int32_t needDeleteSpan = deleteCount - newChildrenSizeSize; + while (deleteEndPos != childrenSize_.end() && needDeleteSpan--) { + deleteEndPos++; + } + childrenSize_.erase(deleteStartPos, deleteEndPos); + } else if (deleteCount < newChildrenSizeSize) { + auto insertStartPos = childrenSize_.begin() + start + cursor; + childrenSize_.insert(insertStartPos, newChildrenSize.begin() + cursor, newChildrenSize.end()); + } + if (onChildrenSizeChange_) { + onChildrenSizeChange_(std::make_tuple(start, deleteCount, newChildrenSizeSize), LIST_UPDATE_CHILD_SIZE); + } + } + + void UpdateDefaultSize(float defaultSize) + { + if (NearEqual(defaultSize_, defaultSize)) { + return; + } + defaultSize_ = defaultSize; + if (onChildrenSizeChange_) { + // -1: default size changed. + onChildrenSizeChange_(std::make_tuple(-1, -1, -1), LIST_UPDATE_DEFAULT_SIZE); + } + } + + float GetChildSize(int32_t index) const + { + if (index > (static_cast(childrenSize_.size()) - 1) || index < 0 || + NearEqual(childrenSize_[index], DEFAULT_SIZE)) { + return defaultSize_; + } + return childrenSize_[index]; + } + + void ResizeChildrenSize(int32_t size) + { + childrenSize_.resize(std::max(size, 0), DEFAULT_SIZE); + } +private: + std::vector childrenSize_; + float defaultSize_ = 0.0f; + std::function, ListChangeFlag)> onChildrenSizeChange_; +}; + +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_CHILDREN_MAIN_SIZE_H \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/list/list_item_group_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/list/list_item_group_layout_algorithm.cpp index 278eb177c14a597f214bc3350a06e4c6ce328f5f..874d345524a6a05cb0b001ce928c27e7525a1db9 100644 --- a/frameworks/core/components_ng/pattern/list/list_item_group_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/list/list_item_group_layout_algorithm.cpp @@ -98,8 +98,14 @@ void ListItemGroupLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) } MeasureHeaderFooter(layoutWrapper); totalMainSize_ = std::max(totalMainSize_, headerMainSize_ + footerMainSize_); + if (childrenSize_) { + posMap_->UpdateGroupPosMap(totalItemCount_, GetLanes(), spaceWidth_, childrenSize_, + headerMainSize_, footerMainSize_); + totalMainSize_ = posMap_->GetTotalHeight(); + } MeasureListItem(layoutWrapper, childLayoutConstraint_); AdjustItemPosition(); + AdjustByPosMap(); SetActiveChildRange(layoutWrapper); auto crossSize = contentIdealSize.CrossSize(axis_); @@ -614,7 +620,7 @@ void ListItemGroupLayoutAlgorithm::MeasureStart(LayoutWrapper* layoutWrapper, } MeasureJumpToItemForward(layoutWrapper, layoutConstraint, startIndex, currentStartPos); - if (Positive(contentStartOffset_)) { + if (GreatNotEqual(currentStartPos, startPos_)) { MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, startIndex - 1, currentStartPos); } @@ -643,7 +649,7 @@ void ListItemGroupLayoutAlgorithm::MeasureEnd(LayoutWrapper* layoutWrapper, } MeasureJumpToItemBackward(layoutWrapper, layoutConstraint, endIndex, currentEndPos); - if (Positive(contentEndOffset_)) { + if (LessNotEqual(currentEndPos, endPos_)) { MeasureJumpToItemForward(layoutWrapper, layoutConstraint, endIndex + 1, currentEndPos); } @@ -725,6 +731,19 @@ void ListItemGroupLayoutAlgorithm::MeasureBackward(LayoutWrapper* layoutWrapper, } } +void ListItemGroupLayoutAlgorithm::AdjustByPosMap() +{ + if (!childrenSize_) { + return; + } + totalMainSize_ = posMap_->GetTotalHeight(); + float offset = posMap_->GetGroupLayoutOffset(GetStartIndex(), itemPosition_.begin()->second.startPos); + for (auto& pos : itemPosition_) { + pos.second.startPos += offset; + pos.second.endPos += offset; + } +} + void ListItemGroupLayoutAlgorithm::AdjustItemPosition() { if (itemPosition_.empty()) { diff --git a/frameworks/core/components_ng/pattern/list/list_item_group_layout_algorithm.h b/frameworks/core/components_ng/pattern/list/list_item_group_layout_algorithm.h index 442bac84739693f0fae34e7dc2f854dcb574eb16..09607981d33a256a4a75021162a37550f7462c4d 100644 --- a/frameworks/core/components_ng/pattern/list/list_item_group_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/list/list_item_group_layout_algorithm.h @@ -24,6 +24,8 @@ #include "core/components_v2/list/list_properties.h" namespace OHOS::Ace::NG { +class ListPositionMap; +class ListChildrenMainSize; struct LayoutedItemInfo { int32_t startIndex = 0; float startPos = 0.0f; @@ -210,6 +212,18 @@ public: return layoutedItemInfo_; } + void SetListChildrenMainSize(const RefPtr& childrenMainSize) + { + childrenSize_ = childrenMainSize; + } + + void SetListPositionMap(const RefPtr& posMap) + { + posMap_ = posMap; + } + + void AdjustByPosMap(); + static void SyncGeometry(RefPtr& wrapper); float GetStartHeaderPos() const @@ -279,6 +293,8 @@ private: float paddingAfterContent_ = 0.0f; PositionMap itemPosition_; + RefPtr childrenSize_; + RefPtr posMap_; Axis axis_ = Axis::VERTICAL; int32_t lanes_ = 1; float laneGutter_ = 0.0f; diff --git a/frameworks/core/components_ng/pattern/list/list_item_group_model.h b/frameworks/core/components_ng/pattern/list/list_item_group_model.h index 360b392dc54e030a99d2e66e4522e335a243d762..815878bd8cc0ad6a3b49fd87200a905fc06ffd27 100644 --- a/frameworks/core/components_ng/pattern/list/list_item_group_model.h +++ b/frameworks/core/components_ng/pattern/list/list_item_group_model.h @@ -23,6 +23,7 @@ #include "base/geometry/axis.h" #include "base/geometry/dimension.h" #include "core/components/common/layout/constants.h" +#include "core/components_ng/pattern/list/list_children_main_size.h" #include "core/components_v2/list/list_properties.h" namespace OHOS::Ace { @@ -37,6 +38,11 @@ public: virtual void SetDivider(const V2::ItemDivider& divider) = 0; virtual void SetHeader(std::function&& header) = 0; virtual void SetFooter(std::function&& footer) = 0; + virtual RefPtr GetOrCreateListChildrenMainSize() + { + return nullptr; + } + private: static std::unique_ptr instance_; diff --git a/frameworks/core/components_ng/pattern/list/list_item_group_model_ng.cpp b/frameworks/core/components_ng/pattern/list/list_item_group_model_ng.cpp index 813fc09d078d4c6524ed8b3aeb4db50548005172..216b88c3c6d693c85d38af7396fd64e2b17e1825 100644 --- a/frameworks/core/components_ng/pattern/list/list_item_group_model_ng.cpp +++ b/frameworks/core/components_ng/pattern/list/list_item_group_model_ng.cpp @@ -110,4 +110,12 @@ void ListItemGroupModelNG::SetFooter(FrameNode* frameNode, FrameNode* footerNode pattern->AddFooter(AceType::Claim(footerNode)); } +RefPtr ListItemGroupModelNG::GetOrCreateListChildrenMainSize() +{ + auto frameNode = ViewStackProcessor::GetInstance()->GetMainFrameNode(); + CHECK_NULL_RETURN(frameNode, nullptr); + auto pattern = frameNode->GetPattern(); + CHECK_NULL_RETURN(pattern, nullptr); + return pattern->GetOrCreateListChildrenMainSize(); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/list/list_item_group_model_ng.h b/frameworks/core/components_ng/pattern/list/list_item_group_model_ng.h index 06de469fd81cc470743fa2e31ba97d459932ba0e..645c79ea2ae994e93bb2f597ae72697512c98b16 100644 --- a/frameworks/core/components_ng/pattern/list/list_item_group_model_ng.h +++ b/frameworks/core/components_ng/pattern/list/list_item_group_model_ng.h @@ -34,6 +34,7 @@ public: void SetDivider(const V2::ItemDivider& divider) override; void SetHeader(std::function&& header) override; void SetFooter(std::function&& footer) override; + RefPtr GetOrCreateListChildrenMainSize() override; static void SetDivider(FrameNode* frameNode, const V2::ItemDivider& divider); static RefPtr CreateFrameNode(int32_t nodeId); diff --git a/frameworks/core/components_ng/pattern/list/list_item_group_pattern.cpp b/frameworks/core/components_ng/pattern/list/list_item_group_pattern.cpp index a2a25964c7494d9cf5ca90b548c3b759cd1c2989..65e461e14240b20617961b0d258b4e33fd701369 100644 --- a/frameworks/core/components_ng/pattern/list/list_item_group_pattern.cpp +++ b/frameworks/core/components_ng/pattern/list/list_item_group_pattern.cpp @@ -85,6 +85,13 @@ RefPtr ListItemGroupPattern::CreateLayoutAlgorithm() auto layoutAlgorithm = MakeRefPtr(headerIndex, footerIndex, itemStartIndex_); layoutAlgorithm->SetItemsPosition(itemPosition_); layoutAlgorithm->SetLayoutedItemInfo(layoutedItemInfo_); + if (childrenSize_ && ListChildrenSizeExist()) { + if (!posMap_) { + posMap_ = MakeRefPtr(); + } + layoutAlgorithm->SetListChildrenMainSize(childrenSize_); + layoutAlgorithm->SetListPositionMap(posMap_); + } return layoutAlgorithm; } @@ -174,6 +181,61 @@ void ListItemGroupPattern::CheckListDirectionInCardStyle() } } +RefPtr ListItemGroupPattern::GetListFrameNode() const +{ + auto host = GetHost(); + CHECK_NULL_RETURN(host, nullptr); + auto parent = host->GetParent(); + RefPtr frameNode = AceType::DynamicCast(parent); + while (parent && !frameNode) { + parent = parent->GetParent(); + frameNode = AceType::DynamicCast(parent); + } + return frameNode; +} + +bool ListItemGroupPattern::ListChildrenSizeExist() +{ + RefPtr listNode = GetListFrameNode(); + CHECK_NULL_RETURN(listNode, false); + auto listPattern = listNode->GetPattern(); + CHECK_NULL_RETURN(listPattern, false); + return listPattern->ListChildrenSizeExist(); +} + +RefPtr ListItemGroupPattern::GetOrCreateListChildrenMainSize() +{ + if (childrenSize_) { + return childrenSize_; + } + childrenSize_ = AceType::MakeRefPtr(); + auto callback = [weakPattern = WeakClaim(this)](std::tuple change, ListChangeFlag flag) { + auto pattern = weakPattern.Upgrade(); + CHECK_NULL_VOID(pattern); + auto context = PipelineContext::GetCurrentContext(); + CHECK_NULL_VOID(context); + context->AddBuildFinishCallBack([weakPattern, change, flag]() { + auto pattern = weakPattern.Upgrade(); + CHECK_NULL_VOID(pattern); + pattern->OnChildrenSizeChanged(change, flag); + }); + context->RequestFrame(); + }; + childrenSize_->SetOnDataChange(callback); + return childrenSize_; +} + +void ListItemGroupPattern::OnChildrenSizeChanged(std::tuple change, ListChangeFlag flag) +{ + if (!posMap_) { + posMap_ = MakeRefPtr(); + } + posMap_->MarkDirty(flag); + auto host = GetHost(); + CHECK_NULL_VOID(host); + host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST); +} + VisibleContentInfo ListItemGroupPattern::GetStartListItemIndex() { bool isHeader = false; diff --git a/frameworks/core/components_ng/pattern/list/list_item_group_pattern.h b/frameworks/core/components_ng/pattern/list/list_item_group_pattern.h index 8620d6cfd62aec34814be10870eab7ba28096bcb..d350dc82ae12d58ac62efb503613645a2dba4366 100644 --- a/frameworks/core/components_ng/pattern/list/list_item_group_pattern.h +++ b/frameworks/core/components_ng/pattern/list/list_item_group_pattern.h @@ -20,9 +20,11 @@ #include "base/utils/noncopyable.h" #include "base/utils/utils.h" #include "core/components_ng/pattern/list/list_item_group_accessibility_property.h" +#include "core/components_ng/pattern/list/list_children_main_size.h" #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h" #include "core/components_ng/pattern/list/list_item_group_layout_property.h" #include "core/components_ng/pattern/list/list_layout_property.h" +#include "core/components_ng/pattern/list/list_position_map.h" #include "core/components_ng/pattern/pattern.h" #include "core/components_ng/syntax/shallow_builder.h" @@ -191,6 +193,10 @@ public: } } + RefPtr GetOrCreateListChildrenMainSize(); + void OnChildrenSizeChanged(std::tuple change, ListChangeFlag flag); + bool ListChildrenSizeExist(); + RefPtr GetListFrameNode() const; VisibleContentInfo GetStartListItemIndex(); VisibleContentInfo GetEndListItemIndex(); @@ -205,6 +211,8 @@ private: void SetListItemGroupDefaultAttributes(const RefPtr& itemGroupNode); void CheckListDirectionInCardStyle(); RefPtr shallowBuilder_; + RefPtr posMap_; + RefPtr childrenSize_; V2::ListItemGroupStyle listItemGroupStyle_ = V2::ListItemGroupStyle::NONE; int32_t indexInList_ = 0; diff --git a/frameworks/core/components_ng/pattern/list/list_lanes_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/list/list_lanes_layout_algorithm.cpp index 00c9fbc3c232f07541b84161e11fdee39b4caa6c..c55e1b04e11be851058de4959c6a2d31b48b6889 100644 --- a/frameworks/core/components_ng/pattern/list/list_lanes_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/list/list_lanes_layout_algorithm.cpp @@ -49,7 +49,19 @@ void ListLanesLayoutAlgorithm::UpdateListItemConstraint( } } -float ListLanesLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex) +float ListLanesLayoutAlgorithm::GetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex) +{ + CHECK_NULL_RETURN(childrenSize_, 0.0f); + float mainLen = 0.0f; + int32_t laneCeil = GetLanesCeil(layoutWrapper, childIndex); + for (int32_t index = GetLanesFloor(layoutWrapper, childIndex); index <= laneCeil; index++) { + mainLen = std::max(mainLen, childrenSize_->GetChildSize(index)); + } + return mainLen; +} + +float ListLanesLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex, + bool groupLayoutAll) { auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex); CHECK_NULL_RETURN(wrapper, 0.0f); @@ -64,7 +76,7 @@ float ListLanesLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWr mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); } else { auto laneCeil = GetLanesCeil(layoutWrapper, childIndex); - for (int32_t i = childIndex; i <= laneCeil; i++) { + for (int32_t i = GetLanesFloor(layoutWrapper, childIndex); i <= laneCeil; i++) { auto wrapper = layoutWrapper->GetOrCreateChildByIndex(i); wrapper->Measure(childLayoutConstraint_); mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_)); @@ -110,7 +122,8 @@ int32_t ListLanesLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrappe } wrapper->Measure(childLayoutConstraint_); } - mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_)); + mainLen = std::max(mainLen, childrenSize_ ? childrenSize_->GetChildSize(currentIndex) : + GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_)); if (isGroup) { break; } @@ -123,8 +136,7 @@ int32_t ListLanesLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrappe SetItemInfo(currentIndex - i, { id, startPos, endPos, isGroup }); } } - float startIndex = GetLanesFloor(layoutWrapper, currentIndex); - OnItemPositionAddOrUpdate(layoutWrapper, startIndex); + OnItemPositionAddOrUpdate(layoutWrapper, GetLanesFloor(layoutWrapper, currentIndex)); return cnt; } @@ -170,7 +182,8 @@ int32_t ListLanesLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapp } wrapper->Measure(childLayoutConstraint_); } - mainLen = std::max(mainLen, GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_)); + mainLen = std::max(mainLen, childrenSize_ ? childrenSize_->GetChildSize(currentIndex) : + GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_)); if (isGroup || (currentIndex - FindLanesStartIndex(layoutWrapper, currentIndex)) % lanes == 0) { break; } diff --git a/frameworks/core/components_ng/pattern/list/list_lanes_layout_algorithm.h b/frameworks/core/components_ng/pattern/list/list_lanes_layout_algorithm.h index 8b21125bd21b2141767595f5ee9616cf5a987b75..08e45d44f994e48c230d854f0d8dcabf65c388dc 100644 --- a/frameworks/core/components_ng/pattern/list/list_lanes_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/list/list_lanes_layout_algorithm.h @@ -37,7 +37,10 @@ public: return lanes_; } - float MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex) override; + float MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex, + bool groupLayoutAll = true) override; + + float GetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex) override; static int32_t CalculateLanesParam(std::optional& minLaneLength, std::optional& maxLaneLength, int32_t lanes, std::optional crossSizeOptional, float laneGutter = 0.0f); diff --git a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp index 18dacca5342e8e60d2e0e07ddc12b52ab9396eb3..73002469fb9a534d3a67d883b1e3898a149d936a 100644 --- a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.cpp @@ -56,6 +56,24 @@ void ListLayoutAlgorithm::UpdateListItemConstraint( } } +void ListLayoutAlgorithm::ReviseSpace(const RefPtr& listLayoutProperty) +{ + if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, contentMainSize_)) { + spaceWidth_ = 0.0f; + } + if (listLayoutProperty->GetDivider().has_value()) { + auto divider = listLayoutProperty->GetDivider().value(); + std::optional dividerSpace = divider.strokeWidth.ConvertToPx(); + if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) { + dividerSpace.reset(); + } + if (dividerSpace.has_value()) { + spaceWidth_ = std::max(spaceWidth_, static_cast(Round(dividerSpace.value()))); + } + } + spaceWidth_ += chainInterval_; +} + void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) { auto listLayoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); @@ -92,6 +110,9 @@ void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) contentMainSize_ = 0.0f; totalItemCount_ = layoutWrapper->GetTotalChildCount(); isSnapCenter_ = IsScrollSnapAlignCenter(layoutWrapper); + if (childrenSize_) { + childrenSize_->ResizeChildrenSize(totalItemCount_); + } if (!GetMainAxisSize(contentIdealSize, axis_)) { if (totalItemCount_ == 0) { contentMainSize_ = 0.0f; @@ -119,20 +140,7 @@ void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) auto mainPercentRefer = GetMainAxisSize(childLayoutConstraint_.percentReference, axis_); auto space = listLayoutProperty->GetSpace().value_or(Dimension(0)); spaceWidth_ = ConvertToPx(space, layoutConstraint.scaleProperty, mainPercentRefer).value_or(0); - if (Negative(spaceWidth_) || GreatOrEqual(spaceWidth_, contentMainSize_)) { - spaceWidth_ = 0.0f; - } - if (listLayoutProperty->GetDivider().has_value()) { - auto divider = listLayoutProperty->GetDivider().value(); - std::optional dividerSpace = divider.strokeWidth.ConvertToPx(); - if (GreatOrEqual(dividerSpace.value(), contentMainSize_)) { - dividerSpace.reset(); - } - if (dividerSpace.has_value()) { - spaceWidth_ = std::max(spaceWidth_, static_cast(Round(dividerSpace.value()))); - } - } - spaceWidth_ += chainInterval_; + ReviseSpace(listLayoutProperty); CheckJumpToIndex(); currentOffset_ = currentDelta_; startMainPos_ = currentOffset_; @@ -141,9 +149,15 @@ void ListLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) listItemAlign_ = listLayoutProperty->GetListItemAlign().value_or(V2::ListItemAlign::START); // calculate child layout constraint. UpdateListItemConstraint(axis_, contentIdealSize, childLayoutConstraint_); + if (posMap_) { + posMap_->UpdatePosMap(layoutWrapper, GetLanes(), spaceWidth_, childrenSize_); + } MeasureList(layoutWrapper); } else { itemPosition_.clear(); + if (posMap_) { + posMap_->ClearPosMap(); + } } if (itemPosition_.empty()) { @@ -238,8 +252,8 @@ void ListLayoutAlgorithm::ClearAllItemPosition(LayoutWrapper* layoutWrapper) void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layoutWrapper) { - int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value()); - LayoutForward(layoutWrapper, index, startPos); + jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value()); + LayoutForward(layoutWrapper, jumpIndex_.value(), startPos); if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) { LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition()); if ((GetEndIndex() < totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) { @@ -250,8 +264,8 @@ void ListLayoutAlgorithm::BeginLayoutForward(float startPos, LayoutWrapper* layo void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper) { - int32_t index = GetLanesCeil(layoutWrapper, jumpIndex_.value()); - LayoutBackward(layoutWrapper, index, startPos); + jumpIndex_ = GetLanesCeil(layoutWrapper, jumpIndex_.value()); + LayoutBackward(layoutWrapper, jumpIndex_.value(), startPos); if (LessOrEqual(GetEndIndex(), totalItemCount_ - 1) && LessNotEqual(GetEndPosition(), endMainPos_)) { LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition()); if ((GetStartIndex() > 0) && GreatNotEqual(GetStartPosition(), startMainPos_)) { @@ -260,44 +274,18 @@ void ListLayoutAlgorithm::BeginLayoutBackward(float startPos, LayoutWrapper* lay } } -void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper, - int32_t& startIndex, int32_t& endIndex, float& startPos, float& endPos) +void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t endIndex) { - bool isSmoothJump = false; - int32_t jumpIndex = 0; - if (jumpIndex_.has_value()) { - jumpIndex = jumpIndex_.value(); - } else { - jumpIndex = targetIndex_.value(); - isSmoothJump = true; - } - int32_t tempStartIndex = startIndex; - int32_t tempEndIndex = endIndex; - if (GreatNotEqual(GetLanes(), 1)) { - jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex); - tempStartIndex = GetLanesFloor(layoutWrapper, tempStartIndex); - tempEndIndex = GetLanesFloor(layoutWrapper, tempEndIndex); - } + int32_t jumpIndex = jumpIndex_.has_value() ? jumpIndex_.value() : targetIndex_.value(); + jumpIndex = GetLanesFloor(layoutWrapper, jumpIndex); + startIndex = GetLanesFloor(layoutWrapper, startIndex); + endIndex = GetLanesFloor(layoutWrapper, endIndex); + float contentStartOffset = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_; + float contentEndOffset = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_; auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex); CHECK_NULL_VOID(wrapper); bool isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; - if (!isGroup) { - if (jumpIndex >= tempEndIndex) { - scrollAutoType_ = ScrollAutoType::END; - if (!isSmoothJump) { - jumpIndex_ = GetLanesCeil(layoutWrapper, jumpIndex_.value()); - startPos = contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_); - BeginLayoutBackward(startPos, layoutWrapper); - } - } else if (jumpIndex <= tempStartIndex) { - scrollAutoType_ = ScrollAutoType::START; - if (!isSmoothJump) { - jumpIndex_ = GetLanesFloor(layoutWrapper, jumpIndex_.value()); - startPos = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_; - BeginLayoutForward(startPos, layoutWrapper); - } - } - } else if (jumpIndexInGroup_) { + if (isGroup && jumpIndexInGroup_) { if (scrollAutoType_ == ScrollAutoType::START) { scrollAlign_ = ScrollAlign::START; HandleJumpStart(layoutWrapper); @@ -305,40 +293,32 @@ void ListLayoutAlgorithm::HandleJumpAuto(LayoutWrapper* layoutWrapper, scrollAlign_ = ScrollAlign::END; HandleJumpEnd(layoutWrapper); } - } else if (jumpIndex <= tempStartIndex) { - auto listLayoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - SetListItemGroupParam(wrapper, jumpIndex, contentMainSize_, false, listLayoutProperty, false); - wrapper->Measure(childLayoutConstraint_); - float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); - if (GreatNotEqual(contentMainSize_, mainLen)) { + } else if (jumpIndex <= startIndex) { + float mainLen = childrenSize_ ? + GetChildHeight(layoutWrapper, jumpIndex) : MeasureAndGetChildHeight(layoutWrapper, jumpIndex, false); + if (GreatNotEqual(contentMainSize_ - contentStartOffset - contentEndOffset, mainLen)) { scrollAutoType_ = ScrollAutoType::START; - if (!isSmoothJump) { - startPos = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_; - BeginLayoutForward(startPos, layoutWrapper); + if (jumpIndex_.has_value()) { + BeginLayoutForward(contentStartOffset, layoutWrapper); } } else { scrollAutoType_ = ScrollAutoType::END; - if (!isSmoothJump) { - startPos = contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_); - BeginLayoutBackward(startPos, layoutWrapper); + if (jumpIndex_.has_value()) { + BeginLayoutBackward(contentMainSize_ - contentEndOffset, layoutWrapper); } } - } else if (jumpIndex >= tempEndIndex) { - auto listLayoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - SetListItemGroupParam(wrapper, jumpIndex, 0.0f, false, listLayoutProperty, false); - wrapper->Measure(childLayoutConstraint_); - float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); - if (GreatOrEqual(mainLen, contentMainSize_)) { + } else if (jumpIndex >= endIndex) { + float mainLen = childrenSize_ ? + GetChildHeight(layoutWrapper, jumpIndex) : MeasureAndGetChildHeight(layoutWrapper, jumpIndex, false); + if (GreatOrEqual(mainLen, contentMainSize_ - contentStartOffset - contentEndOffset)) { scrollAutoType_ = ScrollAutoType::START; - if (!isSmoothJump) { - startPos = IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentStartOffset_; - BeginLayoutForward(startPos, layoutWrapper); + if (jumpIndex_.has_value()) { + BeginLayoutForward(contentStartOffset, layoutWrapper); } } else { scrollAutoType_ = ScrollAutoType::END; - if (!isSmoothJump) { - startPos = contentMainSize_ - (IsScrollSnapAlignCenter(layoutWrapper) ? 0.0f : contentEndOffset_); - BeginLayoutBackward(startPos, layoutWrapper); + if (jumpIndex_.has_value()) { + BeginLayoutBackward(contentMainSize_ - contentEndOffset, layoutWrapper); } } } @@ -360,7 +340,8 @@ void ListLayoutAlgorithm::HandleJumpCenter(LayoutWrapper* layoutWrapper) LayoutForward(layoutWrapper, index + 1, GetEndPosition()); } } else { - float mainLen = MeasureAndGetChildHeight(layoutWrapper, index); + float mainLen = childrenSize_ ? + GetChildHeight(layoutWrapper, index) : MeasureAndGetChildHeight(layoutWrapper, index); float startPos = (contentMainSize_ - mainLen) / 2.0f; if (LessNotEqual(startPos, endMainPos_)) { LayoutForward(layoutWrapper, index, startPos); @@ -376,18 +357,17 @@ void ListLayoutAlgorithm::HandleJumpCenter(LayoutWrapper* layoutWrapper) void ListLayoutAlgorithm::HandleJumpStart(LayoutWrapper* layoutWrapper) { - int32_t index = GetLanesFloor(layoutWrapper, jumpIndex_.value()); - auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index); + auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value()); bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; if (isGroup && jumpIndexInGroup_.has_value()) { int32_t indexInGroup = jumpIndexInGroup_.value(); auto listLayoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - SetListItemGroupParam(wrapper, index, 0.0f, true, listLayoutProperty, false); + SetListItemGroupParam(wrapper, jumpIndex_.value(), 0.0f, true, listLayoutProperty, false); wrapper->Measure(GetGroupLayoutConstraint()); - itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup); + itemPosition_[jumpIndex_.value()] = GetListItemGroupPosition(wrapper, indexInGroup); if (LessNotEqual(GetEndPosition(), endMainPos_)) { - LayoutForward(layoutWrapper, index + 1, GetEndPosition()); + LayoutForward(layoutWrapper, jumpIndex_.value() + 1, GetEndPosition()); } if (GetStartIndex() > 0 && GreatNotEqual(GetStartPosition(), startMainPos_)) { LayoutBackward(layoutWrapper, GetStartIndex() - 1, GetStartPosition()); @@ -399,18 +379,17 @@ void ListLayoutAlgorithm::HandleJumpStart(LayoutWrapper* layoutWrapper) void ListLayoutAlgorithm::HandleJumpEnd(LayoutWrapper* layoutWrapper) { - int32_t index = GetLanesCeil(layoutWrapper, jumpIndex_.value()); - auto wrapper = layoutWrapper->GetOrCreateChildByIndex(index); + auto wrapper = layoutWrapper->GetOrCreateChildByIndex(jumpIndex_.value()); bool isGroup = wrapper && wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; if (isGroup && jumpIndexInGroup_.has_value()) { int32_t indexInGroup = jumpIndexInGroup_.value(); auto listLayoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - SetListItemGroupParam(wrapper, index, contentMainSize_, true, listLayoutProperty, false); + SetListItemGroupParam(wrapper, jumpIndex_.value(), contentMainSize_, true, listLayoutProperty, false); wrapper->Measure(GetGroupLayoutConstraint()); - itemPosition_[index] = GetListItemGroupPosition(wrapper, indexInGroup); + itemPosition_[jumpIndex_.value()] = GetListItemGroupPosition(wrapper, indexInGroup); if (GreatNotEqual(GetStartPosition(), startMainPos_)) { - LayoutBackward(layoutWrapper, index - 1, GetStartPosition()); + LayoutBackward(layoutWrapper, jumpIndex_.value() - 1, GetStartPosition()); } if (GetEndIndex() <= totalItemCount_ -1 && LessNotEqual(GetEndPosition(), endMainPos_)) { LayoutForward(layoutWrapper, GetEndIndex() + 1, GetEndPosition()); @@ -574,7 +553,8 @@ bool ListLayoutAlgorithm::NoNeedJump(LayoutWrapper* layoutWrapper, float startPo return false; } -float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex) +float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex, + bool groupLayoutAll) { auto wrapper = layoutWrapper->GetOrCreateChildByIndex(childIndex); CHECK_NULL_RETURN(wrapper, 0.0f); @@ -582,8 +562,8 @@ float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper if (isGroup) { auto listLayoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - // true: layout forward, true: layout all group items. - SetListItemGroupParam(wrapper, childIndex, 0.0f, true, listLayoutProperty, true); + // true: layout forward, 0.0f: layout start position. + SetListItemGroupParam(wrapper, childIndex, 0.0f, true, listLayoutProperty, groupLayoutAll); } wrapper->Measure(childLayoutConstraint_); float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); @@ -592,7 +572,7 @@ float ListLayoutAlgorithm::MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper void ListLayoutAlgorithm::CheckJumpToIndex() { - if (jumpIndex_.has_value() || !isNeedCheckOffset_) { + if (jumpIndex_.has_value() || !isNeedCheckOffset_ || childrenSize_) { return; } if (LessOrEqual(std::abs(currentDelta_), contentMainSize_ * 2.0f) || itemPosition_.empty()) { @@ -676,13 +656,9 @@ void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper) float endPos = 0.0f; float itemTotalSize = 0.0f; float jumpIndexStartPos = 0.0f; - int32_t jumpIndex = 0; if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO) { - if (jumpIndex_.has_value()) { - jumpIndex = jumpIndex_.value(); - } - auto it = itemPosition_.find(jumpIndex); + auto it = itemPosition_.find(jumpIndex_.value()); if (it != itemPosition_.end()) { jumpIndexStartPos = it->second.startPos; } @@ -727,7 +703,7 @@ void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper) itemPosition_.clear(); } if (jumpIndex_ && scrollAlign_ == ScrollAlign::AUTO && - NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex, jumpIndexStartPos)) { + NoNeedJump(layoutWrapper, startPos, endPos, startIndex, endIndex, jumpIndex_.value(), jumpIndexStartPos)) { jumpIndex_.reset(); jumpIndexInGroup_.reset(); } @@ -744,7 +720,7 @@ void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper) HandleJumpEnd(layoutWrapper); break; case ScrollAlign::AUTO: - HandleJumpAuto(layoutWrapper, startIndex, endIndex, startPos, endPos); + HandleJumpAuto(layoutWrapper, startIndex, endIndex); break; } needEstimateOffset_ = true; @@ -765,7 +741,8 @@ void ListLayoutAlgorithm::MeasureList(LayoutWrapper* layoutWrapper) bool overScrollTop = startIndex == 0 && GreatNotEqual(startPos, startMainPos_ + contentStartOffset_); float midItemHeight = 0.0f; if (IsScrollSnapAlignCenter(layoutWrapper)) { - midItemHeight = MeasureAndGetChildHeight(layoutWrapper, midIndex); + midItemHeight = childrenSize_ ? + GetChildHeight(layoutWrapper, midIndex) : MeasureAndGetChildHeight(layoutWrapper, midIndex); } if (NearZero(currentOffset_) || (!overScrollFeature_ && NonNegative(currentOffset_)) || (overScrollFeature_ && overScrollTop) || @@ -820,7 +797,8 @@ int32_t ListLayoutAlgorithm::LayoutALineForward(LayoutWrapper* layoutWrapper, ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex); wrapper->Measure(childLayoutConstraint_); } - float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); + float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex) : + GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); endPos = startPos + mainLen; itemPosition_[currentIndex] = { id, startPos, endPos, isGroup }; OnItemPositionAddOrUpdate(layoutWrapper, currentIndex); @@ -847,7 +825,8 @@ int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper, ACE_SCOPED_TRACE("ListLayoutAlgorithm::MeasureListItem:%d", currentIndex); wrapper->Measure(childLayoutConstraint_); } - float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); + float mainLen = childrenSize_ ? childrenSize_->GetChildSize(currentIndex) : + GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis_); startPos = endPos - mainLen; itemPosition_[currentIndex] = { id, startPos, endPos, isGroup }; OnItemPositionAddOrUpdate(layoutWrapper, currentIndex); @@ -856,6 +835,9 @@ int32_t ListLayoutAlgorithm::LayoutALineBackward(LayoutWrapper* layoutWrapper, void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t startIndex, float startPos) { + if (childrenSize_ && itemPosition_.empty() && !overScrollFeature_ && !jumpIndex_ && !targetIndex_) { + posMap_->OptimizeBeforeMeasure(startIndex, startPos, currentOffset_, contentMainSize_); + } float currentEndPos = startPos; float currentStartPos = 0.0f; float endMainPos = overScrollFeature_ ? @@ -882,7 +864,6 @@ void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t st targetIndex_.reset(); } } while (LessNotEqual(currentEndPos + chainOffset, endMainPos)); - currentEndPos += chainOffset; // adjust offset. UpdateSnapCenterContentOffset(layoutWrapper); @@ -933,6 +914,9 @@ void ListLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, int32_t st void ListLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, int32_t endIndex, float endPos) { + if (childrenSize_ && itemPosition_.empty() && !overScrollFeature_ && !jumpIndex_ && !targetIndex_) { + posMap_->OptimizeBeforeMeasure(endIndex, endPos, currentOffset_, contentMainSize_); + } float currentStartPos = endPos; float currentEndPos = 0.0f; float startMainPos = overScrollFeature_ ? @@ -1416,6 +1400,9 @@ void ListLayoutAlgorithm::AdjustPostionForListItemGroup(LayoutWrapper* layoutWra } itemGroup->SetScrollAlign(ScrollAlign::NONE); wrapper->Measure(GetGroupLayoutConstraint()); + if (childrenSize_) { + return; + } float mainLen = GetMainAxisSize(wrapper->GetGeometryNode()->GetMarginFrameSize(), axis); auto& pos = itemPosition_[index]; if (forwardLayout) { diff --git a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h index a5b23d11e42d79f14792eabd78b1d0be40be3f78..b4cdc16293eb96088aee3db3796bb1f42200fa11 100644 --- a/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/list/list_layout_algorithm.h @@ -24,11 +24,13 @@ #include "core/components_ng/layout/layout_algorithm.h" #include "core/components_ng/layout/layout_wrapper.h" #include "core/components_ng/pattern/list/list_layout_property.h" +#include "core/components_ng/pattern/list/list_position_map.h" #include "core/components_v2/list/list_component.h" #include "core/components_v2/list/list_properties.h" namespace OHOS::Ace::NG { class PipelineContext; +class ListPositionMap; struct ListItemInfo { int32_t id; @@ -268,8 +270,7 @@ public: void BeginLayoutBackward(float startPos, LayoutWrapper* layoutWrapper); - void HandleJumpAuto(LayoutWrapper* layoutWrapper, - int32_t& startIndex, int32_t& endIndex, float& startPos, float& endPos); + void HandleJumpAuto(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t endIndex); void HandleJumpCenter(LayoutWrapper* layoutWrapper); @@ -286,7 +287,13 @@ public: bool CheckNoNeedJumpListItemGroup(LayoutWrapper* layoutWrapper, int32_t startIndex, int32_t endIndex, int32_t jumpIndex, float jumpIndexStartPos); - virtual float MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex); + virtual float MeasureAndGetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex, + bool groupLayoutAll = true); + + virtual float GetChildHeight(LayoutWrapper* layoutWrapper, int32_t childIndex) + { + return childrenSize_->GetChildSize(childIndex); + } virtual int32_t GetLanes() const { @@ -328,6 +335,16 @@ public: void OnItemPositionAddOrUpdate(LayoutWrapper* layoutWrapper, uint32_t index); + void SetListChildrenMainSize(const RefPtr& childrenMainSize) + { + childrenSize_ = childrenMainSize; + } + + void SetListPositionMap(const RefPtr& posMap) + { + posMap_ = posMap; + } + protected: virtual void UpdateListItemConstraint( Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint); @@ -363,9 +380,12 @@ protected: static void SyncGeometry(RefPtr& wrapper); ListItemInfo GetListItemGroupPosition(const RefPtr& layoutWrapper, int32_t index); bool CheckNeedMeasure(const RefPtr& layoutWrapper) const; + void ReviseSpace(const RefPtr& listLayoutProperty); Axis axis_ = Axis::VERTICAL; LayoutConstraintF childLayoutConstraint_; + RefPtr childrenSize_; + RefPtr posMap_; private: void MeasureList(LayoutWrapper* layoutWrapper); void CheckJumpToIndex(); diff --git a/frameworks/core/components_ng/pattern/list/list_model.h b/frameworks/core/components_ng/pattern/list/list_model.h index d7032304d2f73042da9b3a4203897eb7b991918f..69a4ebf68b45a4c8e943918d8d160dde64382c29 100644 --- a/frameworks/core/components_ng/pattern/list/list_model.h +++ b/frameworks/core/components_ng/pattern/list/list_model.h @@ -22,6 +22,7 @@ #include "base/geometry/axis.h" #include "base/geometry/dimension.h" #include "core/components/common/layout/constants.h" +#include "core/components_ng/pattern/list/list_children_main_size.h" #include "core/components_ng/pattern/list/list_event_hub.h" #include "core/components_v2/list/list_properties.h" @@ -80,6 +81,10 @@ public: virtual void SetOnItemDrop(OnItemDropFunc&& onItemDrop) = 0; virtual void SetScrollSnapAlign(V2::ScrollSnapAlign scrollSnapAlign) {}; virtual void SetFadingEdge(bool fadingEdge) = 0; + virtual RefPtr GetOrCreateListChildrenMainSize() + { + return nullptr; + } virtual DisplayMode GetDisplayMode() const = 0; diff --git a/frameworks/core/components_ng/pattern/list/list_model_ng.cpp b/frameworks/core/components_ng/pattern/list/list_model_ng.cpp index adc88580fb63f129e2973ef24f6c404f5917a344..f5109664de8adb29fc514e69b240939ad8330142 100644 --- a/frameworks/core/components_ng/pattern/list/list_model_ng.cpp +++ b/frameworks/core/components_ng/pattern/list/list_model_ng.cpp @@ -678,4 +678,12 @@ void ListModelNG::SetOnScrollStop(FrameNode* frameNode, OnScrollStopEvent&& onSc eventHub->SetOnScrollStop(std::move(onScrollStop)); } +RefPtr ListModelNG::GetOrCreateListChildrenMainSize() +{ + auto frameNode = ViewStackProcessor::GetInstance()->GetMainFrameNode(); + CHECK_NULL_RETURN(frameNode, nullptr); + auto pattern = frameNode->GetPattern(); + CHECK_NULL_RETURN(pattern, nullptr); + return pattern->GetOrCreateListChildrenMainSize(); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/list/list_model_ng.h b/frameworks/core/components_ng/pattern/list/list_model_ng.h index 919542d6f348523a504d6f598637990ab28d55ad..328a602eda537a1e8ce0bcbe5f2829d3217ec4c5 100644 --- a/frameworks/core/components_ng/pattern/list/list_model_ng.h +++ b/frameworks/core/components_ng/pattern/list/list_model_ng.h @@ -70,6 +70,7 @@ public: void SetOnItemDragMove(OnItemDragMoveFunc&& onItemDragMove) override; void SetOnItemDrop(OnItemDropFunc&& onItemDrop) override; void SetFadingEdge(bool fadingEdge) override; + RefPtr GetOrCreateListChildrenMainSize() override; DisplayMode GetDisplayMode() const override; static RefPtr CreateFrameNode(int32_t nodeId); diff --git a/frameworks/core/components_ng/pattern/list/list_pattern.cpp b/frameworks/core/components_ng/pattern/list/list_pattern.cpp index 0e2068a692405000c2b4c9543ff3277e4d5662d4..e44141597b22ff635958ffe4b0b55e633d87d639 100644 --- a/frameworks/core/components_ng/pattern/list/list_pattern.cpp +++ b/frameworks/core/components_ng/pattern/list/list_pattern.cpp @@ -135,7 +135,6 @@ bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dirty, c if (config.skipMeasure && config.skipLayout) { return false; } - bool isJump = false; auto layoutAlgorithmWrapper = DynamicCast(dirty->GetLayoutAlgorithm()); CHECK_NULL_RETURN(layoutAlgorithmWrapper, false); auto listLayoutAlgorithm = DynamicCast(layoutAlgorithmWrapper->GetLayoutAlgorithm()); @@ -146,17 +145,23 @@ bool ListPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dirty, c float relativeOffset = listLayoutAlgorithm->GetCurrentOffset(); auto predictSnapOffset = listLayoutAlgorithm->GetPredictSnapOffset(); auto predictSnapEndPos = listLayoutAlgorithm->GetPredictSnapEndPosition(); - if (listLayoutAlgorithm->NeedEstimateOffset() || needReEstimateOffset_) { - lanes_ = listLayoutAlgorithm->GetLanes(); - auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis()); - calculate.GetEstimateHeightAndOffset(GetHost()); - currentOffset_ = calculate.GetEstimateOffset(); - isJump = true; - relativeOffset = 0.0f; - needReEstimateOffset_ = false; - posMap_.clear(); + bool isJump = listLayoutAlgorithm->NeedEstimateOffset(); + if (childrenSize_) { + listTotalHeight_ = posMap_->GetTotalHeight(); + currentOffset_ = itemPosition_.empty() ? 0.0f : + posMap_->GetPos(itemPosition_.begin()->first, itemPosition_.begin()->second.startPos); + } else { + if (isJump || needReEstimateOffset_) { + lanes_ = listLayoutAlgorithm->GetLanes(); + auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis()); + calculate.GetEstimateHeightAndOffset(GetHost()); + currentOffset_ = calculate.GetEstimateOffset(); + relativeOffset = 0.0f; + needReEstimateOffset_ = false; + posMap_->ClearPosMap(); + } + CalculateCurrentOffset(relativeOffset); } - CalculateCurrentOffset(relativeOffset); if (targetIndex_) { AnimateToTarget(targetIndex_.value(), targetIndexInGroup_, scrollAlign_); targetIndex_.reset(); @@ -599,6 +604,13 @@ RefPtr ListPattern::CreateLayoutAlgorithm() } else { listLayoutAlgorithm.Swap(MakeRefPtr()); } + if (!posMap_) { + posMap_ = MakeRefPtr(); + } + if (childrenSize_) { + listLayoutAlgorithm->SetListChildrenMainSize(childrenSize_); + listLayoutAlgorithm->SetListPositionMap(posMap_); + } if (!isInitialized_) { jumpIndex_ = listLayoutProperty->GetInitialIndex().value_or(0); if (NeedScrollSnapAlignEffect()) { @@ -633,6 +645,7 @@ RefPtr ListPattern::CreateLayoutAlgorithm() listLayoutAlgorithm->SetCanOverScroll(CanOverScroll(GetScrollSource())); if (chainAnimation_) { SetChainAnimationLayoutAlgorithm(listLayoutAlgorithm, listLayoutProperty); + SetChainAnimationToPosMap(); } if (predictSnapEndPos_.has_value()) { listLayoutAlgorithm->SetPredictSnapEndPosition(predictSnapEndPos_.value()); @@ -640,6 +653,16 @@ RefPtr ListPattern::CreateLayoutAlgorithm() return listLayoutAlgorithm; } +void ListPattern::SetChainAnimationToPosMap() +{ + CHECK_NULL_VOID(posMap_); + posMap_->SetChainOffsetCallback([weak = AceType::WeakClaim(this)](int32_t index) { + auto list = weak.Upgrade(); + CHECK_NULL_RETURN(list, 0.0f); + return list->GetChainDelta(index); + }); +} + void ListPattern::SetChainAnimationLayoutAlgorithm( RefPtr listLayoutAlgorithm, RefPtr listLayoutProperty) { @@ -1434,7 +1457,7 @@ bool ListPattern::GetListItemAnimatePos(float startPos, float endPos, ScrollAlig case ScrollAlign::START: case ScrollAlign::NONE: targetPos = startPos; - if (!IsScrollSnapAlignCenter()) { + if (!IsScrollSnapAlignCenter() || childrenSize_) { targetPos -= contentStartOffset_; } break; @@ -1443,7 +1466,7 @@ bool ListPattern::GetListItemAnimatePos(float startPos, float endPos, ScrollAlig break; case ScrollAlign::END: targetPos = endPos - contentMainSize_; - if (!IsScrollSnapAlignCenter()) { + if (!IsScrollSnapAlignCenter() || childrenSize_) { targetPos += contentEndOffset_; } break; @@ -1476,7 +1499,7 @@ bool ListPattern::GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index, f return false; } targetPos = startPos; - if (!IsScrollSnapAlignCenter()) { + if (!IsScrollSnapAlignCenter() || childrenSize_) { targetPos -= contentStartOffset_; } break; @@ -1491,7 +1514,7 @@ bool ListPattern::GetListItemGroupAnimatePosWithoutIndexInGroup(int32_t index, f return false; } targetPos = endPos - contentMainSize_; - if (!IsScrollSnapAlignCenter()) { + if (!IsScrollSnapAlignCenter() || childrenSize_) { targetPos += contentEndOffset_; } break; @@ -1541,7 +1564,7 @@ bool ListPattern::GetListItemGroupAnimatePosWithIndexInGroup(int32_t index, int3 if (stickyStyle == V2::StickyStyle::HEADER || stickyStyle == V2::StickyStyle::BOTH) { targetPos -= groupPattern->GetHeaderMainSize(); } - if (!IsScrollSnapAlignCenter()) { + if (!IsScrollSnapAlignCenter() || childrenSize_) { targetPos -= contentStartOffset_; } break; @@ -1555,7 +1578,7 @@ bool ListPattern::GetListItemGroupAnimatePosWithIndexInGroup(int32_t index, int3 if (stickyStyle == V2::StickyStyle::FOOTER || stickyStyle == V2::StickyStyle::BOTH) { targetPos += groupPattern->GetFooterMainSize(); } - if (!IsScrollSnapAlignCenter()) { + if (!IsScrollSnapAlignCenter() || childrenSize_) { targetPos += contentEndOffset_; } break; @@ -1715,77 +1738,18 @@ Rect ListPattern::GetItemRectInGroup(int32_t index, int32_t indexInGroup) const groupItemGeometry->GetFrameRect().Width(), groupItemGeometry->GetFrameRect().Height()); } -void ListPattern::UpdatePosMapStart(float delta) -{ - currentOffset_ += delta; - if (itemPosition_.empty()) { - return; - } - - int32_t startIndex = itemPosition_.begin()->first; - if (startIndex == 0) { - currentOffset_ = -itemPosition_.begin()->second.startPos; - return; - } - auto it = posMap_.find(startIndex); - if (it == posMap_.begin() || it == posMap_.end()) { - return; - } - float startPos = it->second.mainPos; - it--; - float prevPos = it->second.mainPos + it->second.mainSize + spaceWidth_; - int32_t prevIndex = it->first; - if (prevIndex + 1 >= startIndex) { - if (NearEqual(prevPos, startPos)) { - return; - } - } else { - if (LessNotEqual(prevPos, startPos)) { - return; - } - } - currentOffset_ += prevPos - startPos; -} - -void ListPattern::UpdatePosMapEnd() +void ListPattern::CalculateCurrentOffset(float delta) { if (itemPosition_.empty()) { return; } - int32_t prevIndex = itemPosition_.rbegin()->first; - auto it = posMap_.find(prevIndex); - if (it == posMap_.end()) { - return; - } - float prevPos = it->second.mainPos + it->second.mainSize + spaceWidth_; - it++; - if (it == posMap_.end()) { - return; - } - if (prevIndex + 1 >= it->first) { - if (NearEqual(prevPos, it->second.mainPos)) { - return; - } - } else { - if (LessNotEqual(prevPos, it->second.mainPos)) { - return; - } - } - float delta = prevPos - it->second.mainPos; - while (it != posMap_.end()) { - it->second.mainPos += delta; - it++; - } -} - -void ListPattern::CalculateCurrentOffset(float delta) -{ - UpdatePosMapStart(delta); + posMap_->UpdatePosMapStart(delta, currentOffset_, spaceWidth_, + itemPosition_.begin()->first, itemPosition_.begin()->second.startPos); for (auto& [index, pos] : itemPosition_) { float height = pos.endPos - pos.startPos; - posMap_[index] = { currentOffset_ + pos.startPos, height }; + posMap_->UpdatePos(index, { currentOffset_ + pos.startPos, height }); } - UpdatePosMapEnd(); + posMap_->UpdatePosMapEnd(itemPosition_.rbegin()->first, spaceWidth_); } void ListPattern::UpdateScrollBarOffset() @@ -1796,14 +1760,21 @@ void ListPattern::UpdateScrollBarOffset() if (!GetScrollBar() && !GetScrollBarProxy()) { return; } - auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis()); - calculate.GetEstimateHeightAndOffset(GetHost()); - float currentOffset = calculate.GetEstimateOffset(); - float estimatedHeight = calculate.GetEstimateHeight(); + float currentOffset = 0.0f; + float estimatedHeight = 0.0f; + if (childrenSize_) { + currentOffset = currentOffset_; + estimatedHeight = listTotalHeight_; + } else { + auto calculate = ListHeightOffsetCalculator(itemPosition_, spaceWidth_, lanes_, GetAxis()); + calculate.GetEstimateHeightAndOffset(GetHost()); + currentOffset = calculate.GetEstimateOffset(); + estimatedHeight = calculate.GetEstimateHeight(); + } if (GetAlwaysEnabled()) { estimatedHeight = estimatedHeight - spaceWidth_; } - if (!IsScrollSnapAlignCenter()) { + if (!IsScrollSnapAlignCenter() || childrenSize_) { currentOffset += contentStartOffset_; estimatedHeight += contentStartOffset_ + contentEndOffset_; } @@ -2397,4 +2368,36 @@ void ListPattern::UpdateFrameSizeToWeb() } } } + +RefPtr ListPattern::GetOrCreateListChildrenMainSize() +{ + if (childrenSize_) { + return childrenSize_; + } + childrenSize_ = AceType::MakeRefPtr(); + auto callback = + [weakPattern = WeakClaim(this)](std::tuple change, ListChangeFlag flag) { + auto pattern = weakPattern.Upgrade(); + CHECK_NULL_VOID(pattern); + auto context = PipelineContext::GetCurrentContext(); + CHECK_NULL_VOID(context); + context->AddBuildFinishCallBack([weakPattern, change, flag]() { + auto pattern = weakPattern.Upgrade(); + CHECK_NULL_VOID(pattern); + pattern->OnChildrenSizeChanged(change, flag); + }); + context->RequestFrame(); + }; + childrenSize_->SetOnDataChange(callback); + return childrenSize_; +} + +void ListPattern::OnChildrenSizeChanged(std::tuple change, ListChangeFlag flag) +{ + if (!posMap_) { + posMap_ = MakeRefPtr(); + } + posMap_->MarkDirty(flag); + MarkDirtyNodeSelf(); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/list/list_pattern.h b/frameworks/core/components_ng/pattern/list/list_pattern.h index 50ffa63681b821c478edffe84ae0a6201f7227d8..6553f15d64b5cfcda04029d2aeacf2f14404b5a6 100644 --- a/frameworks/core/components_ng/pattern/list/list_pattern.h +++ b/frameworks/core/components_ng/pattern/list/list_pattern.h @@ -16,14 +16,17 @@ #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_PATTERN_H #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_PATTERN_H +#include #include "core/animation/chain_animation.h" #include "core/components_ng/pattern/list/list_accessibility_property.h" +#include "core/components_ng/pattern/list/list_children_main_size.h" #include "core/components_ng/pattern/list/list_content_modifier.h" #include "core/components_ng/pattern/list/list_event_hub.h" #include "core/components_ng/pattern/list/list_item_pattern.h" #include "core/components_ng/pattern/list/list_layout_algorithm.h" #include "core/components_ng/pattern/list/list_layout_property.h" #include "core/components_ng/pattern/list/list_paint_method.h" +#include "core/components_ng/pattern/list/list_position_map.h" #include "core/components_ng/pattern/scroll/inner/scroll_bar.h" #include "core/components_ng/pattern/scroll_bar/proxy/scroll_bar_proxy.h" #include "core/components_ng/pattern/scrollable/scrollable_pattern.h" @@ -253,11 +256,13 @@ public: } } + RefPtr GetOrCreateListChildrenMainSize(); + void OnChildrenSizeChanged(std::tuple change, ListChangeFlag flag); + bool ListChildrenSizeExist() + { + return static_cast(childrenSize_); + } private: - struct PositionInfo { - float mainPos; - float mainSize; - }; bool IsNeedInitClickEventRecorder() const override { @@ -295,6 +300,7 @@ private: void StartDefaultOrCustomSpringMotion(float start, float end, const RefPtr& curve); void UpdateScrollSnap(); bool IsScrollSnapAlignCenter() const; + void SetChainAnimationToPosMap(); void SetChainAnimationLayoutAlgorithm( RefPtr listLayoutAlgorithm, RefPtr listLayoutProperty); bool NeedScrollSnapAlignEffect() const; @@ -361,7 +367,9 @@ private: bool isNeedCheckOffset_ = false; ListLayoutAlgorithm::PositionMap itemPosition_; - std::map posMap_; + RefPtr posMap_; + RefPtr childrenSize_; + float listTotalHeight_ = 0.0f; std::map lanesItemRange_; std::set pressedItem_; diff --git a/frameworks/core/components_ng/pattern/list/list_position_map.h b/frameworks/core/components_ng/pattern/list/list_position_map.h new file mode 100644 index 0000000000000000000000000000000000000000..cd26e2042aba21caf06d445d6c3572e1552b1367 --- /dev/null +++ b/frameworks/core/components_ng/pattern/list/list_position_map.h @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_POSITION_MAP_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_POSITION_MAP_H + + +#include +#include +#include +#include +#include +#include + +#include "base/geometry/dimension.h" +#include "base/memory/referenced.h" +#include "base/utils/utils.h" +#include "core/components_ng/base/ui_node.h" +#include "core/components_ng/syntax/lazy_for_each_node.h" +#include "core/components_ng/pattern/list/list_children_main_size.h" +#include "core/components_ng/pattern/list/list_item_group_pattern.h" +#include "core/components_ng/pattern/list/list_layout_algorithm.h" +#include "core/components_ng/property/measure_property.h" + + +namespace OHOS::Ace::NG { +class ListItemGroupPattern; +namespace { + +struct PositionInfo { + float mainPos; + float mainSize; +}; + +enum class ListPosMapUpdate { + NO_CHANGE = 0, + UPDATE_ALL_SIZE, + RE_CALCULATE, +}; +} + +class ListPositionMap : public virtual AceType { + DECLARE_ACE_TYPE(ListPositionMap, AceType) +public: + ListPositionMap() = default; + ~ListPositionMap() override = default; + + void UpdatePos(int32_t index, PositionInfo posInfo) + { + posMap_[index] = { posInfo.mainPos, posInfo.mainSize }; + } + + void ClearPosMap() + { + posMap_.clear(); + } + + void MarkDirty(ListChangeFlag flag) + { + dirty_ = dirty_ | flag; + } + + void ClearDirty() + { + dirty_ = LIST_NO_CHANGE; + } + + float GetTotalHeight() + { + return totalHeight_; + } + + ListPosMapUpdate CheckPosMapUpdateRule() + { + ListPosMapUpdate flag; + if (dirty_ == LIST_NO_CHANGE) { + flag = ListPosMapUpdate::NO_CHANGE; + } else if (0 == (dirty_ & (LIST_UPDATE_CHILD_SIZE | LIST_UPDATE_LANES | LIST_GROUP_UPDATE_HEADER_FOOTER))) { + flag = ListPosMapUpdate::UPDATE_ALL_SIZE; + } else { + flag = ListPosMapUpdate::RE_CALCULATE; + } + return flag; + } + + void UpdatePosMapStart(float delta, float& listCurrentPos, float space, int32_t startIndex, float startPos) + { + listCurrentPos += delta; + if (startIndex == 0) { + listCurrentPos = -startPos; + return; + } + auto it = posMap_.find(startIndex); + if (it == posMap_.begin() || it == posMap_.end()) { + return; + } + it--; + float prevPos = it->second.mainPos + it->second.mainSize + space; + int32_t prevIndex = it->first; + if (prevIndex + 1 >= startIndex) { + if (NearEqual(prevPos, startPos)) { + return; + } + } else { + if (LessNotEqual(prevPos, startPos)) { + return; + } + } + listCurrentPos += prevPos - startPos; + } + + void UpdatePosMapEnd(int32_t prevEndIndex, float space) + { + auto it = posMap_.find(prevEndIndex); + if (it == posMap_.end()) { + return; + } + float prevPos = it->second.mainPos + it->second.mainSize + space; + it++; + if (it == posMap_.end()) { + return; + } + if (prevEndIndex + 1 >= it->first) { + if (NearEqual(prevPos, it->second.mainPos)) { + return; + } + } else { + if (LessNotEqual(prevPos, it->second.mainPos)) { + return; + } + } + float delta = prevPos - it->second.mainPos; + while (it != posMap_.end()) { + it->second.mainPos += delta; + it++; + } + } + + void CalculateUINode(RefPtr node) + { + CHECK_NULL_VOID(node); + auto children = node->GetChildren(); + for (const auto& child : children) { + if (AceType::InstanceOf(child)) { + auto frameNode = AceType::DynamicCast(child); + CalculateFrameNode(frameNode); + } else if (AceType::InstanceOf(child)) { + auto lazyForEach = AceType::DynamicCast(child); + // Rules: only one type node(ListItem or ListItemGroup) can exist in LazyForEach. + CalculateLazyForEachNode(lazyForEach); + } else { + CalculateUINode(child); + } + } + } + + RefPtr GetListFrameNode(RefPtr node) const + { + auto parent = node->GetParent(); + RefPtr frameNode = AceType::DynamicCast(parent); + while (parent && (!frameNode)) { + parent = parent->GetParent(); + frameNode = AceType::DynamicCast(parent); + } + return frameNode; + } + + std::optional GetLazyForEachChildIsGroup(RefPtr node) + { + std::optional isGroup; + auto children = node->GetChildren(); + if (children.size() > 0) { + auto child = children.begin(); + while (child != children.end() && !((*child)->GetFrameChildByIndex(0, false))) { + child++; + } + auto frameNode = AceType::DynamicCast((*child)->GetFrameChildByIndex(0, false)); + if (frameNode) { + isGroup = frameNode->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; + } + } + if (!(isGroup.has_value())) { + auto listNode = GetListFrameNode(node); + CHECK_NULL_RETURN(listNode, isGroup); + auto wrapper = listNode->GetOrCreateChildByIndex(curIndex_); + CHECK_NULL_RETURN(wrapper, isGroup); + isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG; + } + return isGroup; + } + + void CalculateLazyForEachNode(RefPtr node) + { + int32_t count = node->FrameCount(); + if (count <= 0) { + return; + } + std::optional isGroup = GetLazyForEachChildIsGroup(node); + if (!(isGroup.has_value())) { + TAG_LOGW(AceLogTag::ACE_LIST, + "ListPositionMap Conflict: LazyForEach FrameCount > 0, but get child type failed."); + isGroup = false; + } + while (count--) { + isGroup.value() ? CalculateGroupNode() : CalculateListItemNode(); + } + return; + } + + void CalculateFrameNode(RefPtr frameNode) + { + CHECK_NULL_VOID(frameNode); + auto listItemGroupPatten = frameNode->GetPattern(); + if (listItemGroupPatten) { + CalculateGroupNode(); + } else { + CalculateListItemNode(); + } + } + + void CalculateListItemNode() + { + curRowHeight_ = std::max(curRowHeight_, childrenSize_->GetChildSize(curIndex_)); + curLine_++; + if (curLine_ == lanes_ || curIndex_ == totalItemCount_ - 1) { + while (curLine_) { + curLine_--; + posMap_[curIndex_ - curLine_] = { totalHeight_, curRowHeight_ }; + } + totalHeight_ += (curRowHeight_ + space_); + curRowHeight_ = 0.0f; + } + curIndex_++; + } + + void CalculateGroupNode() + { + if (curLine_ > 0) { + while (curLine_) { + curLine_--; + posMap_[curIndex_ - 1 - curLine_] = { totalHeight_, curRowHeight_ }; + } + totalHeight_ += (curRowHeight_ + space_); + curRowHeight_ = 0.0f; + } + curRowHeight_ = childrenSize_->GetChildSize(curIndex_); + posMap_[curIndex_] = { totalHeight_, curRowHeight_ }; + totalHeight_ += (curRowHeight_ + space_); + curLine_ = 0; + curRowHeight_ = 0.0f; + curIndex_++; + } + + void PosMapRecalculate(LayoutWrapper* layoutWrapper) + { + curIndex_ = 0; + curLine_ = 0; + totalHeight_ = 0.0f; + curRowHeight_ = 0.0f; + if (lanes_ == 1) { + for (int32_t index = 0; index < totalItemCount_; index++) { + curRowHeight_ = childrenSize_->GetChildSize(index); + posMap_[index] = { totalHeight_, curRowHeight_ }; + totalHeight_ += (curRowHeight_ + space_); + } + totalHeight_ -= space_; + } else { + auto listNode = layoutWrapper->GetHostNode(); + CHECK_NULL_VOID(listNode); + CalculateUINode(listNode); + totalHeight_ -= space_; + } + } + + void GroupPosMapRecalculate() + { + curIndex_ = 0; + totalHeight_ = headerSize_; + curRowHeight_ = 0.0f; + curLine_ = 0; + + for (int32_t index = 0; index < totalItemCount_;) { + while (curLine_ < lanes_) { + curRowHeight_ = std::max(curRowHeight_, childrenSize_->GetChildSize(index + curLine_)); + curLine_++; + } + curLine_ = 0; + int32_t curRowEndIndex = std::min(index + lanes_ - 1, totalItemCount_ - 1); + while (index <= curRowEndIndex) { + posMap_[index++] = { totalHeight_, curRowHeight_ }; + } + totalHeight_ += (curRowHeight_ + space_); + curRowHeight_ = 0.0f; + } + totalHeight_ = totalHeight_ - space_ + footerSize_; + } + + void PosMapUpdateAllSize() + { + float curPos = 0.0f; + for (int32_t index = 0; index < totalItemCount_; index++) { + posMap_[index] = { curPos, curRowHeight_ }; + } + } + + void UpdatePosMap(LayoutWrapper* layoutWrapper, int32_t lanes, float space, + RefPtr& childrenSize) + { + totalItemCount_ = layoutWrapper->GetTotalChildCount(); + childrenSize_ = childrenSize; + if (lanes != lanes_) { + dirty_ |= LIST_UPDATE_LANES; + lanes_ = lanes; + } + if (!NearEqual(space, space_)) { + dirty_ |= LIST_UPDATE_SPACE; + space_ = space; + } + switch (CheckPosMapUpdateRule()) { + case ListPosMapUpdate::NO_CHANGE: + break; + case ListPosMapUpdate::UPDATE_ALL_SIZE: + PosMapRecalculate(layoutWrapper); + break; + case ListPosMapUpdate::RE_CALCULATE: + default: + PosMapRecalculate(layoutWrapper); + } + ClearDirty(); + } + + void UpdateGroupPosMap(int32_t totalCount, int32_t lanes, float space, + RefPtr& childrenSize, float headerSize, float footerSize) + { + totalItemCount_ = totalCount; + childrenSize_ = childrenSize; + if (lanes != lanes_) { + dirty_ |= LIST_UPDATE_LANES; + lanes_ = lanes; + } + if (!NearEqual(space, space_)) { + dirty_ |= LIST_UPDATE_SPACE; + space_ = space; + } + if (!NearEqual(headerSize, headerSize_) || !NearEqual(footerSize, footerSize_)) { + dirty_ |= LIST_GROUP_UPDATE_HEADER_FOOTER; + headerSize_ = headerSize; + footerSize_ = footerSize; + } + if (totalItemCount_ <= 0) { + totalHeight_ = 0; + ClearPosMap(); + ClearDirty(); + return; + } + switch (CheckPosMapUpdateRule()) { + case ListPosMapUpdate::NO_CHANGE: + break; + case ListPosMapUpdate::UPDATE_ALL_SIZE: + GroupPosMapRecalculate(); + break; + case ListPosMapUpdate::RE_CALCULATE: + default: + GroupPosMapRecalculate(); + } + ClearDirty(); + } + + float GetPos(int32_t startIndex, float startPos) + { + return posMap_[startIndex].mainPos - startPos; + } + + float GetGroupLayoutOffset(int32_t startIndex, float startPos) + { + return posMap_[startIndex].mainPos - startPos; + } + + void OptimizeBeforeMeasure(int32_t& beginIndex, float& beginPos, const float offset, const float contentSize) + { + if (NearZero(offset) || GreatOrEqual(contentSize, totalHeight_)) { + return; + } + float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f; + if (Positive(offset)) { + float criticalPos = offset; + std::pair rowInfo = GetRowEndIndexAndHeight(beginIndex); + while (!NearEqual(posMap_[beginIndex].mainPos + rowInfo.second, totalHeight_) && + LessNotEqual(beginPos + rowInfo.second + chainOffset, criticalPos)) { + beginIndex = rowInfo.first + 1; + beginPos += (rowInfo.second + space_); + rowInfo = GetRowEndIndexAndHeight(beginIndex); + chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f; + } + } else { + float criticalPos = offset + contentSize; + std::pair rowInfo = GetRowEndIndexAndHeight(beginIndex); + while (Positive(posMap_[beginIndex].mainPos) && + GreatNotEqual(beginPos - rowInfo.second + chainOffset, criticalPos)) { + beginIndex = GetRowStartIndex(beginIndex) - 1; + beginPos -= (rowInfo.second + space_); + rowInfo = GetRowEndIndexAndHeight(beginIndex); + chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f; + } + } + } + + void SetChainOffsetCallback(std::function func) + { + chainOffsetFunc_ = std::move(func); + } + + int32_t GetRowStartIndex(const int32_t input) + { + int32_t startIndex = input; + while (startIndex > 0 && NearEqual(posMap_[startIndex].mainPos, posMap_[startIndex - 1].mainPos)) { + startIndex--; + } + return startIndex; + } + + std::pair GetRowEndIndexAndHeight(const int32_t input) + { + int32_t endIndex = input; + float rowHeight = 0.0f; + while (endIndex < (totalItemCount_ - 1) && + NearEqual(posMap_[endIndex].mainPos, posMap_[endIndex + 1].mainPos)) { + endIndex++; + } + if (endIndex == totalItemCount_ - 1) { + rowHeight = totalHeight_ - posMap_[endIndex].mainPos; + } else { + rowHeight = posMap_[endIndex + 1].mainPos - posMap_[endIndex].mainPos - space_; + } + return { endIndex, rowHeight }; + } +private: + std::map posMap_; + RefPtr childrenSize_; + std::function chainOffsetFunc_; + ListChangeFlag dirty_ = LIST_NO_CHANGE; + int32_t totalItemCount_ = 0; + int32_t lanes_ = -1; + int32_t curLine_ = 0; + int32_t curIndex_ = 0; + float totalHeight_ = 0.0f; + float curRowHeight_ = 0.0f; + float space_ = 0.0f; + float headerSize_ = 0.0f; + float footerSize_ = 0.0f; +}; + +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERNS_LIST_LIST_POSITION_MAP_H \ No newline at end of file