From 06b8f6a56c08e377a7c1f1a5f4b0c736f8e6343b Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 2 Apr 2024 16:35:15 +0800 Subject: [PATCH 01/31] sliding window implementation Signed-off-by: Tianer Zhou Change-Id: Iec01ee36a141b61bba04330368f7a0a26b334300 --- .../water_flow_layout_info_sw.cpp | 63 +++++ .../water_flow_layout_info_sw.h | 74 +++++ .../sliding_window/water_flow_sw_layout.cpp | 263 ++++++++++++++++++ .../sliding_window/water_flow_sw_layout.h | 81 ++++++ .../waterflow/water_flow_layout_info.h | 5 +- .../waterflow/water_flow_layout_info_base.h | 44 +++ 6 files changed, 527 insertions(+), 3 deletions(-) create mode 100644 frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp create mode 100644 frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h create mode 100644 frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp create mode 100644 frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h create mode 100644 frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp new file mode 100644 index 00000000000..ead74b9b976 --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp @@ -0,0 +1,63 @@ +/* + * 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. + */ +#include "core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h" + +#include + +#include "base/utils/utils.h" +namespace OHOS::Ace::NG { +void WaterFlowLayoutInfoSW::SyncRange() +{ + auto min = std::min_element(lanes_.begin(), lanes_.end(), + [](const auto& lhs, const auto& rhs) { return LessNotEqual(lhs.startPos, rhs.startPos); }); + auto max = std::max_element(lanes_.begin(), lanes_.end(), + [](const auto& lhs, const auto& rhs) { return LessNotEqual(lhs.endPos, rhs.endPos); }); + + startIndex_ = min->items_.front().idx; + endIndex_ = max->items_.back().idx; +} + +float WaterFlowLayoutInfoSW::DistanceToTop(size_t item, float mainGap) const +{ + if (!idxToLane_.count(item)) { + return 0.0f; + } + const auto& lane = lanes_[idxToLane_[item]]; + float dist = lane.startPos; + for (const auto& item : lane.items_) { + if (item.idx == item) { + break; + } + dist += item.mainSize + mainGap; + } + return dist; +} + +float WaterFlowLayoutInfoSW::DistanceToBottom(size_t item, float mainSize, float mainGap) const +{ + if (!idxToLane_.count(item)) { + return 0.0f; + } + const auto& lane = lanes_[idxToLane_[item]]; + float dist = mainSize - lane.endPos; + for (const auto& item : lane.items_) { + if (item.idx == item) { + break; + } + dist += item.mainSize + mainGap; + } + return dist; +} +} // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h new file mode 100644 index 00000000000..a4aa499e9c4 --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h @@ -0,0 +1,74 @@ +/* + * 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_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_SW_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_SW_H + +#include +#include + +#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" + +namespace OHOS::Ace::NG { + +/** + * @brief Layout Data structure for Sliding Window version of WaterFlowLayout + */ +class WaterFlowLayoutInfoSW : public WaterFlowLayoutInfoBase { +public: + void SyncRange(); + + /** + * @brief Calculates distance from the item's top edge to the top of the viewport. + * + * @param item idx + * @param mainGap + * @return positive result when item's top edge is below viewport. + */ + float DistanceToTop(size_t item, float mainGap) const; + + /** + * @brief Calculates distance from the item's bottom edge to the bottom of the viewport. + * + * @param item idx + * @param mainSize of the viewport + * @param mainGap + * @return positive result when it's bottom edge is above viewport. + */ + float DistanceToBottom(size_t item, float mainSize, float mainGap) const; + + void ResetLanes(); + + struct Lane; + std::vector lanes_; + std::unordered_map idxToLane_; + + float delta_ = 0.0f; + + struct ItemInfo; +}; + +struct WaterFlowLayoutInfoSW::ItemInfo { + int32_t idx = -1; + float mainSize = 0.0f; +}; + +struct WaterFlowLayoutInfoSW::Lane { + float startPos = 0.0f; + float endPos = 0.0f; + std::deque items_; +}; +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_SW_H diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp new file mode 100644 index 00000000000..2bae2d29c09 --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp @@ -0,0 +1,263 @@ +/* + * 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. + */ +#include "core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h" + +#include +#include +#include + +#include "base/utils/utils.h" +#include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/layout/layout_wrapper.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" +namespace OHOS::Ace::NG { +void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) +{ + wrapper_ = wrapper; + float mainSize = Init(); + if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { + MeasureOnJump(info_->jumpIndex_, info_->align_, mainSize); + } else { + MeasureOnOffset(mainSize, info_->delta_); + } +} + +void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) +{ + if (info_->lanes_.empty()) { + return; + } + + float crossPos = 0.0f; + for (auto& lane : info_->lanes_) { + float mainPos = lane.startPos; + for (auto& item : lane.items_) { + auto child = wrapper->GetOrCreateChildByIndex(item.idx); + auto childNode = child->GetGeometryNode(); + childNode->SetMarginFrameOffset( + info_->axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }); + mainPos += childNode->GetMarginFrameSize().MainSize(info_->axis_) + mainGap_; + } + crossPos += width; + } + wrapper->SetActiveChildRange(info_->startIndex_, info_->endIndex_); +} + +void WaterFlowSWLayout::MeasureOnOffset(float mainSize, float offset) +{ + for (auto& lane : info_->lanes_) { + lane.startPos += offset; + lane.endPos += offset; + } + + // clear out items outside viewport after position change + if (Positive(offset)) { + // positive offset is scrolling upwards + ClearBack(mainSize); + FillFront(0.0f, 0); + } else { + ClearFront(); + FillBack(mainSize, info_->childrenCount_ - 1); + } + + info_->SyncRange(); +} + +void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) +{ + if (targetIdx < info_->startIndex_) { + FillFront(-FLT_MAX, targetIdx); + } else if (targetIdx > info_->endIndex_) { + FillBack(FLT_MAX, targetIdx); + } +} + +using lanePos = std::pair; + +void WaterFlowSWLayout::FillBack(float viewportBound, int32_t maxChildIdx) +{ + maxChildIdx = std::min(maxChildIdx, info_->childrenCount_ - 1); + std::priority_queue q; + for (size_t i = 0; i < info_->lanes_.size(); ++i) { + float endPos = info_->lanes_[i].endPos; + if (LessNotEqual(endPos + mainGap_, viewportBound)) { + q.push({ endPos, i }); + } + } + + int32_t childIdx = info_->endIndex_ + 1; + + while (!q.empty() && childIdx <= maxChildIdx) { + auto [endPos, laneIdx] = q.top(); + q.pop(); + auto child = MeasureChild(childIdx); + + float size = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); + endPos += mainGap_ + size; + + auto& lane = info_->lanes_[laneIdx]; + lane.endPos = endPos; + info_->idxToLane_[childIdx] = laneIdx; + lane.items_.push_back({ childIdx++, size }); + if (LessNotEqual(endPos, viewportBound)) { + q.push({ endPos, laneIdx }); + } + } +} + +void WaterFlowSWLayout::FillFront(float viewportBound, int32_t minChildIdx) +{ + minChildIdx = std::max(minChildIdx, 0); + std::priority_queue, std::greater<>> q; + for (size_t i = 0; i < info_->lanes_.size(); ++i) { + float startPos = info_->lanes_[i].startPos; + if (GreatNotEqual(startPos - mainGap_, viewportBound)) { + q.push({ startPos, i }); + } + } + + int32_t childIdx = info_->startIndex_ - 1; + + while (!q.empty() && childIdx >= minChildIdx) { + auto [startPos, laneIdx] = q.top(); + q.pop(); + auto child = MeasureChild(childIdx); + + float size = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); + startPos -= mainGap_ + size; + + auto& lane = info_->lanes_[laneIdx]; + info_->idxToLane_[childIdx] = laneIdx; + lane.startPos = startPos; + lane.items_.push_front({ childIdx--, size }); + if (GreatNotEqual(startPos - mainGap_, viewportBound)) { + q.push({ startPos, laneIdx }); + } + } +} + +void WaterFlowSWLayout::ClearBack(float mainSize) +{ + for (auto& lane : info_->lanes_) { + if (lane.items_.empty()) { + continue; + } + float lastItemStartPos = lane.endPos - lane.items_.back().mainSize; + while (GreatNotEqual(lastItemStartPos, mainSize)) { + lane.items_.pop_back(); + if (lane.items_.empty()) { + break; + } + lastItemStartPos -= mainGap_ + lane.items_.back().mainSize; + } + } +} + +void WaterFlowSWLayout::ClearFront() +{ + for (auto& lane : info_->lanes_) { + if (lane.items_.empty()) { + continue; + } + float firstItemEndPos = lane.startPos + lane.items_.front().mainSize; + while (Negative(firstItemEndPos)) { + lane.items_.pop_front(); + if (lane.items_.empty()) { + break; + } + firstItemEndPos += mainGap_ + lane.items_.front().mainSize; + } + } +} + +ScrollAlign WaterFlowSWLayout::ParseAutoAlign(int32_t jumpIdx, float mainSize, bool inView) +{ + if (inView) { + if (Negative(info_->DistanceToTop(jumpIdx, mainGap_))) { + return ScrollAlign::START; + } + if (Negative(info_->DistanceToBottom(jumpIdx, mainSize, mainGap_))) { + return ScrollAlign::END; + } + // item is already fully in viewport + return ScrollAlign::NONE; + } + if (jumpIdx < info_->startIndex_) { + return ScrollAlign::START; + } + return ScrollAlign::END; +} + +void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float mainSize) +{ + if (jumpIdx == -1) { + jumpIdx = info_->childrenCount_ - 1; + } + + bool inView = jumpIdx >= info_->startIndex_ && jumpIdx <= info_->endIndex_; + if (align == ScrollAlign::AUTO) { + align = ParseAutoAlign(jumpIdx, mainSize, inView); + } + + // if the item is within 1 full-viewport distance (approximately), we consider it close + int32_t cntInView = info_->endIndex_ - info_->startIndex_ + 1; + bool closeToView = + jumpIdx > info_->endIndex_ ? jumpIdx - info_->endIndex_ < cntInView : info_->startIndex_ - jumpIdx < cntInView; + if (closeToView) { + MeasureToTarget(jumpIdx); + } + switch (align) { + case ScrollAlign::START: { + if (inView || closeToView) { + MeasureOnOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_)); + } else { + std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [](auto& it) { + it->items_.clear(); + it->startPos = 0.0f; + it->endPos = 0.0f; + }); + info_->idxToLane_.clear(); + info_->endIndex_ = jumpIdx - 1; + FillBack(mainSize, info_->childrenCount_ - 1); + } + break; + } + case ScrollAlign::CENTER: { + if (inView || closeToView) { + MeasureOnOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_) - mainSize / 2.0f); + } else { + } + break; + } + case ScrollAlign::END: { + if (inView || closeToView) { + MeasureOnOffset(mainSize, info_->DistanceToBottom(jumpIdx, mainSize, mainGap_)); + } else { + std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize](auto& it) { + it->items_.clear(); + it->startPos = mainSize; + it->endPos = mainSize; + }); + info_->idxToLane_.clear(); + info_->startIndex_ = jumpIdx + 1; + FillFront(0.0f, 0); + } + break; + } + default: + break; + } +} +} // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h new file mode 100644 index 00000000000..512ca655b73 --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h @@ -0,0 +1,81 @@ +/* + * 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_PATTERN_WATERFLOW_WATER_FLOW_SW_LAYOUT_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SW_LAYOUT_H + +#include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/layout/layout_wrapper.h" +#include "core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" + +namespace OHOS::Ace::NG { + +class ACE_EXPORT WaterFlowSWLayout : public WaterFlowLayoutBase { + DECLARE_ACE_TYPE(WaterFlowSWLayout, WaterFlowLayoutBase); + +public: + void Measure(LayoutWrapper* wrapper) override; + void Layout(LayoutWrapper* wrapper) override; + +private: + float Init(); + + void MeasureOnOffset(float mainSize, float offset); + + void MeasureToTarget(int32_t targetIdx); + + /** + * @brief When the item is within or close to viewport, layout is preserved and we merely apply an offset. + * But when jumping to an item further away, the current layout would be reset, and a new layout happens from that index. + * + * @param jumpIdx + * @param align ScrollAlign + * @param mainSize of the viewport + */ + void MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float mainSize); + + ScrollAlign ParseAutoAlign(int32_t jumpIdx, float mainSize, bool inView); + + /** + * @brief fills the viewport backward until either condition is not satisfied. + * + * @param viewportBound + * @param minChildIdx smallest item index to fill before stopping. + */ + void FillFront(float viewportBound, int32_t minChildIdx); + + /** + * @brief fills the viewport forward until either condition is not satisfied. + * + * @param viewportBound + * @param maxChildIdx greatest item index to fill before stopping. + */ + void FillBack(float viewportBound, int32_t maxChildIdx); + + void ClearFront(); + void ClearBack(float mainSize); + + RefPtr MeasureChild(int32_t idx); + + LayoutWrapper* wrapper_; + WaterFlowLayoutInfoSW* info_; + + float mainGap_ = 0.0f; + float crossGap_ = 0.0f; +}; +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SW_LAYOUT_H diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index 844895d5b58..fe1a325fb18 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -22,6 +22,7 @@ #include #include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_sections.h" #include "core/components_ng/property/measure_property.h" @@ -36,9 +37,7 @@ struct FlowItemPosition { float startMainPos = 0; }; -constexpr int32_t EMPTY_JUMP_INDEX = -2; - -class WaterFlowLayoutInfo { +class WaterFlowLayoutInfo: public WaterFlowLayoutInfoBase { public: int32_t GetCrossIndex(int32_t itemIndex) const; void UpdateStartIndex(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h new file mode 100644 index 00000000000..084f502eb60 --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -0,0 +1,44 @@ +/* + * 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_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_BASE_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_BASE_H + +#include "core/components/scroll/scroll_controller_base.h" + +namespace OHOS::Ace::NG { + +constexpr int32_t EMPTY_JUMP_INDEX = -2; + +class WaterFlowLayoutInfoBase { +public: + Axis axis_ = Axis::VERTICAL; + + bool itemStart_ = false; + bool itemEnd_ = false; // last item is partially in viewport + bool offsetEnd_ = false; // last item's bottom is in viewport + + int32_t jumpIndex_ = EMPTY_JUMP_INDEX; + ScrollAlign align_ = ScrollAlign::START; + std::optional targetIndex_; + + int32_t startIndex_ = 0; + int32_t endIndex_ = -1; + int32_t footerIndex_ = -1; + int32_t childrenCount_ = 0; +}; + +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_BASE_H -- Gitee From 58249959cc99755913f0d365a694b6e4bf20cb99 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 2 Apr 2024 19:54:23 +0800 Subject: [PATCH 02/31] implement jump Signed-off-by: Tianer Zhou Change-Id: Ie8ab1874a328f407b1bf2b98fefa09b32b4c2bdb --- .../sliding_window/water_flow_sw_layout.cpp | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp index 2bae2d29c09..7218181f76b 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp @@ -97,20 +97,20 @@ void WaterFlowSWLayout::FillBack(float viewportBound, int32_t maxChildIdx) } } - int32_t childIdx = info_->endIndex_ + 1; + int32_t idx = info_->endIndex_ + 1; - while (!q.empty() && childIdx <= maxChildIdx) { + while (!q.empty() && idx <= maxChildIdx) { auto [endPos, laneIdx] = q.top(); q.pop(); - auto child = MeasureChild(childIdx); + auto child = MeasureChild(idx); float size = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); endPos += mainGap_ + size; auto& lane = info_->lanes_[laneIdx]; lane.endPos = endPos; - info_->idxToLane_[childIdx] = laneIdx; - lane.items_.push_back({ childIdx++, size }); + info_->idxToLane_[idx] = laneIdx; + lane.items_.push_back({ idx++, size }); if (LessNotEqual(endPos, viewportBound)) { q.push({ endPos, laneIdx }); } @@ -128,20 +128,20 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t minChildIdx) } } - int32_t childIdx = info_->startIndex_ - 1; + int32_t idx = info_->startIndex_ - 1; - while (!q.empty() && childIdx >= minChildIdx) { + while (!q.empty() && idx >= minChildIdx) { auto [startPos, laneIdx] = q.top(); q.pop(); - auto child = MeasureChild(childIdx); + auto child = MeasureChild(idx); float size = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); startPos -= mainGap_ + size; auto& lane = info_->lanes_[laneIdx]; - info_->idxToLane_[childIdx] = laneIdx; + info_->idxToLane_[idx] = laneIdx; lane.startPos = startPos; - lane.items_.push_front({ childIdx--, size }); + lane.items_.push_front({ idx--, size }); if (GreatNotEqual(startPos - mainGap_, viewportBound)) { q.push({ startPos, laneIdx }); } @@ -235,9 +235,22 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float break; } case ScrollAlign::CENTER: { + auto child = MeasureChild(jumpIdx); + float itemH = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); if (inView || closeToView) { - MeasureOnOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_) - mainSize / 2.0f); + MeasureOnOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize - itemH) / 2.0f); } else { + std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize, itemH](auto& it) { + it->items_.clear(); + it->startPos = (mainSize - itemH) / 2.0f; + it->endPos = (mainSize + itemH) / 2.0f; + }); + auto& lane = info_->lanes_[0]; + lane.items_.push_back({ jumpIdx, itemH }); + info_->startIndex_ = info_->endIndex_ = jumpIdx; + + FillFront(0.0f, 0); + FillBack(mainSize, info_->childrenCount_ - 1); } break; } -- Gitee From 230396011b7c9e0f58aa77f111e4b2c0917bf1a6 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 2 Apr 2024 20:47:53 +0800 Subject: [PATCH 03/31] adjust overScroll Signed-off-by: Tianer Zhou Change-Id: If5e2dc096f1616fcc417c5fddde882a39033451a --- .../water_flow_layout_info_sw.cpp | 15 ++++---- .../sliding_window/water_flow_sw_layout.cpp | 38 ++++++++++++++++--- .../sliding_window/water_flow_sw_layout.h | 6 ++- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp index ead74b9b976..d03f5fd004f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp @@ -16,17 +16,16 @@ #include -#include "base/utils/utils.h" namespace OHOS::Ace::NG { void WaterFlowLayoutInfoSW::SyncRange() { - auto min = std::min_element(lanes_.begin(), lanes_.end(), - [](const auto& lhs, const auto& rhs) { return LessNotEqual(lhs.startPos, rhs.startPos); }); - auto max = std::max_element(lanes_.begin(), lanes_.end(), - [](const auto& lhs, const auto& rhs) { return LessNotEqual(lhs.endPos, rhs.endPos); }); - - startIndex_ = min->items_.front().idx; - endIndex_ = max->items_.back().idx; + for (const auto& lane : lanes_) { + if (lane.items_.empty()) { + continue; + } + startIndex_ = std::min(startIndex_, lane.items_.front().idx); + endIndex_ = std::max(endIndex_, lane.items_.back().idx); + } } float WaterFlowLayoutInfoSW::DistanceToTop(size_t item, float mainGap) const diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp index 7218181f76b..610de97f494 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp @@ -22,16 +22,25 @@ #include "core/components/scroll/scroll_controller_base.h" #include "core/components_ng/layout/layout_wrapper.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" namespace OHOS::Ace::NG { void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) { wrapper_ = wrapper; + auto props = DynamicCast(wrapper->GetLayoutProperty()); + float mainSize = Init(); if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { MeasureOnJump(info_->jumpIndex_, info_->align_, mainSize); + } else if (info_->targetIndex_) { + MeasureToTarget(*info_->targetIndex_); } else { - MeasureOnOffset(mainSize, info_->delta_); + ApplyOffset(mainSize, info_->delta_); + if (!overScroll_) { + AdjustOverScroll(mainSize); + } } + wrapper->SetCacheCount(props->GetCachedCountValue(1)); } void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) @@ -55,7 +64,7 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) wrapper->SetActiveChildRange(info_->startIndex_, info_->endIndex_); } -void WaterFlowSWLayout::MeasureOnOffset(float mainSize, float offset) +void WaterFlowSWLayout::ApplyOffset(float mainSize, float offset) { for (auto& lane : info_->lanes_) { lane.startPos += offset; @@ -221,7 +230,7 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float switch (align) { case ScrollAlign::START: { if (inView || closeToView) { - MeasureOnOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_)); + ApplyOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_)); } else { std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [](auto& it) { it->items_.clear(); @@ -238,7 +247,7 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float auto child = MeasureChild(jumpIdx); float itemH = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); if (inView || closeToView) { - MeasureOnOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize - itemH) / 2.0f); + ApplyOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize - itemH) / 2.0f); } else { std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize, itemH](auto& it) { it->items_.clear(); @@ -256,7 +265,7 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float } case ScrollAlign::END: { if (inView || closeToView) { - MeasureOnOffset(mainSize, info_->DistanceToBottom(jumpIdx, mainSize, mainGap_)); + ApplyOffset(mainSize, info_->DistanceToBottom(jumpIdx, mainSize, mainGap_)); } else { std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize](auto& it) { it->items_.clear(); @@ -272,5 +281,24 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float default: break; } + info_->SyncRange(); + AdjustOverScroll(mainSize); +} + +void WaterFlowSWLayout::AdjustOverScroll(float mainSize) +{ + if (info_->lanes_.empty()) { + return; + } + auto minStart = std::min_element(info_->lanes_.begin(), info_->lanes_.end(), + [](const auto& a, const auto& b) { return LessNotEqual(a.startPos, b.startPos); }); + auto maxEnd = std::max_element(info_->lanes_.begin(), info_->lanes_.end(), + [](const auto& a, const auto& b) { return LessNotEqual(a.endPos, b.endPos); }); + + if (Positive(minStart->startPos)) { + ApplyOffset(mainSize, -minStart->startPos); + } else if (LessNotEqual(maxEnd->endPos, mainSize)) { + ApplyOffset(mainSize, mainSize - maxEnd->endPos); + } } } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h index 512ca655b73..6415f65e191 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h @@ -34,7 +34,7 @@ public: private: float Init(); - void MeasureOnOffset(float mainSize, float offset); + void ApplyOffset(float mainSize, float offset); void MeasureToTarget(int32_t targetIdx); @@ -69,6 +69,8 @@ private: void ClearFront(); void ClearBack(float mainSize); + void AdjustOverScroll(float mainSize); + RefPtr MeasureChild(int32_t idx); LayoutWrapper* wrapper_; @@ -76,6 +78,8 @@ private: float mainGap_ = 0.0f; float crossGap_ = 0.0f; + + bool overScroll_ = true; }; } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SW_LAYOUT_H -- Gitee From 058cae1686d54918b8fca1a0101cfd72ae23cb7d Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 15 Apr 2024 20:06:22 +0800 Subject: [PATCH 04/31] adapt new layout to pattern Signed-off-by: Tianer Zhou Change-Id: Ia0672d6f931d5a9a3896b40eda87c542682882d6 --- .../core/components_ng/pattern/BUILD.gn | 2 + .../water_flow_layout_info_sw.cpp | 21 +- .../water_flow_layout_info_sw.h | 50 +- .../sliding_window/water_flow_sw_layout.cpp | 57 +- .../sliding_window/water_flow_sw_layout.h | 15 +- .../waterflow/water_flow_layout_algorithm.cpp | 161 +-- .../waterflow/water_flow_layout_algorithm.h | 14 +- .../waterflow/water_flow_layout_info.cpp | 76 +- .../waterflow/water_flow_layout_info.h | 67 +- .../waterflow/water_flow_layout_info_base.h | 92 +- .../waterflow/water_flow_layout_utils.cpp | 27 +- .../waterflow/water_flow_layout_utils.h | 9 +- .../pattern/waterflow/water_flow_pattern.cpp | 155 ++- .../pattern/waterflow/water_flow_pattern.h | 26 +- .../waterflow/water_flow_segmented_layout.cpp | 203 ++-- .../waterflow/water_flow_segmented_layout.h | 16 +- .../water_flow_segment_layout_test.cpp | 1018 ++++++++--------- .../pattern/waterflow/water_flow_test_ng.cpp | 127 +- 18 files changed, 1187 insertions(+), 949 deletions(-) diff --git a/frameworks/core/components_ng/pattern/BUILD.gn b/frameworks/core/components_ng/pattern/BUILD.gn index f4de11ea2de..5e393d0f703 100644 --- a/frameworks/core/components_ng/pattern/BUILD.gn +++ b/frameworks/core/components_ng/pattern/BUILD.gn @@ -524,6 +524,8 @@ build_component_ng("pattern_ng") { "video/video_node.cpp", "video/video_pattern.cpp", "view_context/view_context_model_ng.cpp", + "waterflow/sliding_window/water_flow_layout_info_sw.cpp", + "waterflow/sliding_window/water_flow_sw_layout.cpp", "waterflow/water_flow_accessibility_property.cpp", "waterflow/water_flow_content_modifier.cpp", "waterflow/water_flow_item_model_ng.cpp", diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp index d03f5fd004f..a46a7f5d487 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp @@ -15,6 +15,7 @@ #include "core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h" #include +#include namespace OHOS::Ace::NG { void WaterFlowLayoutInfoSW::SyncRange() @@ -28,15 +29,15 @@ void WaterFlowLayoutInfoSW::SyncRange() } } -float WaterFlowLayoutInfoSW::DistanceToTop(size_t item, float mainGap) const +float WaterFlowLayoutInfoSW::DistanceToTop(int32_t itemIdx, float mainGap) const { - if (!idxToLane_.count(item)) { + if (!idxToLane_.count(itemIdx)) { return 0.0f; } - const auto& lane = lanes_[idxToLane_[item]]; + const auto& lane = lanes_[idxToLane_.at(itemIdx)]; float dist = lane.startPos; for (const auto& item : lane.items_) { - if (item.idx == item) { + if (item.idx == itemIdx) { break; } dist += item.mainSize + mainGap; @@ -44,19 +45,23 @@ float WaterFlowLayoutInfoSW::DistanceToTop(size_t item, float mainGap) const return dist; } -float WaterFlowLayoutInfoSW::DistanceToBottom(size_t item, float mainSize, float mainGap) const +float WaterFlowLayoutInfoSW::DistanceToBottom(int32_t itemIdx, float mainSize, float mainGap) const { - if (!idxToLane_.count(item)) { + if (!idxToLane_.count(itemIdx)) { return 0.0f; } - const auto& lane = lanes_[idxToLane_[item]]; + const auto& lane = lanes_[idxToLane_.at(itemIdx)]; float dist = mainSize - lane.endPos; for (const auto& item : lane.items_) { - if (item.idx == item) { + if (item.idx == itemIdx) { break; } dist += item.mainSize + mainGap; } return dist; } +float WaterFlowLayoutInfoSW::offset() const +{ + return lanes_[0].startPos; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h index a4aa499e9c4..e9f31aa5100 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h @@ -27,7 +27,51 @@ namespace OHOS::Ace::NG { * @brief Layout Data structure for Sliding Window version of WaterFlowLayout */ class WaterFlowLayoutInfoSW : public WaterFlowLayoutInfoBase { + DECLARE_ACE_TYPE(WaterFlowLayoutInfoSW, WaterFlowLayoutInfoBase); public: + + float offset() const override; + int32_t firstIdx() const override {return startIndex_;} + + void UpdateOffset(float delta) override {} + + int32_t GetCrossIndex(int32_t itemIndex) const override {} + + + OverScrollOffset GetOverScrolledDelta(float delta) const override {} + + float CalcOverScroll(float mainSize, float delta) const override {} + + bool ReachStart(float prevPos, bool firstLayout) const override {} + + bool ReachEnd(float prevPos) const override {} + + bool OutOfBounds() const override {} + + /** + * @return total height of all recorded items. + */ + float GetContentHeight() const override {} + + /** + * @brief Get target item's position in order to perform scrollTo animation. + * + * @param idx item's index. + * @param crossIdx item's cross-axis lane index. + * @return position + */ + float CalcTargetPosition(int32_t idx, int32_t crossIdx) const override {} + + /** + * @return change in position, comparing to [prevPos] + */ + float GetDelta(float prevPos) const override {return delta_;} + + int32_t GetMainCount() const override {} + int32_t GetCrossCount() const override {} + + void Reset() override {} + void SyncRange(); /** @@ -37,7 +81,7 @@ public: * @param mainGap * @return positive result when item's top edge is below viewport. */ - float DistanceToTop(size_t item, float mainGap) const; + float DistanceToTop(int32_t item, float mainGap) const; /** * @brief Calculates distance from the item's bottom edge to the bottom of the viewport. @@ -47,9 +91,7 @@ public: * @param mainGap * @return positive result when it's bottom edge is above viewport. */ - float DistanceToBottom(size_t item, float mainSize, float mainGap) const; - - void ResetLanes(); + float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; struct Lane; std::vector lanes_; diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp index 610de97f494..3be3a125221 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp @@ -20,16 +20,24 @@ #include "base/utils/utils.h" #include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/base/frame_node.h" #include "core/components_ng/layout/layout_wrapper.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_utils.h" +#include "core/components_ng/property/templates_parser.h" + namespace OHOS::Ace::NG { void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) { wrapper_ = wrapper; auto props = DynamicCast(wrapper->GetLayoutProperty()); + axis_ = axis_ = props->GetAxis(); + + auto [idealSize, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_); + Init(idealSize); - float mainSize = Init(); + float mainSize = idealSize.MainSize(axis_); if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { MeasureOnJump(info_->jumpIndex_, info_->align_, mainSize); } else if (info_->targetIndex_) { @@ -45,23 +53,64 @@ void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) { + wrapper->RemoveAllChildInRenderTree(); if (info_->lanes_.empty()) { return; } float crossPos = 0.0f; - for (auto& lane : info_->lanes_) { + for (size_t i = 0; i < info_->lanes_.size(); ++i) { + auto& lane = info_->lanes_[i]; float mainPos = lane.startPos; for (auto& item : lane.items_) { auto child = wrapper->GetOrCreateChildByIndex(item.idx); + if (!child) { + continue; + } auto childNode = child->GetGeometryNode(); childNode->SetMarginFrameOffset( info_->axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }); + if (child->CheckNeedForceMeasureAndLayout()) { + child->Layout(); + } else { + child->GetHostNode()->ForceSyncGeometryNode(); + } mainPos += childNode->GetMarginFrameSize().MainSize(info_->axis_) + mainGap_; } - crossPos += width; + crossPos += itemCrossSize_[i] + crossGap_; + } +} + +void WaterFlowSWLayout::Init(const SizeF& frameSize) +{ + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + auto scale = props->GetLayoutConstraint()->scaleProperty; + auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0); + auto columnsGap = ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0); + mainGap_ = { axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap }; + crossGap_ = { axis_ == Axis::VERTICAL ? columnsGap : rowsGap }; + + auto crossSize = frameSize.CrossSize(axis_); + std::pair, bool> cross; + auto rowsTemplate = props->GetRowsTemplate().value_or("1fr"); + auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr"); + if (axis_ == Axis::VERTICAL) { + cross = ParseTemplateArgs( + WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, info_->childrenCount_); + } else { + cross = ParseTemplateArgs( + WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, info_->childrenCount_); + } + if (cross.second) { + crossGap_ = 0.0f; + } + + for (const auto& len : cross.first) { + itemCrossSize_.push_back(static_cast(len)); + } + if (itemCrossSize_.empty()) { + itemCrossSize_.push_back(crossSize); } - wrapper->SetActiveChildRange(info_->startIndex_, info_->endIndex_); } void WaterFlowSWLayout::ApplyOffset(float mainSize, float offset) diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h index 6415f65e191..9878f868fdb 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h @@ -20,7 +20,6 @@ #include "core/components_ng/layout/layout_wrapper.h" #include "core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" namespace OHOS::Ace::NG { @@ -28,11 +27,17 @@ class ACE_EXPORT WaterFlowSWLayout : public WaterFlowLayoutBase { DECLARE_ACE_TYPE(WaterFlowSWLayout, WaterFlowLayoutBase); public: + explicit WaterFlowSWLayout(const RefPtr& info) : info_(info) {} void Measure(LayoutWrapper* wrapper) override; void Layout(LayoutWrapper* wrapper) override; + void SetCanOverScroll(bool value) override + { + overScroll_ = value; + } + private: - float Init(); + void Init(const SizeF& frameSize); void ApplyOffset(float mainSize, float offset); @@ -73,9 +78,11 @@ private: RefPtr MeasureChild(int32_t idx); - LayoutWrapper* wrapper_; - WaterFlowLayoutInfoSW* info_; + LayoutWrapper* wrapper_ {}; + RefPtr info_; + Axis axis_ {}; + std::vector itemCrossSize_; float mainGap_ = 0.0f; float crossGap_ = 0.0f; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp index 6d722979ec0..11cee3dfd2e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp @@ -76,15 +76,15 @@ void WaterFlowLayoutAlgorithm::InitialItemsCrossSize( } // cross count changed by auto-fill and cross size change - if (!layoutInfo_.items_[0].empty() && crossLens.size() != layoutInfo_.items_[0].size()) { - layoutInfo_.Reset(); + if (!layoutInfo_->items_[0].empty() && crossLens.size() != layoutInfo_->items_[0].size()) { + layoutInfo_->Reset(); } int32_t index = 0; for (const auto& len : crossLens) { itemsCrossSize_.try_emplace(index, len); itemsCrossPosition_.try_emplace(index, ComputeCrossPosition(index)); - layoutInfo_.items_[0].try_emplace(index, std::map>()); + layoutInfo_->items_[0].try_emplace(index, std::map>()); ++index; } } @@ -110,59 +110,59 @@ void WaterFlowLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) int32_t updateIdx = layoutWrapper->GetHostNode()->GetChildrenUpdated(); if (updateIdx != -1) { - layoutInfo_.Reset(updateIdx); + layoutInfo_->Reset(updateIdx); layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1); } - layoutInfo_.childrenCount_ = layoutWrapper->GetTotalChildCount(); + layoutInfo_->childrenCount_ = layoutWrapper->GetTotalChildCount(); - InitialItemsCrossSize(layoutProperty, idealSize, layoutInfo_.childrenCount_); + InitialItemsCrossSize(layoutProperty, idealSize, layoutInfo_->childrenCount_); mainSize_ = GetMainAxisSize(idealSize, axis); - if (layoutInfo_.jumpIndex_ >= 0 && layoutInfo_.jumpIndex_ < layoutInfo_.childrenCount_) { - auto crossIndex = layoutInfo_.GetCrossIndex(layoutInfo_.jumpIndex_); + if (layoutInfo_->jumpIndex_ >= 0 && layoutInfo_->jumpIndex_ < layoutInfo_->childrenCount_) { + auto crossIndex = layoutInfo_->GetCrossIndex(layoutInfo_->jumpIndex_); if (crossIndex == -1) { // jump to out of cache } else { - layoutInfo_.JumpTo(layoutInfo_.items_[0][crossIndex][layoutInfo_.jumpIndex_]); + layoutInfo_->JumpTo(layoutInfo_->items_[0][crossIndex][layoutInfo_->jumpIndex_]); } } else { - layoutInfo_.jumpIndex_ = EMPTY_JUMP_INDEX; + layoutInfo_->jumpIndex_ = EMPTY_JUMP_INDEX; } FillViewport(mainSize_, layoutWrapper); - if (layoutInfo_.targetIndex_.has_value()) { + if (layoutInfo_->targetIndex_.has_value()) { MeasureForAnimation(layoutWrapper); } - if (layoutInfo_.jumpIndex_ != EMPTY_JUMP_INDEX) { - layoutInfo_.JumpTo({ footerMainStartPos_, footerMainSize_ }); + if (layoutInfo_->jumpIndex_ != EMPTY_JUMP_INDEX) { + layoutInfo_->JumpTo({ footerMainStartPos_, footerMainSize_ }); } if (matchChildren) { - mainSize_ = layoutInfo_.GetMaxMainHeight() + footerMainSize_; + mainSize_ = layoutInfo_->GetMaxMainHeight() + footerMainSize_; idealSize.SetMainSize(mainSize_, axis_); AddPaddingToSize(layoutProperty->CreatePaddingAndBorder(), idealSize); layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize); } - layoutInfo_.lastMainSize_ = mainSize_; + layoutInfo_->lastMainSize_ = mainSize_; layoutWrapper->SetCacheCount(layoutProperty->GetCachedCountValue(1)); } void WaterFlowLayoutAlgorithm::MeasureForAnimation(LayoutWrapper* layoutWrapper) { - if (layoutInfo_.targetIndex_.value() > layoutInfo_.childrenCount_) { - layoutInfo_.targetIndex_.reset(); + if (layoutInfo_->targetIndex_.value() > layoutInfo_->childrenCount_) { + layoutInfo_->targetIndex_.reset(); return; } auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - auto currentIndex = layoutInfo_.endIndex_; + auto currentIndex = layoutInfo_->endIndex_; auto position = GetItemPosition(currentIndex); - if (layoutInfo_.targetIndex_.value() == LAST_ITEM) { - layoutInfo_.targetIndex_ = layoutInfo_.childrenCount_ - 1; + if (layoutInfo_->targetIndex_.value() == LAST_ITEM) { + layoutInfo_->targetIndex_ = layoutInfo_->childrenCount_ - 1; } - while (layoutInfo_.targetIndex_.has_value() && (layoutInfo_.endIndex_ < layoutInfo_.targetIndex_.value())) { + while (layoutInfo_->targetIndex_.has_value() && (layoutInfo_->endIndex_ < layoutInfo_->targetIndex_.value())) { auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(GetChildIndexWithFooter(currentIndex)); if (!itemWrapper) { - layoutInfo_.targetIndex_.reset(); + layoutInfo_->targetIndex_.reset(); break; } auto itemCrossPosition = itemsCrossPosition_.find(position.crossIndex); @@ -173,19 +173,19 @@ void WaterFlowLayoutAlgorithm::MeasureForAnimation(LayoutWrapper* layoutWrapper) { itemCrossPosition->second, mainSize_, axis_ }, layoutProperty, itemWrapper)); auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize(); auto itemHeight = GetMainAxisSize(itemSize, axis_); - auto item = layoutInfo_.items_[0][position.crossIndex].find(currentIndex); - if (item == layoutInfo_.items_[0][position.crossIndex].end()) { - layoutInfo_.items_[0][position.crossIndex][currentIndex] = + auto item = layoutInfo_->items_[0][position.crossIndex].find(currentIndex); + if (item == layoutInfo_->items_[0][position.crossIndex].end()) { + layoutInfo_->items_[0][position.crossIndex][currentIndex] = std::make_pair(position.startMainPos, itemHeight); } else { if (item->second.second != itemHeight) { item->second.second = itemHeight; - layoutInfo_.ClearCacheAfterIndex(currentIndex); + layoutInfo_->ClearCacheAfterIndex(currentIndex); TAG_LOGD(AceLogTag::ACE_WATERFLOW, "item size changed"); } } - if (layoutInfo_.targetIndex_.value() == currentIndex) { - layoutInfo_.targetIndex_.reset(); + if (layoutInfo_->targetIndex_.value() == currentIndex) { + layoutInfo_->targetIndex_.reset(); } currentIndex++; position = GetItemPosition(currentIndex); @@ -205,14 +205,14 @@ void WaterFlowLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) MinusPaddingToSize(padding, size); auto childFrameOffset = OffsetF(padding.left.value_or(0.0f), padding.top.value_or(0.0f)); auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - layoutInfo_.UpdateStartIndex(); - auto firstIndex = layoutInfo_.endIndex_; + layoutInfo_->UpdateStartIndex(); + auto firstIndex = layoutInfo_->endIndex_; auto crossSize = size.CrossSize(axis_); auto layoutDirection = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection(); auto isRtl = (layoutDirection == TextDirection::RTL) && (axis_ == Axis::VERTICAL); - for (const auto& mainPositions : layoutInfo_.items_[0]) { + for (const auto& mainPositions : layoutInfo_->items_[0]) { for (const auto& item : mainPositions.second) { - if (item.first < layoutInfo_.startIndex_ || item.first > layoutInfo_.endIndex_) { + if (item.first < layoutInfo_->startIndex_ || item.first > layoutInfo_->endIndex_) { continue; } auto itemCrossPosition = itemsCrossPosition_.find(mainPositions.first); @@ -240,8 +240,8 @@ void WaterFlowLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) wrapper->GetGeometryNode()->SetMarginFrameOffset(currentOffset); wrapper->Layout(); // recode restore info - if (item.first == layoutInfo_.startIndex_) { - layoutInfo_.storedOffset_ = mainOffset; + if (item.first == layoutInfo_->startIndex_) { + layoutInfo_->storedOffset_ = mainOffset; } if (NonNegative(mainOffset + item.second.second)) { @@ -253,16 +253,16 @@ void WaterFlowLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) } } } - layoutInfo_.firstIndex_ = firstIndex; + layoutInfo_->firstIndex_ = firstIndex; LayoutFooter(layoutWrapper, childFrameOffset, layoutProperty->IsReverse()); } void WaterFlowLayoutAlgorithm::LayoutFooter(LayoutWrapper* layoutWrapper, const OffsetF& childFrameOffset, bool reverse) { - if (layoutInfo_.itemEnd_ && layoutInfo_.footerIndex_ >= 0) { - auto footer = layoutWrapper->GetOrCreateChildByIndex(layoutInfo_.footerIndex_); + if (layoutInfo_->itemEnd_ && layoutInfo_->footerIndex_ >= 0) { + auto footer = layoutWrapper->GetOrCreateChildByIndex(layoutInfo_->footerIndex_); auto footerOffset = childFrameOffset; - auto mainOffset = layoutInfo_.GetMaxMainHeight() + layoutInfo_.currentOffset_; + auto mainOffset = layoutInfo_->GetMaxMainHeight() + layoutInfo_->currentOffset_; if (reverse) { mainOffset = mainSize_ - footerMainSize_ - mainOffset; } @@ -274,35 +274,36 @@ void WaterFlowLayoutAlgorithm::LayoutFooter(LayoutWrapper* layoutWrapper, const FlowItemPosition WaterFlowLayoutAlgorithm::GetItemPosition(int32_t index) { - auto crossIndex = layoutInfo_.GetCrossIndex(index); + auto crossIndex = layoutInfo_->GetCrossIndex(index); // already in layoutInfo if (crossIndex != -1) { - return { crossIndex, layoutInfo_.GetStartMainPos(crossIndex, index) }; + return { crossIndex, layoutInfo_->GetStartMainPos(crossIndex, index) }; } - auto itemIndex = layoutInfo_.GetCrossIndexForNextItem(layoutInfo_.GetSegment(index)); + auto itemIndex = layoutInfo_->GetCrossIndexForNextItem(layoutInfo_->GetSegment(index)); if (itemIndex.lastItemIndex < 0) { return { itemIndex.crossIndex, 0.0f }; } - auto mainHeight = layoutInfo_.GetMainHeight(itemIndex.crossIndex, itemIndex.lastItemIndex); + auto mainHeight = layoutInfo_->GetMainHeight(itemIndex.crossIndex, itemIndex.lastItemIndex); return { itemIndex.crossIndex, mainHeight + mainGap_ }; } void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layoutWrapper) { - if (layoutInfo_.currentOffset_ >= 0) { + if (layoutInfo_->currentOffset_ >= 0) { if (!canOverScroll_) { - layoutInfo_.currentOffset_ = 0; + layoutInfo_->currentOffset_ = 0; } - layoutInfo_.itemStart_ = true; + layoutInfo_->itemStart_ = true; } else { - layoutInfo_.itemStart_ = false; + layoutInfo_->itemStart_ = false; } - layoutInfo_.UpdateStartIndex(); + + layoutInfo_->UpdateStartIndex(); auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - auto currentIndex = layoutInfo_.startIndex_; + auto currentIndex = layoutInfo_->startIndex_; auto position = GetItemPosition(currentIndex); bool fill = false; - while (LessNotEqual(position.startMainPos + layoutInfo_.currentOffset_, mainSize) || layoutInfo_.jumpIndex_ >= 0) { + while (LessNotEqual(position.startMainPos + layoutInfo_->currentOffset_, mainSize) || layoutInfo_->jumpIndex_ >= 0) { auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(GetChildIndexWithFooter(currentIndex)); if (!itemWrapper) { break; @@ -315,83 +316,83 @@ void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layou { itemCrossSize->second, mainSize_, axis_ }, layoutProperty, itemWrapper)); auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize(); auto itemHeight = GetMainAxisSize(itemSize, axis_); - auto item = layoutInfo_.items_[0][position.crossIndex].find(currentIndex); - if (item == layoutInfo_.items_[0][position.crossIndex].end()) { - layoutInfo_.items_[0][position.crossIndex][currentIndex] = + auto item = layoutInfo_->items_[0][position.crossIndex].find(currentIndex); + if (item == layoutInfo_->items_[0][position.crossIndex].end()) { + layoutInfo_->items_[0][position.crossIndex][currentIndex] = std::make_pair(position.startMainPos, itemHeight); } else { if (item->second.second != itemHeight) { item->second.second = itemHeight; - layoutInfo_.ClearCacheAfterIndex(currentIndex); + layoutInfo_->ClearCacheAfterIndex(currentIndex); } } - if (layoutInfo_.jumpIndex_ == currentIndex) { - layoutInfo_.currentOffset_ = - layoutInfo_.JumpToTargetAlign(layoutInfo_.items_[0][position.crossIndex][currentIndex]); - layoutInfo_.currentOffset_ += layoutInfo_.restoreOffset_; + if (layoutInfo_->jumpIndex_ == currentIndex) { + layoutInfo_->currentOffset_ = + layoutInfo_->JumpToTargetAlign(layoutInfo_->items_[0][position.crossIndex][currentIndex]); + layoutInfo_->currentOffset_ += layoutInfo_->restoreOffset_; // restoreOffSet only be used once - layoutInfo_.restoreOffset_ = 0.0f; - layoutInfo_.align_ = ScrollAlign::START; - layoutInfo_.jumpIndex_ = EMPTY_JUMP_INDEX; - layoutInfo_.itemStart_ = false; + layoutInfo_->restoreOffset_ = 0.0f; + layoutInfo_->align_ = ScrollAlign::START; + layoutInfo_->jumpIndex_ = EMPTY_JUMP_INDEX; + layoutInfo_->itemStart_ = false; } position = GetItemPosition(++currentIndex); fill = true; } - layoutInfo_.endIndex_ = !fill ? currentIndex : currentIndex - 1; + layoutInfo_->endIndex_ = !fill ? currentIndex : currentIndex - 1; - layoutInfo_.itemEnd_ = GetChildIndexWithFooter(currentIndex) == layoutInfo_.childrenCount_; - if (layoutInfo_.itemEnd_) { + layoutInfo_->itemEnd_ = GetChildIndexWithFooter(currentIndex) == layoutInfo_->childrenCount_; + if (layoutInfo_->itemEnd_) { ModifyCurrentOffsetWhenReachEnd(mainSize, layoutWrapper); } else { - layoutInfo_.offsetEnd_ = false; + layoutInfo_->offsetEnd_ = false; } } void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, LayoutWrapper* layoutWrapper) { - auto maxItemHeight = layoutInfo_.GetMaxMainHeight(); - if (layoutInfo_.footerIndex_ >= 0) { + auto maxItemHeight = layoutInfo_->GetMaxMainHeight(); + if (layoutInfo_->footerIndex_ >= 0) { footerMainStartPos_ = maxItemHeight; footerMainSize_ = MeasureFooter(layoutWrapper); maxItemHeight += footerMainSize_; } - layoutInfo_.maxHeight_ = maxItemHeight; + layoutInfo_->maxHeight_ = maxItemHeight; if (mainSize >= maxItemHeight) { if (!canOverScroll_) { - layoutInfo_.currentOffset_ = 0; + layoutInfo_->currentOffset_ = 0; } - layoutInfo_.offsetEnd_ = true; - layoutInfo_.itemStart_ = true; + layoutInfo_->offsetEnd_ = true; + layoutInfo_->itemStart_ = true; return; } - if (layoutInfo_.currentOffset_ + maxItemHeight <= mainSize) { - layoutInfo_.offsetEnd_ = true; + if (layoutInfo_->currentOffset_ + maxItemHeight <= mainSize) { + layoutInfo_->offsetEnd_ = true; if (!canOverScroll_) { - layoutInfo_.currentOffset_ = mainSize - maxItemHeight; + layoutInfo_->currentOffset_ = mainSize - maxItemHeight; } - auto oldStart = layoutInfo_.startIndex_; - layoutInfo_.UpdateStartIndex(); + auto oldStart = layoutInfo_->startIndex_; + layoutInfo_->UpdateStartIndex(); // lazyforeach - for (auto i = oldStart; i >= layoutInfo_.startIndex_; i--) { + for (auto i = oldStart; i >= layoutInfo_->startIndex_; i--) { auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(GetChildIndexWithFooter(i)); CHECK_NULL_VOID(itemWrapper); auto layoutProperty = AceType::DynamicCast(layoutWrapper->GetLayoutProperty()); - float crossSize = itemsCrossSize_.at(layoutInfo_.GetCrossIndex(i)); + float crossSize = itemsCrossSize_.at(layoutInfo_->GetCrossIndex(i)); itemWrapper->Measure(WaterFlowLayoutUtils::CreateChildConstraint( { crossSize, mainSize_, axis_ }, layoutProperty, itemWrapper)); } } else { - layoutInfo_.offsetEnd_ = false; + layoutInfo_->offsetEnd_ = false; } } float WaterFlowLayoutAlgorithm::MeasureFooter(LayoutWrapper* layoutWrapper) { - auto footer = layoutWrapper->GetOrCreateChildByIndex(layoutInfo_.footerIndex_); + auto footer = layoutWrapper->GetOrCreateChildByIndex(layoutInfo_->footerIndex_); auto layoutProperty = layoutWrapper->GetLayoutProperty(); auto footerConstraint = layoutProperty->CreateChildConstraint(); footer->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_CONTENT); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h index 7463d92d47d..38b9eef3325 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h @@ -25,25 +25,22 @@ class WaterFlowLayoutBase : public LayoutAlgorithm { DECLARE_ACE_TYPE(WaterFlowLayoutBase, LayoutAlgorithm); public: - virtual WaterFlowLayoutInfo GetLayoutInfo() = 0; virtual void SetCanOverScroll(bool canOverScroll) = 0; }; +enum class WaterFlowLayoutMode { TOP_DOWN = 0, SLIDING_WINDOW = 1 }; + class ACE_EXPORT WaterFlowLayoutAlgorithm : public WaterFlowLayoutBase { DECLARE_ACE_TYPE(WaterFlowLayoutAlgorithm, WaterFlowLayoutBase); public: - explicit WaterFlowLayoutAlgorithm(WaterFlowLayoutInfo layoutInfo) : layoutInfo_(std::move(layoutInfo)) {} + explicit WaterFlowLayoutAlgorithm(const RefPtr& layoutInfo) : layoutInfo_(layoutInfo) {} ~WaterFlowLayoutAlgorithm() override = default; void Measure(LayoutWrapper* layoutWrapper) override; void Layout(LayoutWrapper* layoutWrapper) override; - WaterFlowLayoutInfo GetLayoutInfo() override - { - return std::move(layoutInfo_); - } void SetCanOverScroll(bool canOverScroll) override { canOverScroll_ = canOverScroll; @@ -59,7 +56,7 @@ private: const RefPtr& layoutProperty, const SizeF& frameSize, int32_t childrenCount); int32_t GetChildIndexWithFooter(int32_t index) const { - return index + layoutInfo_.footerIndex_ + 1; + return index + layoutInfo_->footerIndex_ + 1; } float MeasureFooter(LayoutWrapper* layoutWrapper); void LayoutFooter(LayoutWrapper* layoutWrapper, const OffsetF& childFrameOffset, bool reverse); @@ -68,6 +65,8 @@ private: std::map itemsCrossPosition_; Axis axis_ = Axis::VERTICAL; + RefPtr layoutInfo_; + float mainGap_ = 0.0f; float crossGap_ = 0.0f; float mainSize_ = 0.0f; @@ -75,7 +74,6 @@ private: float footerMainStartPos_ = 0.0f; bool canOverScroll_ = false; bool skipMeasure_ = false; - WaterFlowLayoutInfo layoutInfo_; }; } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_ALGORITHM_H diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index d1e85250048..7d232f237d1 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -157,6 +157,38 @@ float WaterFlowLayoutInfo::GetStartMainPos(int32_t crossIndex, int32_t itemIndex return result; } +OverScrollOffset WaterFlowLayoutInfo::GetOverScrolledDelta(float delta) const +{ + OverScrollOffset offset = { 0, 0 }; + if (startIndex_ == 0) { + auto startPos = currentOffset_; + auto newStartPos = startPos + delta; + if (startPos > 0 && newStartPos > 0) { + offset.start = delta; + } + if (startPos > 0 && newStartPos <= 0) { + offset.start = -startPos; + } + if (startPos <= 0 && newStartPos > 0) { + offset.start = newStartPos; + } + } + if (itemEnd_) { + auto endPos = currentOffset_ + maxHeight_; + auto newEndPos = endPos + delta; + if (endPos < lastMainSize_ && newEndPos < lastMainSize_) { + offset.end = delta; + } + if (endPos < lastMainSize_ && newEndPos >= lastMainSize_) { + offset.end = lastMainSize_ - endPos; + } + if (endPos >= lastMainSize_ && newEndPos < lastMainSize_) { + offset.end = newEndPos - lastMainSize_; + } + } + return offset; +} + bool WaterFlowLayoutInfo::IsAllCrossReachEnd(float mainSize) const { bool result = true; @@ -498,39 +530,57 @@ void WaterFlowLayoutInfo::PrintWaterFlowItems() const } } -float WaterFlowLayoutInfo::JumpToTargetAlign(const std::pair& item) const +void WaterFlowLayoutInfo::UpdateOffset(float delta) { - float targetPosition = 0.0f; + prevOffset_ = currentOffset_; + currentOffset_ += delta; +} + +float WaterFlowLayoutInfo::CalcTargetPosition(int32_t idx, int32_t crossIdx) const +{ + auto item = items_[GetSegment(idx)].at(crossIdx).at(idx); + float res = 0.0f; ScrollAlign align = align_; switch (align) { case ScrollAlign::START: - targetPosition = -item.first; + res = item.first; break; case ScrollAlign::END: - targetPosition = lastMainSize_ - (item.first + item.second); + res = -(lastMainSize_ - (item.first + item.second)); break; case ScrollAlign::AUTO: if (currentOffset_ + item.first < 0) { - targetPosition = -item.first; + res = item.first; } else if (currentOffset_ + item.first + item.second > lastMainSize_) { - targetPosition = lastMainSize_ - (item.first + item.second); + res = -(lastMainSize_ - (item.first + item.second)); } else { - targetPosition = currentOffset_; + res = -currentOffset_; } break; case ScrollAlign::CENTER: - targetPosition = -item.first + (lastMainSize_ - item.second) * HALF; + res = -(-item.first + (lastMainSize_ - item.second) / 2); break; default: break; } - return targetPosition; + return res; +} +bool WaterFlowLayoutInfo::OutOfBounds() const +{ + bool outOfStart = itemStart_ && Positive(currentOffset_); + bool outOfEnd = offsetEnd_ && LessNotEqual(currentOffset_ + maxHeight_, lastMainSize_); + return outOfStart || outOfEnd; } -void WaterFlowLayoutInfo::JumpTo(const std::pair& item) +float WaterFlowLayoutInfo::CalcOverScroll(float mainSize, float delta) const { - currentOffset_ = JumpToTargetAlign(item); - align_ = ScrollAlign::START; - jumpIndex_ = EMPTY_JUMP_INDEX; + float res = 0; + if (itemStart_) { + res = offset() + delta; + } + if (offsetEnd_) { + res = mainSize - (GetMaxMainHeight() + offset() - delta); + } + return res; } } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index fe1a325fb18..71541a39176 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -37,13 +37,27 @@ struct FlowItemPosition { float startMainPos = 0; }; -class WaterFlowLayoutInfo: public WaterFlowLayoutInfoBase { +class WaterFlowLayoutInfo : public WaterFlowLayoutInfoBase { + DECLARE_ACE_TYPE(WaterFlowLayoutInfo, WaterFlowLayoutInfoBase); + public: - int32_t GetCrossIndex(int32_t itemIndex) const; - void UpdateStartIndex(); + WaterFlowLayoutInfo() = default; + ~WaterFlowLayoutInfo() override = default; + + float offset() const override + { + return currentOffset_; + } + int32_t firstIdx() const override + { + return firstIndex_; + } + int32_t GetCrossIndex(int32_t itemIndex) const override; + + void UpdateStartIndex() override; int32_t GetEndIndexByOffset(float offset) const; float GetMaxMainHeight() const; - float GetContentHeight() const; + float GetContentHeight() const override; bool IsAllCrossReachEnd(float mainSize) const; /** @@ -56,15 +70,28 @@ public: float GetMainHeight(int32_t crossIndex, int32_t itemIndex) const; float GetStartMainPos(int32_t crossIndex, int32_t itemIndex) const; - void Reset(); + void Reset() override; void Reset(int32_t resetFrom); - int32_t GetCrossCount() const; - int32_t GetMainCount() const; + int32_t GetCrossCount() const override; + int32_t GetMainCount() const override; void ClearCacheAfterIndex(int32_t currentIndex); - bool ReachStart(float prevOffset, bool firstLayout) const; - bool ReachEnd(float prevOffset) const; - float JumpToTargetAlign(const std::pair& item) const; + bool ReachStart(float prevOffset, bool firstLayout) const override; + bool ReachEnd(float prevOffset) const override; + bool OutOfBounds() const override; + + OverScrollOffset GetOverScrolledDelta(float delta) const override; + float CalcOverScroll(float mainSize, float delta) const override; + + void UpdateOffset(float delta) override; + + float CalcTargetPosition(int32_t idx, int32_t crossIdx) const override; + + float GetDelta(float prevPos) const override + { + return prevPos - currentOffset_; + } + void JumpTo(const std::pair& item); /** @@ -81,7 +108,7 @@ public: * @param sections vector of Sections info. * @param start index of the first modified section, all sections prior to [start] remain the same. */ - void InitSegments(const std::vector& sections, int32_t start); + void InitSegments(const std::vector& sections, int32_t start) override; /** * @brief Initialize margin of each section, along with segmentStartPos_, which depends on margin_. @@ -134,32 +161,14 @@ public: */ void Sync(float mainSize, bool overScroll); - Axis axis_ = Axis::VERTICAL; float currentOffset_ = 0.0f; float prevOffset_ = 0.0f; float lastMainSize_ = 0.0f; // 0.0f until itemEnd_ is true float maxHeight_ = 0.0f; - // store offset for distributed migration - float storedOffset_ = 0.0f; - float restoreOffset_ = 0.0f; - - bool itemStart_ = false; - bool itemEnd_ = false; // last item is partially in viewport - bool offsetEnd_ = false; // last item's bottom is in viewport - - int32_t jumpIndex_ = EMPTY_JUMP_INDEX; - - ScrollAlign align_ = ScrollAlign::START; - - int32_t startIndex_ = 0; - int32_t endIndex_ = -1; - int32_t footerIndex_ = -1; - int32_t childrenCount_ = 0; // first index for onScrollIndex int32_t firstIndex_ = 0; - std::optional targetIndex_; // Map structure: [crossIndex, [index, {mainOffset, itemMainSize}]], using ItemMap = std::map>>; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index 084f502eb60..5f2765cdd65 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -16,14 +16,96 @@ #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_BASE_H #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_BASE_H +#include "base/memory/ace_type.h" +#include "base/utils/noncopyable.h" #include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/pattern/scrollable/scrollable.h" +#include "core/components_ng/pattern/waterflow/water_flow_sections.h" namespace OHOS::Ace::NG { - constexpr int32_t EMPTY_JUMP_INDEX = -2; -class WaterFlowLayoutInfoBase { +class WaterFlowLayoutInfoBase : public AceType { + DECLARE_ACE_TYPE(WaterFlowLayoutInfoBase, AceType); + public: + WaterFlowLayoutInfoBase() = default; + ~WaterFlowLayoutInfoBase() override = default; + + /* PURE GETTERs */ + virtual float offset() const = 0; + virtual int32_t firstIdx() const = 0; // for compatibility + + virtual void UpdateOffset(float delta) = 0; + + /** + * @brief Get which cross-axis lane the item is in. + * + * @param itemIndex + * @return lane index + */ + virtual int32_t GetCrossIndex(int32_t itemIndex) const = 0; + + // implementation of WaterFlowPattern::GetOverScrollOffset + // returns the portion of [delta] that's in overScroll range + virtual OverScrollOffset GetOverScrolledDelta(float delta) const = 0; + + /** + * @param mainSize of viewport. + * @param delta change in content offset. + * @return amount of overScroll (distance to edge) after applying delta. + */ + virtual float CalcOverScroll(float mainSize, float delta) const = 0; + + /** + * @brief Check if WaterFlow just reached content top from the recent layout by comparing current position to [prevPos]. + * For triggering events. + * + * @param prevPos previous layout position. + * @param firstLayout check this to prevent emitting ReachStart on the initial layout. + * @return true if current position just reached content top. + */ + virtual bool ReachStart(float prevPos, bool firstLayout) const = 0; + /** + * @brief Check if WaterFlow just reached content bottom from the recent layout by comparing current position to [prevPos]. + * For triggering events. + * + * @param prevPos previous layout position. + * @return true if current position just reached content bottom. + */ + virtual bool ReachEnd(float prevPos) const = 0; + + virtual bool OutOfBounds() const = 0; + + /** + * @return total height of all recorded items. + */ + virtual float GetContentHeight() const = 0; + + /** + * @brief Get target item's position in order to perform scrollTo animation. + * + * @param idx item's index. + * @param crossIdx item's cross-axis lane index. + * @return position + */ + virtual float CalcTargetPosition(int32_t idx, int32_t crossIdx) const = 0; + + /** + * @return change in position, comparing to [prevPos] + */ + virtual float GetDelta(float prevPos) const = 0; + + virtual int32_t GetMainCount() const = 0; + virtual int32_t GetCrossCount() const = 0; + + virtual void Reset() = 0; + + // for compatibility + virtual void InitSegments(const std::vector& sections, int32_t start) {} + // for compatibility + virtual void UpdateStartIndex() {}; + Axis axis_ = Axis::VERTICAL; bool itemStart_ = false; @@ -38,6 +120,12 @@ public: int32_t endIndex_ = -1; int32_t footerIndex_ = -1; int32_t childrenCount_ = 0; + + // store offset for distributed migration + float storedOffset_ = 0.0f; + float restoreOffset_ = 0.0f; + + ACE_DISALLOW_COPY_AND_MOVE(WaterFlowLayoutInfoBase); }; } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp index 330eee1bbba..50587c901c9 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp @@ -42,19 +42,19 @@ std::string WaterFlowLayoutUtils::PreParseArgs(const std::string& args) return rowsArgs; } -FlowItemPosition WaterFlowLayoutUtils::GetItemPosition(const WaterFlowLayoutInfo& info, int32_t index, float mainGap) +FlowItemPosition WaterFlowLayoutUtils::GetItemPosition(const RefPtr& info, int32_t index, float mainGap) { - auto crossIndex = info.GetCrossIndex(index); + auto crossIndex = info->GetCrossIndex(index); // already in layoutInfo if (crossIndex != -1) { - return { crossIndex, info.GetStartMainPos(crossIndex, index) }; + return { crossIndex, info->GetStartMainPos(crossIndex, index) }; } - int32_t segment = info.GetSegment(index); - auto itemIndex = info.GetCrossIndexForNextItem(segment); + int32_t segment = info->GetSegment(index); + auto itemIndex = info->GetCrossIndexForNextItem(segment); if (itemIndex.lastItemIndex < 0) { - return { itemIndex.crossIndex, info.segmentStartPos_[segment] }; + return { itemIndex.crossIndex, info->segmentStartPos_[segment] }; } - auto mainHeight = info.GetMainHeight(itemIndex.crossIndex, itemIndex.lastItemIndex); + auto mainHeight = info->GetMainHeight(itemIndex.crossIndex, itemIndex.lastItemIndex); return { itemIndex.crossIndex, mainHeight + mainGap }; } @@ -118,4 +118,17 @@ LayoutConstraintF WaterFlowLayoutUtils::CreateChildConstraint( return itemConstraint; } + +std::pair WaterFlowLayoutUtils::PreMeasureSelf(LayoutWrapper* wrapper, Axis axis) +{ + const auto& props = wrapper->GetLayoutProperty(); + auto size = CreateIdealSize(props->GetLayoutConstraint().value(), axis, props->GetMeasureType(), true); + auto matchChildren = GreaterOrEqualToInfinity(GetMainAxisSize(size, axis)); + if (!matchChildren) { + wrapper->GetGeometryNode()->SetFrameSize(size); + } + MinusPaddingToSize(props->CreatePaddingAndBorder(), size); + wrapper->GetGeometryNode()->SetContentSize(size); + return { size, matchChildren }; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h index 7bc6ee5e28a..77423663f90 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h @@ -24,7 +24,7 @@ namespace OHOS::Ace::NG { class WaterFlowLayoutUtils { public: static std::string PreParseArgs(const std::string& args); - static FlowItemPosition GetItemPosition(const WaterFlowLayoutInfo& info, int32_t index, float mainGap); + static FlowItemPosition GetItemPosition(const RefPtr& info, int32_t index, float mainGap); struct ConstraintParams { float crossSize = 0.0f; @@ -33,6 +33,13 @@ public: }; static LayoutConstraintF CreateChildConstraint(const ConstraintParams& params, const RefPtr& props, const RefPtr& child); + + /** + * @brief Measure self before measuring children. + * + * @return [idealSize given by parent, whether measure is successful (need to adapt to children size if not)]. + */ + static std::pair PreMeasureSelf(LayoutWrapper* wrapper, Axis axis); }; } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_UTILS_H diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index b479b82bed5..dbbff545a24 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -17,7 +17,9 @@ #include "base/utils/utils.h" #include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" #include "core/components_ng/pattern/waterflow/water_flow_paint_method.h" #include "core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" @@ -47,31 +49,24 @@ bool WaterFlowPattern::UpdateCurrentOffset(float delta, int32_t source) FireAndCleanScrollingListener(); if (GetScrollEdgeEffect()) { // over scroll in drag update from normal to over scroll. - float overScroll = 0.0f; - if (layoutInfo_.itemStart_) { - overScroll = layoutInfo_.currentOffset_ + delta; - } - if (layoutInfo_.offsetEnd_) { - overScroll = GetMainContentSize() - (layoutInfo_.GetMaxMainHeight() + layoutInfo_.currentOffset_ - delta); - } + float overScroll = layoutInfo_->CalcOverScroll(GetMainContentSize(), delta); if (source == SCROLL_FROM_UPDATE) { auto friction = ScrollablePattern::CalculateFriction(std::abs(overScroll) / GetMainContentSize()); delta *= friction; } } else { - if (layoutInfo_.itemStart_ && delta > 0) { + if (layoutInfo_->itemStart_ && delta > 0) { return false; } - if (layoutInfo_.offsetEnd_ && delta < 0) { + if (layoutInfo_->offsetEnd_ && delta < 0) { return false; } - if (GreatNotEqual(delta, 0.0f)) { - delta = std::min(delta, -layoutInfo_.currentOffset_); + if (layoutMode_ == WaterFlowLayoutMode::TOP_DOWN && GreatNotEqual(delta, 0.0f)) { + delta = std::min(delta, -layoutInfo_->offset()); } } - auto userOffset = FireOnWillScroll(-delta); - layoutInfo_.prevOffset_ = layoutInfo_.currentOffset_; - layoutInfo_.currentOffset_ -= userOffset; + float userOffset = FireOnWillScroll(-delta); + layoutInfo_->UpdateOffset(-userOffset); host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); return true; }; @@ -82,11 +77,11 @@ bool WaterFlowPattern::IsScrollable() const } bool WaterFlowPattern::IsAtTop() const { - return layoutInfo_.itemStart_; + return layoutInfo_->itemStart_; }; bool WaterFlowPattern::IsAtBottom() const { - return layoutInfo_.offsetEnd_; + return layoutInfo_->offsetEnd_; }; bool WaterFlowPattern::IsReverse() const { @@ -98,34 +93,7 @@ bool WaterFlowPattern::IsReverse() const } OverScrollOffset WaterFlowPattern::GetOverScrollOffset(double delta) const { - OverScrollOffset offset = { 0, 0 }; - if (layoutInfo_.startIndex_ == 0) { - auto startPos = layoutInfo_.currentOffset_; - auto newStartPos = startPos + delta; - if (startPos > 0 && newStartPos > 0) { - offset.start = delta; - } - if (startPos > 0 && newStartPos <= 0) { - offset.start = -startPos; - } - if (startPos <= 0 && newStartPos > 0) { - offset.start = newStartPos; - } - } - if (layoutInfo_.itemEnd_) { - auto endPos = layoutInfo_.currentOffset_ + layoutInfo_.maxHeight_; - auto newEndPos = endPos + delta; - if (endPos < layoutInfo_.lastMainSize_ && newEndPos < layoutInfo_.lastMainSize_) { - offset.end = delta; - } - if (endPos < layoutInfo_.lastMainSize_ && newEndPos >= layoutInfo_.lastMainSize_) { - offset.end = layoutInfo_.lastMainSize_ - endPos; - } - if (endPos >= layoutInfo_.lastMainSize_ && newEndPos < layoutInfo_.lastMainSize_) { - offset.end = newEndPos - layoutInfo_.lastMainSize_; - } - } - return offset; + return layoutInfo_->GetOverScrolledDelta(static_cast(delta)); } void WaterFlowPattern::UpdateScrollBarOffset() @@ -138,25 +106,28 @@ void WaterFlowPattern::UpdateScrollBarOffset() auto geometryNode = host->GetGeometryNode(); auto viewSize = geometryNode->GetFrameSize(); auto overScroll = 0.0f; - if (Positive(layoutInfo_.currentOffset_)) { - overScroll = layoutInfo_.currentOffset_; + auto info = DynamicCast(layoutInfo_); + if (Positive(info->currentOffset_)) { + overScroll = info->currentOffset_; } else { - overScroll = GetMainContentSize() - (layoutInfo_.GetContentHeight() + layoutInfo_.currentOffset_); + overScroll = GetMainContentSize() - (info->GetContentHeight() + info->currentOffset_); overScroll = Positive(overScroll) ? overScroll : 0.0f; } HandleScrollBarOutBoundary(overScroll); - UpdateScrollBarRegion(-layoutInfo_.currentOffset_, layoutInfo_.GetContentHeight(), - Size(viewSize.Width(), viewSize.Height()), Offset(0.0f, 0.0f)); + UpdateScrollBarRegion( + -info->currentOffset_, info->GetContentHeight(), Size(viewSize.Width(), viewSize.Height()), Offset(0.0f, 0.0f)); }; RefPtr WaterFlowPattern::CreateLayoutAlgorithm() { if (targetIndex_.has_value()) { - layoutInfo_.targetIndex_ = targetIndex_; + layoutInfo_->targetIndex_ = targetIndex_; } RefPtr algorithm; if (sections_ || SystemProperties::WaterFlowUseSegmentedLayout()) { - algorithm = MakeRefPtr(layoutInfo_); + algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); + } else if (layoutMode_ == WaterFlowLayoutMode::SLIDING_WINDOW) { + algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); } else { int32_t footerIndex = -1; auto footer = footer_.Upgrade(); @@ -166,8 +137,8 @@ RefPtr WaterFlowPattern::CreateLayoutAlgorithm() footerIndex = 0; } } - layoutInfo_.footerIndex_ = footerIndex; - algorithm = MakeRefPtr(layoutInfo_); + layoutInfo_->footerIndex_ = footerIndex; + algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); } algorithm->SetCanOverScroll(CanOverScroll(GetScrollSource())); return algorithm; @@ -205,7 +176,7 @@ void WaterFlowPattern::OnModifyDone() auto paintProperty = GetPaintProperty(); CHECK_NULL_VOID(paintProperty); - if (paintProperty->GetScrollBarProperty()) { + if (layoutMode_ != WaterFlowLayoutMode::SLIDING_WINDOW && paintProperty->GetScrollBarProperty()) { SetScrollBar(paintProperty->GetScrollBarProperty()); } SetAccessibilityAction(); @@ -226,45 +197,44 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir CHECK_NULL_RETURN(layoutAlgorithmWrapper, false); auto layoutAlgorithm = DynamicCast(layoutAlgorithmWrapper->GetLayoutAlgorithm()); CHECK_NULL_RETURN(layoutAlgorithm, false); - auto layoutInfo = layoutAlgorithm->GetLayoutInfo(); auto host = GetHost(); CHECK_NULL_RETURN(host, false); auto eventHub = host->GetEventHub(); CHECK_NULL_RETURN(eventHub, false); auto onScroll = eventHub->GetOnScroll(); - PrintOffsetLog(AceLogTag::ACE_WATERFLOW, host->GetId(), prevOffset_ - layoutInfo.currentOffset_); + float delta = layoutInfo_->GetDelta(prevOffset_); + PrintOffsetLog(AceLogTag::ACE_WATERFLOW, host->GetId(), delta); if (onScroll) { - FireOnScroll(prevOffset_ - layoutInfo.currentOffset_, onScroll); + FireOnScroll(delta, onScroll); } auto onDidScroll = eventHub->GetOnDidScroll(); if (onDidScroll) { - FireOnScroll(prevOffset_ - layoutInfo.currentOffset_, onDidScroll); + FireOnScroll(delta, onDidScroll); } - bool indexChanged = - layoutInfo_.firstIndex_ != layoutInfo.firstIndex_ || layoutInfo_.endIndex_ != layoutInfo.endIndex_; + bool indexChanged = itemRange_.first != layoutInfo_->firstIdx() || itemRange_.second != layoutInfo_->endIndex_; if (indexChanged) { auto onScrollIndex = eventHub->GetOnScrollIndex(); + itemRange_ = { layoutInfo_->firstIdx(), layoutInfo_->endIndex_ }; if (onScrollIndex) { - onScrollIndex(layoutInfo.firstIndex_, layoutInfo.endIndex_); + onScrollIndex(layoutInfo_->firstIdx(), layoutInfo_->endIndex_); } } auto onReachStart = eventHub->GetOnReachStart(); - if (onReachStart && layoutInfo.ReachStart(prevOffset_, !isInitialized_)) { + if (onReachStart && layoutInfo_->ReachStart(prevOffset_, !isInitialized_)) { onReachStart(); } auto onReachEnd = eventHub->GetOnReachEnd(); - if (onReachEnd && layoutInfo.ReachEnd(prevOffset_)) { + if (onReachEnd && layoutInfo_->ReachEnd(prevOffset_)) { onReachEnd(); } OnScrollStop(eventHub->GetOnScrollStop()); - layoutInfo_ = std::move(layoutInfo); if (targetIndex_.has_value()) { ScrollToTargetIndex(targetIndex_.value()); targetIndex_.reset(); } - layoutInfo_.UpdateStartIndex(); - prevOffset_ = layoutInfo_.currentOffset_; + layoutInfo_->UpdateStartIndex(); + prevOffset_ = layoutInfo_->offset(); UpdateScrollBarOffset(); CheckScrollable(); @@ -281,12 +251,12 @@ bool WaterFlowPattern::ScrollToTargetIndex(int32_t index) auto totalItemCount = host->TotalChildCount(); index = totalItemCount - 1; } - auto crossIndex = layoutInfo_.GetCrossIndex(index); + auto crossIndex = layoutInfo_->GetCrossIndex(index); if (crossIndex == -1) { return false; } - auto item = layoutInfo_.items_[layoutInfo_.GetSegment(index)].at(crossIndex).at(index); - float targetPosition = -layoutInfo_.JumpToTargetAlign(item); + auto item = layoutInfo_->items_[layoutInfo_->GetSegment(index)].at(crossIndex).at(index); + float targetPosition = -layoutInfo_->JumpToTargetAlign(item); ScrollablePattern::AnimateTo(targetPosition, -1, nullptr, true); return true; } @@ -306,9 +276,9 @@ bool WaterFlowPattern::UpdateStartIndex(int32_t index) auto host = GetHost(); CHECK_NULL_RETURN(host, false); auto childCount = host->GetTotalChildCount(); - layoutInfo_.jumpIndex_ = (index == LAST_ITEM ? childCount - 1 : index); + layoutInfo_->jumpIndex_ = (index == LAST_ITEM ? childCount - 1 : index); //if target index is footer, fix align because it will jump after fillViewport. - if (layoutInfo_.footerIndex_ == 0 && layoutInfo_.jumpIndex_ == childCount - 1) { + if (layoutInfo_->footerIndex_ == 0 && layoutInfo_->jumpIndex_ == childCount - 1) { SetScrollAlign(ScrollAlign::END); } host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); @@ -320,7 +290,7 @@ int32_t WaterFlowPattern::GetRows() const auto layoutProperty = GetLayoutProperty(); CHECK_NULL_RETURN(layoutProperty, 0); - return layoutProperty->GetAxis() == Axis::VERTICAL ? layoutInfo_.GetMainCount() : layoutInfo_.GetCrossCount(); + return layoutProperty->GetAxis() == Axis::VERTICAL ? layoutInfo_->GetMainCount() : layoutInfo_->GetCrossCount(); } int32_t WaterFlowPattern::GetColumns() const @@ -328,7 +298,7 @@ int32_t WaterFlowPattern::GetColumns() const auto layoutProperty = GetLayoutProperty(); CHECK_NULL_RETURN(layoutProperty, 0); - return layoutProperty->GetAxis() == Axis::VERTICAL ? layoutInfo_.GetCrossCount() : layoutInfo_.GetMainCount(); + return layoutProperty->GetAxis() == Axis::VERTICAL ? layoutInfo_->GetCrossCount() : layoutInfo_->GetMainCount(); } void WaterFlowPattern::SetAccessibilityAction() @@ -367,7 +337,7 @@ void WaterFlowPattern::ScrollPage(bool reverse, bool smooth) auto mainContentSize = geometryNode->GetPaddingSize().MainSize(axis); if (smooth) { float distance = reverse ? mainContentSize : -mainContentSize; - float position = layoutInfo_.currentOffset_ + distance; + float position = layoutInfo_->offset() + distance; AnimateTo(-position, -1, nullptr, true); } else { UpdateCurrentOffset(reverse ? mainContentSize : -mainContentSize, SCROLL_FROM_JUMP); @@ -398,10 +368,10 @@ void WaterFlowPattern::OnRestoreInfo(const std::string& restoreInfo) Rect WaterFlowPattern::GetItemRect(int32_t index) const { - if (index < 0 || index < layoutInfo_.startIndex_ || index > layoutInfo_.endIndex_) { + if (index < 0 || index < layoutInfo_->startIndex_ || index > layoutInfo_->endIndex_) { return Rect(); } - index += layoutInfo_.footerIndex_ + 1; + index += layoutInfo_->footerIndex_ + 1; auto host = GetHost(); CHECK_NULL_RETURN(host, Rect()); auto item = host->GetChildByIndex(index); @@ -414,6 +384,9 @@ Rect WaterFlowPattern::GetItemRect(int32_t index) const RefPtr WaterFlowPattern::GetOrCreateWaterFlowSections() { + if (layoutMode_ == WaterFlowLayoutMode::SLIDING_WINDOW) { + return nullptr; + } if (sections_) { return sections_; } @@ -441,16 +414,19 @@ RefPtr WaterFlowPattern::GetOrCreateWaterFlowSections() } void WaterFlowPattern::OnSectionChanged(int32_t start) -{ +{ + // SlidingWindow mode should never reach this callback + auto info = DynamicCast(layoutInfo_); + CHECK_NULL_VOID(info); auto host = GetHost(); CHECK_NULL_VOID(host); int32_t childUpdateIdx = host->GetChildrenUpdated(); - if (childUpdateIdx > -1 && layoutInfo_.GetSegment(childUpdateIdx - 1) == start && sections_->IsSpecialUpdate()) { + if (childUpdateIdx > -1 && info->GetSegment(childUpdateIdx - 1) == start && sections_->IsSpecialUpdate()) { // optimize adding or removing children in the last section. Prevent complete reset of that section. ++start; } - layoutInfo_.InitSegments(sections_->GetSectionInfo(), start); - layoutInfo_.margins_.clear(); + info->InitSegments(sections_->GetSectionInfo(), start); + info->margins_.clear(); MarkDirtyNodeSelf(); } @@ -470,7 +446,7 @@ void WaterFlowPattern::OnSectionChangedNow(int32_t start) void WaterFlowPattern::ResetSections() { - layoutInfo_.Reset(); + layoutInfo_->Reset(); sections_.Reset(); MarkDirtyNodeSelf(); } @@ -497,10 +473,7 @@ void WaterFlowPattern::ScrollToIndex(int32_t index, bool smooth, ScrollAlign ali bool WaterFlowPattern::IsOutOfBoundary(bool useCurrentDelta) { - bool outOfStart = layoutInfo_.itemStart_ && Positive(layoutInfo_.currentOffset_); - bool outOfEnd = layoutInfo_.offsetEnd_ && - LessNotEqual(layoutInfo_.currentOffset_ + layoutInfo_.maxHeight_, layoutInfo_.lastMainSize_); - return outOfStart || outOfEnd; + return layoutInfo_->OutOfBounds(); } void WaterFlowPattern::SetEdgeEffectCallback(const RefPtr& scrollEffect) @@ -508,12 +481,12 @@ void WaterFlowPattern::SetEdgeEffectCallback(const RefPtr& scr scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double { auto pattern = weak.Upgrade(); CHECK_NULL_RETURN(pattern, 0.0); - return pattern->layoutInfo_.currentOffset_; + return pattern->layoutInfo_->offset(); }); scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double { auto pattern = weak.Upgrade(); CHECK_NULL_RETURN(pattern, 0.0); - auto leadOffset = pattern->GetMainContentSize() - pattern->layoutInfo_.GetContentHeight(); + auto leadOffset = pattern->GetMainContentSize() - pattern->layoutInfo_->GetContentHeight(); if (pattern->GetAlwaysEnabled() && Positive(leadOffset)) { return 0.0; } @@ -523,7 +496,7 @@ void WaterFlowPattern::SetEdgeEffectCallback(const RefPtr& scr scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double { auto pattern = weak.Upgrade(); CHECK_NULL_RETURN(pattern, 0.0); - auto leadOffset = pattern->GetMainContentSize() - pattern->layoutInfo_.GetContentHeight(); + auto leadOffset = pattern->GetMainContentSize() - pattern->layoutInfo_->GetContentHeight(); if (pattern->GetAlwaysEnabled() && Positive(leadOffset)) { return 0.0; } @@ -570,9 +543,9 @@ bool WaterFlowPattern::NeedRender() void WaterFlowPattern::ResetLayoutInfo() { - layoutInfo_.Reset(); + layoutInfo_->Reset(); if (sections_) { - layoutInfo_.InitSegments(sections_->GetSectionInfo(), 0); + layoutInfo_->InitSegments(sections_->GetSectionInfo(), 0); } } @@ -583,7 +556,7 @@ void WaterFlowPattern::AddFooter(const RefPtr& footer) CHECK_NULL_VOID(host); auto prevFooter = footer_.Upgrade(); if (!prevFooter) { - layoutInfo_.footerIndex_ = 0; + layoutInfo_->footerIndex_ = 0; host->AddChild(footer); } else { host->ReplaceChild(prevFooter, footer); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index cf27e7b5e98..f7d4d9258d7 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -20,7 +20,8 @@ #include "core/components_ng/pattern/waterflow/water_flow_accessibility_property.h" #include "core/components_ng/pattern/waterflow/water_flow_content_modifier.h" #include "core/components_ng/pattern/waterflow/water_flow_event_hub.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/pattern/waterflow/water_flow_sections.h" @@ -41,6 +42,11 @@ public: OverScrollOffset GetOverScrollOffset(double delta) const override; void UpdateScrollBarOffset() override; + void SetLayoutMode(WaterFlowLayoutMode mode) + { + layoutMode_ = mode; + } + RefPtr CreateLayoutAlgorithm() override; RefPtr CreateLayoutProperty() override @@ -75,22 +81,22 @@ public: int32_t GetBeginIndex() const { - return layoutInfo_.startIndex_; + return layoutInfo_->startIndex_; } int32_t GetEndIndex() const { - return layoutInfo_.endIndex_; + return layoutInfo_->endIndex_; } int32_t GetChildrenCount() const { - return layoutInfo_.childrenCount_; + return layoutInfo_->childrenCount_; } float GetTotalOffset() const override { - return -layoutInfo_.currentOffset_; + return -layoutInfo_->offset(); } int32_t GetRows() const; @@ -107,17 +113,17 @@ public: double GetStoredOffset() const { - return layoutInfo_.storedOffset_; + return layoutInfo_->storedOffset_; } void SetRestoreOffset(double restoreOffset) { - layoutInfo_.restoreOffset_ = restoreOffset; + layoutInfo_->restoreOffset_ = restoreOffset; } void SetScrollAlign(ScrollAlign align) { - layoutInfo_.align_ = align; + layoutInfo_->align_ = align; } std::string ProvideRestoreInfo() override; @@ -156,11 +162,13 @@ private: bool ScrollToTargetIndex(int32_t index); bool NeedRender(); std::optional targetIndex_; - WaterFlowLayoutInfo layoutInfo_; + RefPtr layoutInfo_; RefPtr sections_; + WaterFlowLayoutMode layoutMode_ = WaterFlowLayoutMode::TOP_DOWN; float prevOffset_ = 0.0f; SizeF lastSize_; + std::pair itemRange_ = { -1, -1 }; WeakPtr footer_; // clip padding of WaterFlow diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index 6236e9b795d..4a95ac6646d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -32,15 +32,15 @@ namespace OHOS::Ace::NG { namespace { -bool IsDataValid(const WaterFlowLayoutInfo& info) +bool IsDataValid(const RefPtr& info) { - if (info.segmentTails_.empty()) { + if (info->segmentTails_.empty()) { return false; } - if (info.childrenCount_ - 1 != info.segmentTails_.back()) { + if (info->childrenCount_ - 1 != info->segmentTails_.back()) { TAG_LOGW(AceLogTag::ACE_WATERFLOW, "Children count = %{public}d and doesn't match the number provided in Sections, which is %{public}d.", - info.childrenCount_, info.segmentTails_.back() + 1); + info->childrenCount_, info->segmentTails_.back() + 1); return false; } return true; @@ -51,8 +51,8 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) { wrapper_ = wrapper; auto props = DynamicCast(wrapper->GetLayoutProperty()); - info_.axis_ = axis_ = props->GetAxis(); - auto [idealSize, matchChildren] = PreMeasureSelf(); + info_->axis_ = axis_ = props->GetAxis(); + auto [idealSize, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_); Init(idealSize); if (!IsDataValid(info_)) { @@ -64,12 +64,12 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) mainSize_ = GetMainAxisSize(idealSize, axis_); - if (info_.jumpIndex_ != EMPTY_JUMP_INDEX) { - MeasureOnJump(info_.jumpIndex_); - info_.jumpIndex_ = EMPTY_JUMP_INDEX; - } else if (info_.targetIndex_) { - MeasureToTarget(*info_.targetIndex_); - info_.targetIndex_.reset(); + if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { + MeasureOnJump(info_->jumpIndex_); + info_->jumpIndex_ = EMPTY_JUMP_INDEX; + } else if (info_->targetIndex_) { + MeasureToTarget(*info_->targetIndex_); + info_->targetIndex_.reset(); } else { MeasureOnOffset(); } @@ -77,7 +77,7 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) if (matchChildren) { PostMeasureSelf(idealSize); } - info_.lastMainSize_ = mainSize_; + info_->lastMainSize_ = mainSize_; wrapper_->SetCacheCount(props->GetCachedCountValue(1)); } @@ -104,7 +104,7 @@ void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper) auto isRtl = layoutDirection == TextDirection::RTL && axis_ == Axis::VERTICAL; // prepare crossPos for (size_t i = 0; i < segmentCnt; ++i) { - float pos = ((axis_ == Axis::VERTICAL) ? info_.margins_[i].left : info_.margins_[i].top).value_or(0.0f); + float pos = ((axis_ == Axis::VERTICAL) ? info_->margins_[i].left : info_->margins_[i].top).value_or(0.0f); for (const auto& len : itemsCrossSize_[i]) { crossPos[i].push_back(!isRtl ? pos : crossSize - pos - len); pos += len + crossGaps_[i]; @@ -112,13 +112,13 @@ void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper) } bool isReverse = props->IsReverse(); - for (int32_t i = info_.startIndex_; i <= info_.endIndex_; ++i) { - LayoutItem(i, crossPos[info_.GetSegment(i)][info_.itemInfos_[i].crossIdx], initialOffset, isReverse); + for (int32_t i = info_->startIndex_; i <= info_->endIndex_; ++i) { + LayoutItem(i, crossPos[info_->GetSegment(i)][info_->itemInfos_[i].crossIdx], initialOffset, isReverse); } - wrapper_->SetActiveChildRange(info_.startIndex_, info_.endIndex_); + wrapper_->SetActiveChildRange(info_->startIndex_, info_->endIndex_); // for compatibility - info_.firstIndex_ = info_.startIndex_; + info_->firstIndex_ = info_->startIndex_; } namespace { @@ -128,21 +128,21 @@ namespace { * @param info WaterFlowLayoutInfo * @return current StartItem's offset relative to the viewport. */ -float PrepareJump(WaterFlowLayoutInfo& info) +float PrepareJump(const RefPtr& info) { - if (info.endIndex_ == -1) { + if (info->endIndex_ == -1) { // implies that LayoutInfo has already been reset, no need to jump return 0.0f; } - info.jumpIndex_ = std::min(info.startIndex_, info.childrenCount_ - 1); - info.align_ = ScrollAlign::START; - float itemOffset = (info.itemInfos_.size() <= static_cast(info.startIndex_)) - ? info.storedOffset_ - : info.currentOffset_ + info.itemInfos_[info.startIndex_].mainOffset; + info->jumpIndex_ = std::min(info->startIndex_, info->childrenCount_ - 1); + info->align_ = ScrollAlign::START; + float itemOffset = (info->itemInfos_.size() <= static_cast(info->startIndex_)) + ? info->storedOffset_ + : info->currentOffset_ + info->itemInfos_[info->startIndex_].mainOffset; - info.startIndex_ = 0; - info.endIndex_ = -1; - info.currentOffset_ = 0.0f; + info->startIndex_ = 0; + info->endIndex_ = -1; + info->currentOffset_ = 0.0f; return itemOffset; } @@ -150,36 +150,36 @@ float PrepareJump(WaterFlowLayoutInfo& info) void WaterFlowSegmentedLayout::Init(const SizeF& frameSize) { - info_.childrenCount_ = wrapper_->GetTotalChildCount(); - if (info_.childrenCount_ == 0) { + info_->childrenCount_ = wrapper_->GetTotalChildCount(); + if (info_->childrenCount_ == 0) { return; } sections_ = wrapper_->GetHostNode()->GetPattern()->GetSections(); if (sections_) { const auto& sections = sections_->GetSectionInfo(); - if (info_.margins_.empty()) { + if (info_->margins_.empty()) { // empty margins_ implies a segment change auto constraint = wrapper_->GetLayoutProperty()->GetLayoutConstraint(); postJumpOffset_ = PrepareJump(info_); - info_.InitMargins(sections, constraint->scaleProperty, constraint->percentReference.Width()); + info_->InitMargins(sections, constraint->scaleProperty, constraint->percentReference.Width()); } SegmentInit(sections, frameSize); - if (info_.segmentTails_.empty()) { - info_.InitSegments(sections, 0); + if (info_->segmentTails_.empty()) { + info_->InitSegments(sections, 0); } } else { RegularInit(frameSize); - if (info_.footerIndex_ >= 0) { + if (info_->footerIndex_ >= 0) { InitFooter(frameSize.CrossSize(axis_)); } } int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated(); if (updateIdx != -1) { - if (updateIdx <= info_.endIndex_) { + if (updateIdx <= info_->endIndex_) { postJumpOffset_ = PrepareJump(info_); } - info_.ClearCacheAfterIndex(updateIdx - 1); + info_->ClearCacheAfterIndex(updateIdx - 1); wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1); return; } @@ -207,7 +207,7 @@ void WaterFlowSegmentedLayout::SegmentInit( std::swap(crossGaps_[i], mainGaps_[i]); } - const auto& margin = info_.margins_[i]; + const auto& margin = info_->margins_[i]; float crossSize = frameSize.CrossSize(axis_) - (axis_ == Axis::VERTICAL ? margin.Width() : margin.Height()); int32_t crossCnt = options[i].crossCount.value_or(1); itemsCrossSize_[i].resize(crossCnt); @@ -237,10 +237,10 @@ void WaterFlowSegmentedLayout::RegularInit(const SizeF& frameSize) std::pair, bool> cross; if (axis_ == Axis::VERTICAL) { cross = ParseTemplateArgs( - WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGaps_[0], info_.childrenCount_); + WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGaps_[0], info_->childrenCount_); } else { cross = ParseTemplateArgs( - WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGaps_[0], info_.childrenCount_); + WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGaps_[0], info_->childrenCount_); } crossLens = cross.first; if (crossLens.empty()) { @@ -252,18 +252,18 @@ void WaterFlowSegmentedLayout::RegularInit(const SizeF& frameSize) itemsCrossSize_ = { {} }; - if (crossLens.size() < info_.items_[0].size()) { - auto it = info_.items_[0].find(crossLens.size()); - info_.items_[0].erase(it, info_.items_[0].end()); + if (crossLens.size() < info_->items_[0].size()) { + auto it = info_->items_[0].find(crossLens.size()); + info_->items_[0].erase(it, info_->items_[0].end()); } int32_t index = 0; for (const auto& len : crossLens) { itemsCrossSize_[0].push_back(len); - info_.items_[0].try_emplace(index, std::map>()); + info_->items_[0].try_emplace(index, std::map>()); ++index; } - info_.margins_.resize(1); - info_.segmentTails_ = { (info_.footerIndex_ >= 0) ? info_.childrenCount_ - 2 : info_.childrenCount_ - 1 }; + info_->margins_.resize(1); + info_->segmentTails_ = { (info_->footerIndex_ >= 0) ? info_->childrenCount_ - 2 : info_->childrenCount_ - 1 }; } void WaterFlowSegmentedLayout::InitFooter(float crossSize) @@ -271,24 +271,24 @@ void WaterFlowSegmentedLayout::InitFooter(float crossSize) mainGaps_.emplace_back(0.0f); itemsCrossSize_.emplace_back(std::vector { crossSize }); - if (info_.items_.size() == 1) { - info_.items_.emplace_back(); - info_.items_.back().try_emplace(0); + if (info_->items_.size() == 1) { + info_->items_.emplace_back(); + info_->items_.back().try_emplace(0); } - info_.margins_.emplace_back(); - info_.segmentTails_.emplace_back(info_.childrenCount_ - 1); + info_->margins_.emplace_back(); + info_->segmentTails_.emplace_back(info_->childrenCount_ - 1); - if (info_.footerIndex_ != info_.childrenCount_ - 1) { - info_.footerIndex_ = std::min(info_.footerIndex_, info_.childrenCount_ - 1); - info_.ClearCacheAfterIndex(info_.footerIndex_ - 1); + if (info_->footerIndex_ != info_->childrenCount_ - 1) { + info_->footerIndex_ = std::min(info_->footerIndex_, info_->childrenCount_ - 1); + info_->ClearCacheAfterIndex(info_->footerIndex_ - 1); // re-insert at the end - auto footer = wrapper_->GetOrCreateChildByIndex(info_.footerIndex_); + auto footer = wrapper_->GetOrCreateChildByIndex(info_->footerIndex_); auto waterFlow = wrapper_->GetHostNode(); - waterFlow->RemoveChildAtIndex(info_.footerIndex_); + waterFlow->RemoveChildAtIndex(info_->footerIndex_); footer->GetHostNode()->MountToParent(waterFlow); footer->SetActive(false); - info_.segmentCache_.erase(info_.footerIndex_); - info_.footerIndex_ = info_.childrenCount_ - 1; + info_->segmentCache_.erase(info_->footerIndex_); + info_->footerIndex_ = info_->childrenCount_ - 1; } } @@ -310,62 +310,62 @@ float GetUserDefHeight(const RefPtr& sections, int32_t seg, i void WaterFlowSegmentedLayout::MeasureOnOffset() { - bool forward = LessOrEqual(info_.currentOffset_ - info_.prevOffset_, 0.0f) || info_.endIndex_ == -1; + bool forward = LessOrEqual(info_->currentOffset_ - info_->prevOffset_, 0.0f) || info_->endIndex_ == -1; if (forward) { - Fill(info_.endIndex_ + 1); + Fill(info_->endIndex_ + 1); } - int32_t oldStart = info_.startIndex_; - info_.Sync(mainSize_, overScroll_); + int32_t oldStart = info_->startIndex_; + info_->Sync(mainSize_, overScroll_); if (!forward) { // measure appearing items when scrolling upwards auto props = DynamicCast(wrapper_->GetLayoutProperty()); - for (int32_t i = info_.startIndex_; i < oldStart; ++i) { - MeasureItem(props, i, info_.itemInfos_[i].crossIdx, GetUserDefHeight(sections_, info_.GetSegment(i), i)); + for (int32_t i = info_->startIndex_; i < oldStart; ++i) { + MeasureItem(props, i, info_->itemInfos_[i].crossIdx, GetUserDefHeight(sections_, info_->GetSegment(i), i)); } } } void WaterFlowSegmentedLayout::MeasureOnJump(int32_t jumpIdx) { - if (jumpIdx >= info_.childrenCount_) { + if (jumpIdx >= info_->childrenCount_) { return; } if (jumpIdx == LAST_ITEM) { - jumpIdx = info_.childrenCount_ - 1; + jumpIdx = info_->childrenCount_ - 1; } - if (static_cast(jumpIdx) >= info_.itemInfos_.size()) { + if (static_cast(jumpIdx) >= info_->itemInfos_.size()) { // prepare items MeasureToTarget(jumpIdx); } // solve offset - const auto& item = info_.itemInfos_[jumpIdx]; - if (info_.align_ == ScrollAlign::AUTO) { - info_.align_ = TransformAutoScroll(item); + const auto& item = info_->itemInfos_[jumpIdx]; + if (info_->align_ == ScrollAlign::AUTO) { + info_->align_ = TransformAutoScroll(item); } - info_.currentOffset_ = SolveJumpOffset(item) + postJumpOffset_; + info_->currentOffset_ = SolveJumpOffset(item) + postJumpOffset_; Fill(jumpIdx); - info_.Sync(mainSize_, false); + info_->Sync(mainSize_, false); // only if range [startIndex, jumpIdx) isn't measured (used user-defined size) if (!sections_) { return; } auto props = DynamicCast(wrapper_->GetLayoutProperty()); - for (int32_t i = info_.startIndex_; i < jumpIdx; ++i) { - auto seg = info_.GetSegment(i); + for (int32_t i = info_->startIndex_; i < jumpIdx; ++i) { + auto seg = info_->GetSegment(i); if (sections_->GetSectionInfo()[seg].onGetItemMainSizeByIndex) { - MeasureItem(props, i, info_.itemInfos_[i].crossIdx, GetUserDefHeight(sections_, seg, i)); + MeasureItem(props, i, info_->itemInfos_[i].crossIdx, GetUserDefHeight(sections_, seg, i)); } } } ScrollAlign WaterFlowSegmentedLayout::TransformAutoScroll(const WaterFlowLayoutInfo::ItemInfo& item) const { - bool isAbove = LessNotEqual(info_.currentOffset_ + item.mainOffset, 0.0f); - bool isBelow = GreatNotEqual(info_.currentOffset_ + item.mainOffset + item.mainSize, mainSize_); + bool isAbove = LessNotEqual(info_->currentOffset_ + item.mainOffset, 0.0f); + bool isBelow = GreatNotEqual(info_->currentOffset_ + item.mainOffset + item.mainSize, mainSize_); if (isAbove && isBelow) { // possible when the item is larger than viewport return ScrollAlign::NONE; @@ -381,8 +381,8 @@ ScrollAlign WaterFlowSegmentedLayout::TransformAutoScroll(const WaterFlowLayoutI float WaterFlowSegmentedLayout::SolveJumpOffset(const WaterFlowLayoutInfo::ItemInfo& item) const { - float offset = info_.currentOffset_; - switch (info_.align_) { + float offset = info_->currentOffset_; + switch (info_->align_) { case ScrollAlign::START: offset = -item.mainOffset; break; @@ -404,9 +404,9 @@ float WaterFlowSegmentedLayout::SolveJumpOffset(const WaterFlowLayoutInfo::ItemI void WaterFlowSegmentedLayout::MeasureToTarget(int32_t targetIdx) { auto props = DynamicCast(wrapper_->GetLayoutProperty()); - targetIdx = std::min(targetIdx, info_.childrenCount_ - 1); - for (int32_t i = static_cast(info_.itemInfos_.size()); i <= targetIdx; ++i) { - int32_t seg = info_.GetSegment(i); + targetIdx = std::min(targetIdx, info_->childrenCount_ - 1); + for (int32_t i = static_cast(info_->itemInfos_.size()); i <= targetIdx; ++i) { + int32_t seg = info_->GetSegment(i); auto position = WaterFlowLayoutUtils::GetItemPosition(info_, i, mainGaps_[seg]); float itemHeight = GetUserDefHeight(sections_, seg, i); if (itemHeight < 0.0f) { @@ -415,25 +415,25 @@ void WaterFlowSegmentedLayout::MeasureToTarget(int32_t targetIdx) itemHeight = GetMainAxisSize(item->GetGeometryNode()->GetMarginFrameSize(), axis_); } } - info_.RecordItem(i, position, itemHeight); + info_->RecordItem(i, position, itemHeight); } } void WaterFlowSegmentedLayout::Fill(int32_t startIdx) { auto props = DynamicCast(wrapper_->GetLayoutProperty()); - for (int32_t i = startIdx; i < info_.childrenCount_; ++i) { - auto position = WaterFlowLayoutUtils::GetItemPosition(info_, i, mainGaps_[info_.GetSegment(i)]); - if (GreatOrEqual(position.startMainPos + info_.currentOffset_, mainSize_)) { + for (int32_t i = startIdx; i < info_->childrenCount_; ++i) { + auto position = WaterFlowLayoutUtils::GetItemPosition(info_, i, mainGaps_[info_->GetSegment(i)]); + if (GreatOrEqual(position.startMainPos + info_->currentOffset_, mainSize_)) { break; } - float itemHeight = GetUserDefHeight(sections_, info_.GetSegment(i), i); + float itemHeight = GetUserDefHeight(sections_, info_->GetSegment(i), i); auto item = MeasureItem(props, i, position.crossIndex, itemHeight); if (!item) { continue; } - if (info_.itemInfos_.size() <= static_cast(i)) { - info_.RecordItem(i, position, GetMainAxisSize(item->GetGeometryNode()->GetMarginFrameSize(), axis_)); + if (info_->itemInfos_.size() <= static_cast(i)) { + info_->RecordItem(i, position, GetMainAxisSize(item->GetGeometryNode()->GetMarginFrameSize(), axis_)); } } } @@ -457,26 +457,13 @@ RefPtr WaterFlowSegmentedLayout::MeasureItem( : CalcSize(CalcLength(userDefMainSize), crossSize)); } item->Measure(WaterFlowLayoutUtils::CreateChildConstraint( - { itemsCrossSize_[info_.GetSegment(idx)][crossIdx], mainSize_, axis_ }, props, item)); + { itemsCrossSize_[info_->GetSegment(idx)][crossIdx], mainSize_, axis_ }, props, item)); return item; } -std::pair WaterFlowSegmentedLayout::PreMeasureSelf() -{ - auto props = wrapper_->GetLayoutProperty(); - auto size = CreateIdealSize(props->GetLayoutConstraint().value(), axis_, props->GetMeasureType(), true); - auto matchChildren = GreaterOrEqualToInfinity(GetMainAxisSize(size, axis_)); - if (!matchChildren) { - wrapper_->GetGeometryNode()->SetFrameSize(size); - } - MinusPaddingToSize(props->CreatePaddingAndBorder(), size); - wrapper_->GetGeometryNode()->SetContentSize(size); - return { size, matchChildren }; -} - void WaterFlowSegmentedLayout::PostMeasureSelf(SizeF size) { - mainSize_ = info_.maxHeight_; + mainSize_ = info_->maxHeight_; size.SetMainSize(mainSize_, axis_); auto props = wrapper_->GetLayoutProperty(); AddPaddingToSize(props->CreatePaddingAndBorder(), size); @@ -485,8 +472,8 @@ void WaterFlowSegmentedLayout::PostMeasureSelf(SizeF size) void WaterFlowSegmentedLayout::LayoutItem(int32_t idx, float crossPos, const OffsetF& padding, bool isReverse) { - const auto& item = info_.itemInfos_[idx]; - auto mainOffset = item.mainOffset + info_.currentOffset_; + const auto& item = info_->itemInfos_[idx]; + auto mainOffset = item.mainOffset + info_->currentOffset_; if (isReverse) { mainOffset = mainSize_ - item.mainSize - mainOffset; } @@ -502,8 +489,8 @@ void WaterFlowSegmentedLayout::LayoutItem(int32_t idx, float crossPos, const Off } // recode restore info - if (idx == info_.startIndex_) { - info_.storedOffset_ = mainOffset; + if (idx == info_->startIndex_) { + info_->storedOffset_ = mainOffset; } } } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h index 18f8c0c4815..57939428533 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.h @@ -25,18 +25,13 @@ class WaterFlowSegmentedLayout : public WaterFlowLayoutBase { DECLARE_ACE_TYPE(WaterFlowSegmentedLayout, WaterFlowLayoutBase); public: - explicit WaterFlowSegmentedLayout(WaterFlowLayoutInfo layoutInfo) : info_(std::move(layoutInfo)) {} + explicit WaterFlowSegmentedLayout(const RefPtr& layoutInfo) : info_(layoutInfo) {} ~WaterFlowSegmentedLayout() override = default; void Measure(LayoutWrapper* layoutWrapper) override; void Layout(LayoutWrapper* layoutWrapper) override; - WaterFlowLayoutInfo GetLayoutInfo() override - { - return std::move(info_); - } - void SetCanOverScroll(bool value) override { overScroll_ = value; @@ -66,13 +61,6 @@ private: void RegularInit(const SizeF& frameSize); void InitFooter(float crossSize); - /** - * @brief Measure self before measuring children. - * - * @return [idealSize given by parent, whether measure is successful (need to adapt to children size if not)]. - */ - std::pair PreMeasureSelf(); - /** * @brief Measure self after measuring children. Only when pre-measure failed. * @@ -157,7 +145,7 @@ private: // offset to apply after a ResetAndJump float postJumpOffset_ = 0.0f; - WaterFlowLayoutInfo info_; + RefPtr info_; // true if WaterFlow can be overScrolled bool overScroll_ = false; diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp index dede61128a7..8bca536a9d6 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -128,20 +128,20 @@ HWTEST_F(WaterFlowSegmentTest, Fill001, TestSize.Level1) algo->mainGaps_ = { 5.0f, 0.0f, 1.0f }; auto& info = algo->info_; - info.margins_ = { {}, {}, PaddingPropertyF { .top = 5.0f } }; - info.childrenCount_ = 10; + info->margins_ = { {}, {}, PaddingPropertyF { .top = 5.0f } }; + info->childrenCount_ = 10; - info.items_.resize(3); + info->items_.resize(3); for (int i = 0; i < 3; ++i) { - info.items_[0][i] = {}; - info.items_[2][i] = {}; + info->items_[0][i] = {}; + info->items_[2][i] = {}; } - info.items_[0][3] = {}; + info->items_[0][3] = {}; - info.segmentTails_ = SEGMENT_TAILS_1; + info->segmentTails_ = SEGMENT_TAILS_1; algo->Fill(0); - EXPECT_EQ(info.items_, ITEM_MAP_1); + EXPECT_EQ(info->items_, ITEM_MAP_1); } /** @@ -170,39 +170,39 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnOffset001, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; for (int i = 0; i < 2; ++i) { algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.childrenCount_, 11); - EXPECT_EQ(info.items_, ITEM_MAP_2); - EXPECT_EQ(info.itemInfos_, ITEM_INFO_2); - EXPECT_EQ(info.segmentTails_, SEGMENT_TAILS_2); - EXPECT_EQ(info.endPosArray_, END_POS_ARRAY_2); - EXPECT_EQ(info.segmentStartPos_, SEGMENT_START_POS_2); + EXPECT_EQ(info->childrenCount_, 11); + EXPECT_EQ(info->items_, ITEM_MAP_2); + EXPECT_EQ(info->itemInfos_, ITEM_INFO_2); + EXPECT_EQ(info->segmentTails_, SEGMENT_TAILS_2); + EXPECT_EQ(info->endPosArray_, END_POS_ARRAY_2); + EXPECT_EQ(info->segmentStartPos_, SEGMENT_START_POS_2); } - info.prevOffset_ = 0.0f; - info.currentOffset_ = -100.0f; + info->prevOffset_ = 0.0f; + info->currentOffset_ = -100.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 10); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 10); algo->overScroll_ = true; - info.prevOffset_ = 0.0f; - info.currentOffset_ = -200.0f; + info->prevOffset_ = 0.0f; + info->currentOffset_ = -200.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.currentOffset_, -200.0f); - EXPECT_EQ(info.startIndex_, 4); - EXPECT_EQ(info.endIndex_, 10); + EXPECT_EQ(info->currentOffset_, -200.0f); + EXPECT_EQ(info->startIndex_, 4); + EXPECT_EQ(info->endIndex_, 10); - info.Reset(); + info->Reset(); algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.currentOffset_, -200.0f); - EXPECT_EQ(info.startIndex_, 4); - EXPECT_EQ(info.endIndex_, 10); + EXPECT_EQ(info->currentOffset_, -200.0f); + EXPECT_EQ(info->startIndex_, 4); + EXPECT_EQ(info->endIndex_, 10); } /** @@ -221,9 +221,9 @@ HWTEST_F(WaterFlowSegmentTest, MeasureFooter001, TestSize.Level1) ViewStackProcessor::GetInstance()->Pop(); }); - auto& info = pattern_->layoutInfo_; - EXPECT_EQ(info.childrenCount_, 11); - EXPECT_EQ(info.footerIndex_, 10); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + EXPECT_EQ(info->childrenCount_, 11); + EXPECT_EQ(info->footerIndex_, 10); auto footer = WaterFlowItemNode::GetOrCreateFlowItem( V2::FLOW_ITEM_ETS_TAG, -1, []() { return AceType::MakeRefPtr(); }); footer->GetLayoutProperty()->UpdateUserDefinedIdealSize( @@ -236,17 +236,17 @@ HWTEST_F(WaterFlowSegmentTest, MeasureFooter001, TestSize.Level1) pattern_->MarkDirtyNodeSelf(); FlushLayoutTask(frameNode_); - EXPECT_EQ(GetChildFrameNode(frameNode_, info.footerIndex_), footer); + EXPECT_EQ(GetChildFrameNode(frameNode_, info->footerIndex_), footer); UpdateCurrentOffset(-1000.0f); - EXPECT_EQ(info.items_.size(), 2); - EXPECT_EQ(info.items_[1][0].size(), 1); - EXPECT_EQ(info.childrenCount_, 13); - EXPECT_EQ(info.endIndex_, 12); - EXPECT_EQ(info.segmentTails_[0], 11); - EXPECT_EQ(info.segmentTails_[1], 12); - EXPECT_EQ(info.footerIndex_, 12); - EXPECT_EQ(info.itemInfos_[12].mainOffset, 503.0f); - EXPECT_EQ(info.itemInfos_[12].mainSize, 200.0f); + EXPECT_EQ(info->items_.size(), 2); + EXPECT_EQ(info->items_[1][0].size(), 1); + EXPECT_EQ(info->childrenCount_, 13); + EXPECT_EQ(info->endIndex_, 12); + EXPECT_EQ(info->segmentTails_[0], 11); + EXPECT_EQ(info->segmentTails_[1], 12); + EXPECT_EQ(info->footerIndex_, 12); + EXPECT_EQ(info->itemInfos_[12].mainOffset, 503.0f); + EXPECT_EQ(info->itemInfos_[12].mainSize, 200.0f); } /** @@ -265,10 +265,10 @@ HWTEST_F(WaterFlowSegmentTest, MeasureFooter002, TestSize.Level1) ViewStackProcessor::GetInstance()->Pop(); }); - auto& info = pattern_->layoutInfo_; - EXPECT_EQ(info.childrenCount_, 11); - EXPECT_EQ(info.footerIndex_, 10); - EXPECT_EQ(info.itemInfos_[8].mainOffset, 202.0f); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + EXPECT_EQ(info->childrenCount_, 11); + EXPECT_EQ(info->footerIndex_, 10); + EXPECT_EQ(info->itemInfos_[8].mainOffset, 202.0f); auto footer = WaterFlowItemNode::GetOrCreateFlowItem( V2::FLOW_ITEM_ETS_TAG, -1, []() { return AceType::MakeRefPtr(); }); @@ -283,18 +283,18 @@ HWTEST_F(WaterFlowSegmentTest, MeasureFooter002, TestSize.Level1) pattern_->MarkDirtyNodeSelf(); FlushLayoutTask(frameNode_); - EXPECT_EQ(GetChildFrameNode(frameNode_, info.footerIndex_), footer); + EXPECT_EQ(GetChildFrameNode(frameNode_, info->footerIndex_), footer); UpdateCurrentOffset(-1000.0f); - EXPECT_EQ(info.items_.size(), 2); - EXPECT_EQ(info.items_[1][0].size(), 1); - EXPECT_EQ(info.segmentStartPos_[1], 401.0f); - EXPECT_EQ(info.childrenCount_, 9); - EXPECT_EQ(info.endIndex_, 8); - EXPECT_EQ(info.segmentTails_[0], 7); - EXPECT_EQ(info.segmentTails_[1], 8); - EXPECT_EQ(info.footerIndex_, 8); - EXPECT_EQ(info.itemInfos_[8].mainOffset, 401.0f); - EXPECT_EQ(info.itemInfos_[8].mainSize, 200.0f); + EXPECT_EQ(info->items_.size(), 2); + EXPECT_EQ(info->items_[1][0].size(), 1); + EXPECT_EQ(info->segmentStartPos_[1], 401.0f); + EXPECT_EQ(info->childrenCount_, 9); + EXPECT_EQ(info->endIndex_, 8); + EXPECT_EQ(info->segmentTails_[0], 7); + EXPECT_EQ(info->segmentTails_[1], 8); + EXPECT_EQ(info->footerIndex_, 8); + EXPECT_EQ(info->itemInfos_[8].mainOffset, 401.0f); + EXPECT_EQ(info->itemInfos_[8].mainSize, 200.0f); } /** @@ -309,7 +309,7 @@ HWTEST_F(WaterFlowSegmentTest, Layout001, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; algo->Measure(AceType::RawPtr(frameNode_)); const std::vector> crossSize = { { 116.25f, 116.25f, 116.25f, 116.25f }, { 480.0f } }; @@ -340,15 +340,15 @@ HWTEST_F(WaterFlowSegmentTest, Layout002, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; - info.prevOffset_ = 0.0f; - info.currentOffset_ = -100.0f; + info->prevOffset_ = 0.0f; + info->currentOffset_ = -100.0f; algo->overScroll_ = true; algo->Measure(AceType::RawPtr(frameNode_)); algo->Layout(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 1); - EXPECT_EQ(info.endIndex_, 10); + EXPECT_EQ(info->startIndex_, 1); + EXPECT_EQ(info->endIndex_, 10); EXPECT_EQ(GetChildOffset(frameNode_, 1), OffsetF(121.25f, -100.0f)); EXPECT_EQ(GetChildOffset(frameNode_, 2), OffsetF(242.5f, -100.0f)); EXPECT_EQ(GetChildOffset(frameNode_, 3), OffsetF(363.75f, -100.0f)); @@ -373,52 +373,52 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnOffset002, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; for (int i = 0; i < 2; ++i) { algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.childrenCount_, 101); - EXPECT_EQ(info.items_.size(), 2); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 27); - EXPECT_EQ(info.segmentTails_, SEGMENT_TAILS_3); + EXPECT_EQ(info->childrenCount_, 101); + EXPECT_EQ(info->items_.size(), 2); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 27); + EXPECT_EQ(info->segmentTails_, SEGMENT_TAILS_3); } - info.prevOffset_ = 0.0f; - info.currentOffset_ = -100.0f; + info->prevOffset_ = 0.0f; + info->currentOffset_ = -100.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.currentOffset_, -100.0f); - EXPECT_EQ(info.startIndex_, 1); - EXPECT_EQ(info.endIndex_, 30); + EXPECT_EQ(info->currentOffset_, -100.0f); + EXPECT_EQ(info->startIndex_, 1); + EXPECT_EQ(info->endIndex_, 30); - info.prevOffset_ = -100.0f; - info.currentOffset_ = -500.0f; + info->prevOffset_ = -100.0f; + info->currentOffset_ = -500.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.currentOffset_, -500.0f); - EXPECT_EQ(info.startIndex_, 11); - EXPECT_EQ(info.endIndex_, 44); + EXPECT_EQ(info->currentOffset_, -500.0f); + EXPECT_EQ(info->startIndex_, 11); + EXPECT_EQ(info->endIndex_, 44); - const auto itemMap = info.items_; - const auto itemInfo = info.itemInfos_; - const auto endPosArr = info.endPosArray_; + const auto itemMap = info->items_; + const auto itemInfo = info->itemInfos_; + const auto endPosArr = info->endPosArray_; - info.prevOffset_ = -500.0f; - info.currentOffset_ = -300.0f; + info->prevOffset_ = -500.0f; + info->currentOffset_ = -300.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.items_, itemMap); - EXPECT_EQ(info.itemInfos_, itemInfo); - EXPECT_EQ(info.endPosArray_, endPosArr); - EXPECT_EQ(info.startIndex_, 5); - EXPECT_EQ(info.endIndex_, 37); - EXPECT_EQ(info.segmentStartPos_, std::vector { 0.0f }); + EXPECT_EQ(info->items_, itemMap); + EXPECT_EQ(info->itemInfos_, itemInfo); + EXPECT_EQ(info->endPosArray_, endPosArr); + EXPECT_EQ(info->startIndex_, 5); + EXPECT_EQ(info->endIndex_, 37); + EXPECT_EQ(info->segmentStartPos_, std::vector { 0.0f }); - info.prevOffset_ = -300.0f; - info.currentOffset_ = -700.0f; + info->prevOffset_ = -300.0f; + info->currentOffset_ = -700.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 19); - EXPECT_EQ(info.endIndex_, 50); - EXPECT_EQ(info.segmentStartPos_, std::vector { 0.0f }); + EXPECT_EQ(info->startIndex_, 19); + EXPECT_EQ(info->endIndex_, 50); + EXPECT_EQ(info->segmentStartPos_, std::vector { 0.0f }); } /** @@ -433,32 +433,32 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnJump001, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; - info.align_ = ScrollAlign::END; - info.jumpIndex_ = 5; + info->align_ = ScrollAlign::END; + info->jumpIndex_ = 5; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 27); - EXPECT_EQ(info.currentOffset_, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 27); + EXPECT_EQ(info->currentOffset_, 0.0f); - info.jumpIndex_ = 99; + info->jumpIndex_ = 99; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 99); - EXPECT_EQ(info.currentOffset_, -2320.0f); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 99); + EXPECT_EQ(info->currentOffset_, -2320.0f); - info.jumpIndex_ = 100; + info->jumpIndex_ = 100; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 100); - EXPECT_EQ(info.currentOffset_, -2370.0f); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 100); + EXPECT_EQ(info->currentOffset_, -2370.0f); - info.jumpIndex_ = 105; + info->jumpIndex_ = 105; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 100); - EXPECT_EQ(info.currentOffset_, -2370.0f); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 100); + EXPECT_EQ(info->currentOffset_, -2370.0f); } /** @@ -473,44 +473,44 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnJump002, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; - info.align_ = ScrollAlign::AUTO; - info.jumpIndex_ = 10; + info->align_ = ScrollAlign::AUTO; + info->jumpIndex_ = 10; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 27); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.align_, ScrollAlign::NONE); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 27); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->align_, ScrollAlign::NONE); - info.align_ = ScrollAlign::AUTO; - info.jumpIndex_ = 53; + info->align_ = ScrollAlign::AUTO; + info->jumpIndex_ = 53; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 29); - EXPECT_EQ(info.endIndex_, 58); - EXPECT_EQ(info.currentOffset_, -911.0f); - EXPECT_EQ(info.align_, ScrollAlign::END); + EXPECT_EQ(info->startIndex_, 29); + EXPECT_EQ(info->endIndex_, 58); + EXPECT_EQ(info->currentOffset_, -911.0f); + EXPECT_EQ(info->align_, ScrollAlign::END); - info.align_ = ScrollAlign::AUTO; - info.jumpIndex_ = 5; + info->align_ = ScrollAlign::AUTO; + info->jumpIndex_ = 5; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 1); - EXPECT_EQ(info.endIndex_, 30); - EXPECT_EQ(info.currentOffset_, -101.0f); - EXPECT_EQ(info.align_, ScrollAlign::START); + EXPECT_EQ(info->startIndex_, 1); + EXPECT_EQ(info->endIndex_, 30); + EXPECT_EQ(info->currentOffset_, -101.0f); + EXPECT_EQ(info->align_, ScrollAlign::START); - info.align_ = ScrollAlign::AUTO; - info.jumpIndex_ = 5; + info->align_ = ScrollAlign::AUTO; + info->jumpIndex_ = 5; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.align_, ScrollAlign::NONE); + EXPECT_EQ(info->align_, ScrollAlign::NONE); - info.align_ = ScrollAlign::AUTO; - info.jumpIndex_ = 7; + info->align_ = ScrollAlign::AUTO; + info->jumpIndex_ = 7; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 1); - EXPECT_EQ(info.endIndex_, 30); - EXPECT_EQ(info.currentOffset_, -101.0f); - EXPECT_EQ(info.align_, ScrollAlign::NONE); + EXPECT_EQ(info->startIndex_, 1); + EXPECT_EQ(info->endIndex_, 30); + EXPECT_EQ(info->currentOffset_, -101.0f); + EXPECT_EQ(info->align_, ScrollAlign::NONE); } /** @@ -525,33 +525,33 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnJump003, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; - info.align_ = ScrollAlign::START; - info.jumpIndex_ = 10; + info->align_ = ScrollAlign::START; + info->jumpIndex_ = 10; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 5); - EXPECT_EQ(info.endIndex_, 34); - EXPECT_EQ(info.currentOffset_, -202.0f); + EXPECT_EQ(info->startIndex_, 5); + EXPECT_EQ(info->endIndex_, 34); + EXPECT_EQ(info->currentOffset_, -202.0f); - info.jumpIndex_ = 99; + info->jumpIndex_ = 99; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 100); - EXPECT_EQ(info.currentOffset_, -2370.0f); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 100); + EXPECT_EQ(info->currentOffset_, -2370.0f); - info.jumpIndex_ = 42; + info->jumpIndex_ = 42; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 37); - EXPECT_EQ(info.endIndex_, 67); - EXPECT_EQ(info.currentOffset_, -1207.0f); + EXPECT_EQ(info->startIndex_, 37); + EXPECT_EQ(info->endIndex_, 67); + EXPECT_EQ(info->currentOffset_, -1207.0f); // invalid - info.jumpIndex_ = 101; + info->jumpIndex_ = 101; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 37); - EXPECT_EQ(info.endIndex_, 67); - EXPECT_EQ(info.currentOffset_, -1207.0f); + EXPECT_EQ(info->startIndex_, 37); + EXPECT_EQ(info->endIndex_, 67); + EXPECT_EQ(info->currentOffset_, -1207.0f); } /** @@ -566,41 +566,41 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnJump004, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; - info.align_ = ScrollAlign::CENTER; - info.jumpIndex_ = 10; + info->align_ = ScrollAlign::CENTER; + info->jumpIndex_ = 10; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 27); - EXPECT_EQ(info.currentOffset_, -0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 27); + EXPECT_EQ(info->currentOffset_, -0.0f); - info.jumpIndex_ = 99; + info->jumpIndex_ = 99; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 100); - EXPECT_EQ(info.currentOffset_, -2370.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 2); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 100); + EXPECT_EQ(info->currentOffset_, -2370.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 2); - info.jumpIndex_ = 42; + info->jumpIndex_ = 42; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 25); - EXPECT_EQ(info.endIndex_, 57); - EXPECT_EQ(info.currentOffset_, -857.0f); + EXPECT_EQ(info->startIndex_, 25); + EXPECT_EQ(info->endIndex_, 57); + EXPECT_EQ(info->currentOffset_, -857.0f); - info.jumpIndex_ = 0; + info->jumpIndex_ = 0; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 27); - EXPECT_EQ(info.currentOffset_, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 27); + EXPECT_EQ(info->currentOffset_, 0.0f); // invalid jumpIndex - info.jumpIndex_ = 500; + info->jumpIndex_ = 500; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 27); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.jumpIndex_, EMPTY_JUMP_INDEX); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 27); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->jumpIndex_, EMPTY_JUMP_INDEX); } /** @@ -615,24 +615,24 @@ HWTEST_F(WaterFlowSegmentTest, Reset001, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; - info.align_ = ScrollAlign::CENTER; + info->align_ = ScrollAlign::CENTER; - info.jumpIndex_ = 99; + info->jumpIndex_ = 99; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 100); - EXPECT_EQ(info.currentOffset_, -2370.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 2); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 100); + EXPECT_EQ(info->currentOffset_, -2370.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 2); // change crossCount, should jump back to index 75 layoutProperty_->UpdateColumnsTemplate("1fr 1fr 1fr"); - info.Reset(); + info->Reset(); algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 45); - EXPECT_EQ(info.endIndex_, 64); - EXPECT_EQ(info.currentOffset_, -2370.0f); + EXPECT_EQ(info->startIndex_, 45); + EXPECT_EQ(info->endIndex_, 64); + EXPECT_EQ(info->currentOffset_, -2370.0f); EXPECT_EQ(algo->itemsCrossSize_[0].size(), 3); } @@ -648,52 +648,52 @@ HWTEST_F(WaterFlowSegmentTest, Reset002, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; - info.align_ = ScrollAlign::CENTER; - info.jumpIndex_ = 99; + info->align_ = ScrollAlign::CENTER; + info->jumpIndex_ = 99; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 100); - EXPECT_EQ(info.currentOffset_, -2370.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 2); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 100); + EXPECT_EQ(info->currentOffset_, -2370.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 2); - info.Reset(); + info->Reset(); algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 100); - EXPECT_EQ(info.currentOffset_, -2370.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 2); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 100); + EXPECT_EQ(info->currentOffset_, -2370.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 2); - info.jumpIndex_ = 42; + info->jumpIndex_ = 42; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 25); - EXPECT_EQ(info.endIndex_, 57); - EXPECT_EQ(info.currentOffset_, -857.0f); + EXPECT_EQ(info->startIndex_, 25); + EXPECT_EQ(info->endIndex_, 57); + EXPECT_EQ(info->currentOffset_, -857.0f); // child requires fresh layout, should jump back to index 75 layoutProperty_->propertyChangeFlag_ = PROPERTY_UPDATE_BY_CHILD_REQUEST; frameNode_->ChildrenUpdatedFrom(0); algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 25); - EXPECT_EQ(info.endIndex_, 57); - EXPECT_EQ(info.currentOffset_, -857.0f); - EXPECT_EQ(info.align_, ScrollAlign::START); + EXPECT_EQ(info->startIndex_, 25); + EXPECT_EQ(info->endIndex_, 57); + EXPECT_EQ(info->currentOffset_, -857.0f); + EXPECT_EQ(info->align_, ScrollAlign::START); // items should be cleared before jumping - EXPECT_EQ(info.items_[1][0].size(), 0); - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.itemInfos_.size(), 58); + EXPECT_EQ(info->items_[1][0].size(), 0); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->itemInfos_.size(), 58); - info.Reset(); + info->Reset(); layoutProperty_->propertyChangeFlag_ = PROPERTY_UPDATE_BY_CHILD_REQUEST; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 25); - EXPECT_EQ(info.endIndex_, 57); - EXPECT_EQ(info.currentOffset_, -857.0f); + EXPECT_EQ(info->startIndex_, 25); + EXPECT_EQ(info->endIndex_, 57); + EXPECT_EQ(info->currentOffset_, -857.0f); // items should be cleared before jumping - EXPECT_EQ(info.items_[1][0].size(), 0); - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.itemInfos_.size(), 58); + EXPECT_EQ(info->items_[1][0].size(), 0); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->itemInfos_.size(), 58); } /** @@ -708,44 +708,44 @@ HWTEST_F(WaterFlowSegmentTest, Reset003, TestSize.Level1) auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); auto& info = algo->info_; - info.footerIndex_ = 0; + info->footerIndex_ = 0; - info.align_ = ScrollAlign::CENTER; - info.jumpIndex_ = 99; + info->align_ = ScrollAlign::CENTER; + info->jumpIndex_ = 99; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 75); - EXPECT_EQ(info.endIndex_, 100); - EXPECT_EQ(info.currentOffset_, -2370.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 2); - EXPECT_EQ(info.itemInfos_.size(), 101); + EXPECT_EQ(info->startIndex_, 75); + EXPECT_EQ(info->endIndex_, 100); + EXPECT_EQ(info->currentOffset_, -2370.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 2); + EXPECT_EQ(info->itemInfos_.size(), 101); - info.jumpIndex_ = 42; + info->jumpIndex_ = 42; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 25); - EXPECT_EQ(info.endIndex_, 57); - EXPECT_EQ(info.currentOffset_, -857.0f); + EXPECT_EQ(info->startIndex_, 25); + EXPECT_EQ(info->endIndex_, 57); + EXPECT_EQ(info->currentOffset_, -857.0f); // index 70 doesn't affect the current layout frameNode_->ChildrenUpdatedFrom(70); algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 25); - EXPECT_EQ(info.endIndex_, 57); - EXPECT_EQ(info.currentOffset_, -857.0f); - EXPECT_EQ(info.align_, ScrollAlign::CENTER); + EXPECT_EQ(info->startIndex_, 25); + EXPECT_EQ(info->endIndex_, 57); + EXPECT_EQ(info->currentOffset_, -857.0f); + EXPECT_EQ(info->align_, ScrollAlign::CENTER); // items starting from 70 are cleared - EXPECT_EQ(info.items_[1][0].size(), 0); - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.itemInfos_.size(), 70); + EXPECT_EQ(info->items_[1][0].size(), 0); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->itemInfos_.size(), 70); // index 20 would reset all and trigger jump frameNode_->ChildrenUpdatedFrom(20); algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 25); - EXPECT_EQ(info.endIndex_, 57); - EXPECT_EQ(info.currentOffset_, -857.0f); - EXPECT_EQ(info.align_, ScrollAlign::START); + EXPECT_EQ(info->startIndex_, 25); + EXPECT_EQ(info->endIndex_, 57); + EXPECT_EQ(info->currentOffset_, -857.0f); + EXPECT_EQ(info->align_, ScrollAlign::START); // items should be cleared before jumping - EXPECT_EQ(info.itemInfos_.size(), 58); + EXPECT_EQ(info->itemInfos_.size(), 58); } /** @@ -765,11 +765,11 @@ HWTEST_F(WaterFlowSegmentTest, Reset004, TestSize.Level1) }); UpdateCurrentOffset(-2000.0f); - auto& info = pattern_->layoutInfo_; - EXPECT_EQ(info.footerIndex_, 200); - EXPECT_EQ(info.endIndex_, 75); - EXPECT_EQ(info.startIndex_, 49); - EXPECT_EQ(info.itemInfos_[info.startIndex_].mainOffset + info.currentOffset_, -188.0f); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + EXPECT_EQ(info->footerIndex_, 200); + EXPECT_EQ(info->endIndex_, 75); + EXPECT_EQ(info->startIndex_, 49); + EXPECT_EQ(info->itemInfos_[info->startIndex_].mainOffset + info->currentOffset_, -188.0f); auto footer = WaterFlowItemNode::GetOrCreateFlowItem( V2::FLOW_ITEM_ETS_TAG, -1, []() { return AceType::MakeRefPtr(); }); footer->GetLayoutProperty()->UpdateUserDefinedIdealSize( @@ -782,9 +782,9 @@ HWTEST_F(WaterFlowSegmentTest, Reset004, TestSize.Level1) layoutProperty_->UpdateColumnsTemplate("1fr 1fr"); FlushLayoutTask(frameNode_); - EXPECT_EQ(GetChildFrameNode(frameNode_, info.footerIndex_), footer); - EXPECT_EQ(info.startIndex_, 25); - EXPECT_EQ(info.currentOffset_, -2000.0f); + EXPECT_EQ(GetChildFrameNode(frameNode_, info->footerIndex_), footer); + EXPECT_EQ(info->startIndex_, 25); + EXPECT_EQ(info->currentOffset_, -2000.0f); EXPECT_EQ(GetChildWidth(frameNode_, 25), 237.5f); } @@ -803,17 +803,17 @@ HWTEST_F(WaterFlowSegmentTest, Reset005, TestSize.Level1) CreateItem(200); ViewStackProcessor::GetInstance()->Pop(); }); - auto& info = pattern_->layoutInfo_; - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 21); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 21); for (int i = 0; i <= 21; ++i) { EXPECT_EQ(GetChildWidth(frameNode_, i), 116.25f); } layoutProperty_->UpdateColumnsGap(Dimension(1.0f)); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 21); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 21); for (int i = 0; i <= 21; ++i) { EXPECT_EQ(GetChildWidth(frameNode_, i), 119.25f); } @@ -845,27 +845,27 @@ HWTEST_F(WaterFlowSegmentTest, Segmented001, TestSize.Level1) auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.endIndex_, 12); - EXPECT_EQ(info.margins_.size(), 3); - EXPECT_EQ(info.segmentTails_, SEGMENT_TAILS_4); - EXPECT_EQ(info.currentOffset_, 0.0f); + EXPECT_EQ(info->endIndex_, 12); + EXPECT_EQ(info->margins_.size(), 3); + EXPECT_EQ(info->segmentTails_, SEGMENT_TAILS_4); + EXPECT_EQ(info->currentOffset_, 0.0f); const std::vector crossGaps = { 0.0f, 0.0f, 0.0f }; EXPECT_EQ(algo->mainGaps_.size(), 3); - info.currentOffset_ = -200.0f; + info->currentOffset_ = -200.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 3); - EXPECT_EQ(info.endIndex_, 16); - EXPECT_EQ(info.currentOffset_, -200.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 1); + EXPECT_EQ(info->startIndex_, 3); + EXPECT_EQ(info->endIndex_, 16); + EXPECT_EQ(info->currentOffset_, -200.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 1); - info.prevOffset_ = -200.0f; - info.currentOffset_ = -4050.0f; + info->prevOffset_ = -200.0f; + info->currentOffset_ = -4050.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 47); - EXPECT_EQ(info.endIndex_, 51); - EXPECT_EQ(info.segmentStartPos_.size(), 3); - EXPECT_EQ(info.endPosArray_.size(), 37); + EXPECT_EQ(info->startIndex_, 47); + EXPECT_EQ(info->endIndex_, 51); + EXPECT_EQ(info->segmentStartPos_.size(), 3); + EXPECT_EQ(info->endPosArray_.size(), 37); } /** @@ -891,28 +891,28 @@ HWTEST_F(WaterFlowSegmentTest, Segmented005, TestSize.Level1) auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); auto& info = algo->info_; - info.jumpIndex_ = 50; - info.align_ = ScrollAlign::END; + info->jumpIndex_ = 50; + info->align_ = ScrollAlign::END; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 47); - EXPECT_EQ(info.endIndex_, 50); - EXPECT_EQ(info.currentOffset_, -4000.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 3); + EXPECT_EQ(info->startIndex_, 47); + EXPECT_EQ(info->endIndex_, 50); + EXPECT_EQ(info->currentOffset_, -4000.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 3); pattern_->layoutInfo_ = info; secObj->ChangeData(0, 3, SECTION_5); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); EXPECT_EQ(secObj->GetSectionInfo().size(), 4); info = pattern_->layoutInfo_; - EXPECT_EQ(info.startIndex_, 47); - EXPECT_EQ(info.segmentTails_.size(), 4); - EXPECT_EQ(info.segmentTails_[3], 59); - algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 47); - EXPECT_EQ(info.endIndex_, 54); - EXPECT_EQ(info.currentOffset_, -5546.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 4); - EXPECT_EQ(info.align_, ScrollAlign::START); + EXPECT_EQ(info->startIndex_, 47); + EXPECT_EQ(info->segmentTails_.size(), 4); + EXPECT_EQ(info->segmentTails_[3], 59); + algo->Measure(AceType::RawPtr(frameNode_)); + EXPECT_EQ(info->startIndex_, 47); + EXPECT_EQ(info->endIndex_, 54); + EXPECT_EQ(info->currentOffset_, -5546.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 4); + EXPECT_EQ(info->align_, ScrollAlign::START); EXPECT_EQ(algo->crossGaps_, CROSS_GAP_5); EXPECT_EQ(algo->mainGaps_, MAIN_GAP_5); EXPECT_EQ(algo->itemsCrossSize_.size(), 4); @@ -932,40 +932,40 @@ HWTEST_F(WaterFlowSegmentTest, Segmented002, TestSize.Level1) auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 10); - EXPECT_EQ(info.margins_.size(), 4); - EXPECT_EQ(info.segmentTails_, SEGMENT_TAILS_5); - EXPECT_EQ(info.currentOffset_, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 10); + EXPECT_EQ(info->margins_.size(), 4); + EXPECT_EQ(info->segmentTails_, SEGMENT_TAILS_5); + EXPECT_EQ(info->currentOffset_, 0.0f); EXPECT_EQ(algo->crossGaps_, CROSS_GAP_5); EXPECT_EQ(algo->mainGaps_, MAIN_GAP_5); EXPECT_EQ(algo->itemsCrossSize_, ITEM_CROSS_SIZE_5); - info.currentOffset_ = -200.0f; + info->currentOffset_ = -200.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 3); - EXPECT_EQ(info.endIndex_, 11); - EXPECT_EQ(info.segmentStartPos_.size(), 3); + EXPECT_EQ(info->startIndex_, 3); + EXPECT_EQ(info->endIndex_, 11); + EXPECT_EQ(info->segmentStartPos_.size(), 3); - info.currentOffset_ = -304.0f; + info->currentOffset_ = -304.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 3); - EXPECT_EQ(info.endIndex_, 12); - EXPECT_EQ(info.segmentStartPos_.size(), 3); + EXPECT_EQ(info->startIndex_, 3); + EXPECT_EQ(info->endIndex_, 12); + EXPECT_EQ(info->segmentStartPos_.size(), 3); - info.currentOffset_ = -305.0f; + info->currentOffset_ = -305.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 5); - EXPECT_EQ(info.endIndex_, 12); - EXPECT_EQ(info.segmentStartPos_.size(), 3); + EXPECT_EQ(info->startIndex_, 5); + EXPECT_EQ(info->endIndex_, 12); + EXPECT_EQ(info->segmentStartPos_.size(), 3); - info.Reset(); + info->Reset(); algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.currentOffset_, -305.0f); - EXPECT_EQ(info.startIndex_, 5); - EXPECT_EQ(info.endIndex_, 12); - EXPECT_EQ(info.margins_.size(), 4); - EXPECT_EQ(info.segmentTails_, SEGMENT_TAILS_5); + EXPECT_EQ(info->currentOffset_, -305.0f); + EXPECT_EQ(info->startIndex_, 5); + EXPECT_EQ(info->endIndex_, 12); + EXPECT_EQ(info->margins_.size(), 4); + EXPECT_EQ(info->segmentTails_, SEGMENT_TAILS_5); } /** @@ -981,41 +981,41 @@ HWTEST_F(WaterFlowSegmentTest, Segmented003, TestSize.Level1) auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); - info.currentOffset_ = -800.0f; + info->currentOffset_ = -800.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 11); - EXPECT_EQ(info.endIndex_, 15); - EXPECT_EQ(info.segmentStartPos_.size(), 3); + EXPECT_EQ(info->startIndex_, 11); + EXPECT_EQ(info->endIndex_, 15); + EXPECT_EQ(info->segmentStartPos_.size(), 3); - info.currentOffset_ = -1200.0f; + info->currentOffset_ = -1200.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 14); - EXPECT_EQ(info.endIndex_, 18); + EXPECT_EQ(info->startIndex_, 14); + EXPECT_EQ(info->endIndex_, 18); - info.currentOffset_ = -2300.0f; + info->currentOffset_ = -2300.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 21); - EXPECT_EQ(info.endIndex_, 25); - EXPECT_EQ(info.segmentStartPos_.size(), 3); + EXPECT_EQ(info->startIndex_, 21); + EXPECT_EQ(info->endIndex_, 25); + EXPECT_EQ(info->segmentStartPos_.size(), 3); - info.prevOffset_ = -2300.0f; - info.currentOffset_ = -1800.0f; + info->prevOffset_ = -2300.0f; + info->currentOffset_ = -1800.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 18); - EXPECT_EQ(info.endIndex_, 22); - EXPECT_EQ(info.segmentStartPos_.size(), 3); + EXPECT_EQ(info->startIndex_, 18); + EXPECT_EQ(info->endIndex_, 22); + EXPECT_EQ(info->segmentStartPos_.size(), 3); - info.currentOffset_ = -10000.0f; + info->currentOffset_ = -10000.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 53); - EXPECT_EQ(info.endIndex_, 59); - EXPECT_EQ(info.currentOffset_, -6058.0f); - EXPECT_EQ(*info.margins_[1].top, 1.0f); - EXPECT_EQ(*info.margins_[1].bottom, 5.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 4); - EXPECT_TRUE(info.offsetEnd_); - EXPECT_TRUE(info.itemEnd_); - EXPECT_FALSE(info.itemStart_); + EXPECT_EQ(info->startIndex_, 53); + EXPECT_EQ(info->endIndex_, 59); + EXPECT_EQ(info->currentOffset_, -6058.0f); + EXPECT_EQ(*info->margins_[1].top, 1.0f); + EXPECT_EQ(*info->margins_[1].bottom, 5.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 4); + EXPECT_TRUE(info->offsetEnd_); + EXPECT_TRUE(info->itemEnd_); + EXPECT_FALSE(info->itemStart_); algo->Layout(AceType::RawPtr(frameNode_)); } @@ -1033,18 +1033,18 @@ HWTEST_F(WaterFlowSegmentTest, Segmented004, TestSize.Level1) auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.endIndex_, 10); - EXPECT_EQ(info.itemInfos_.size(), 11); + EXPECT_EQ(info->endIndex_, 10); + EXPECT_EQ(info->itemInfos_.size(), 11); - info.currentOffset_ = -800.0f; + info->currentOffset_ = -800.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 11); - EXPECT_EQ(info.endIndex_, 15); - EXPECT_EQ(info.segmentStartPos_.size(), 3); - EXPECT_EQ(info.itemInfos_.size(), 16); + EXPECT_EQ(info->startIndex_, 11); + EXPECT_EQ(info->endIndex_, 15); + EXPECT_EQ(info->segmentStartPos_.size(), 3); + EXPECT_EQ(info->itemInfos_.size(), 16); algo->Layout(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.itemInfos_.size(), 16); + EXPECT_EQ(info->itemInfos_.size(), 16); pattern_->layoutInfo_ = info; auto secObj = pattern_->GetSections(); @@ -1052,24 +1052,24 @@ HWTEST_F(WaterFlowSegmentTest, Segmented004, TestSize.Level1) MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); AddItems(10); info = pattern_->layoutInfo_; - EXPECT_EQ(info.itemInfos_.size(), 16); + EXPECT_EQ(info->itemInfos_.size(), 16); algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.currentOffset_, -800.0f); - EXPECT_EQ(info.startIndex_, 11); - EXPECT_EQ(info.endIndex_, 15); - EXPECT_EQ(info.childrenCount_, 70); + EXPECT_EQ(info->currentOffset_, -800.0f); + EXPECT_EQ(info->startIndex_, 11); + EXPECT_EQ(info->endIndex_, 15); + EXPECT_EQ(info->childrenCount_, 70); algo->Layout(AceType::RawPtr(frameNode_)); - info.prevOffset_ = -800.0f; - info.currentOffset_ = -10000.0f; + info->prevOffset_ = -800.0f; + info->currentOffset_ = -10000.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 63); - EXPECT_EQ(info.endIndex_, 69); - EXPECT_EQ(info.itemInfos_.size(), 70); - EXPECT_EQ(info.itemInfos_[69].mainOffset, 7283.0f); - EXPECT_EQ(info.itemInfos_[69].crossIdx, 0); - EXPECT_EQ(info.itemInfos_[69].mainSize, 200.0f); + EXPECT_EQ(info->startIndex_, 63); + EXPECT_EQ(info->endIndex_, 69); + EXPECT_EQ(info->itemInfos_.size(), 70); + EXPECT_EQ(info->itemInfos_[69].mainOffset, 7283.0f); + EXPECT_EQ(info->itemInfos_[69].crossIdx, 0); + EXPECT_EQ(info->itemInfos_[69].mainSize, 200.0f); algo->Layout(AceType::RawPtr(frameNode_)); } @@ -1092,33 +1092,33 @@ HWTEST_F(WaterFlowSegmentTest, Segmented006, TestSize.Level1) MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; - EXPECT_EQ(*info.margins_[0].top, 5.0f); - EXPECT_EQ(info.segmentStartPos_[0], 5.0f); - EXPECT_EQ(info.segmentStartPos_[1], 408.0f); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + EXPECT_EQ(*info->margins_[0].top, 5.0f); + EXPECT_EQ(info->segmentStartPos_[0], 5.0f); + EXPECT_EQ(info->segmentStartPos_[1], 408.0f); UpdateCurrentOffset(-600.0f); - EXPECT_EQ(info.segmentStartPos_[2], 613.0f); - EXPECT_EQ(info.startIndex_, 6); + EXPECT_EQ(info->segmentStartPos_[2], 613.0f); + EXPECT_EQ(info->startIndex_, 6); layoutProperty_->UpdateRowsGap(10.0_vp); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.currentOffset_, -600.0f); - EXPECT_EQ(info.startIndex_, 6); - EXPECT_EQ(info.segmentStartPos_[0], 5.0f); - EXPECT_EQ(info.segmentStartPos_[1], 438.0f); - EXPECT_EQ(info.itemInfos_[4].mainOffset, 438.0f); - EXPECT_EQ(info.segmentStartPos_[2], 653.0f); + EXPECT_EQ(info->currentOffset_, -600.0f); + EXPECT_EQ(info->startIndex_, 6); + EXPECT_EQ(info->segmentStartPos_[0], 5.0f); + EXPECT_EQ(info->segmentStartPos_[1], 438.0f); + EXPECT_EQ(info->itemInfos_[4].mainOffset, 438.0f); + EXPECT_EQ(info->segmentStartPos_[2], 653.0f); UpdateCurrentOffset(600.0f); layoutProperty_->UpdateRowsGap(11.0_vp); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 6); - EXPECT_EQ(info.segmentStartPos_[0], 5.0f); - EXPECT_EQ(info.segmentStartPos_[1], 441.0f); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 6); + EXPECT_EQ(info->segmentStartPos_[0], 5.0f); + EXPECT_EQ(info->segmentStartPos_[1], 441.0f); } /** @@ -1167,12 +1167,12 @@ HWTEST_F(WaterFlowSegmentTest, CheckHeight001, TestSize.Level1) MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-10000.0f); - EXPECT_EQ(info.currentOffset_, -3074.0f); - EXPECT_EQ(info.startIndex_, 31); - EXPECT_EQ(info.endIndex_, 36); + EXPECT_EQ(info->currentOffset_, -3074.0f); + EXPECT_EQ(info->startIndex_, 31); + EXPECT_EQ(info->endIndex_, 36); for (int i = 31; i <= 36; ++i) { EXPECT_EQ(GetChildHeight(frameNode_, i), 100.0f); } @@ -1184,9 +1184,9 @@ HWTEST_F(WaterFlowSegmentTest, CheckHeight001, TestSize.Level1) } UpdateCurrentOffset(10000.0f); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 6); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 6); for (int i = 0; i <= 6; ++i) { EXPECT_EQ(GetChildHeight(frameNode_, i), 100.0f); } @@ -1204,11 +1204,11 @@ HWTEST_F(WaterFlowSegmentTest, TargetIndex001, TestSize.Level1) auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); auto& info = algo->info_; - info.targetIndex_ = 50; + info->targetIndex_ = 50; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 4); - EXPECT_EQ(info.itemInfos_.size(), 51); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 4); + EXPECT_EQ(info->itemInfos_.size(), 51); algo->Layout(AceType::RawPtr(frameNode_)); } @@ -1238,27 +1238,27 @@ HWTEST_F(WaterFlowSegmentTest, ChildrenCount001, TestSize.Level1) // cause layout abort auto& info = algo->info_; - info.targetIndex_ = 50; + info->targetIndex_ = 50; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.endIndex_, -1); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.itemInfos_.size(), 0); + EXPECT_EQ(info->endIndex_, -1); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->itemInfos_.size(), 0); algo->Layout(AceType::RawPtr(frameNode_)); - info.currentOffset_ = -1050.0f; + info->currentOffset_ = -1050.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, -1); - EXPECT_EQ(info.segmentStartPos_.size(), 1); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, -1); + EXPECT_EQ(info->segmentStartPos_.size(), 1); - info.prevOffset_ = -1050.0f; - info.currentOffset_ = -10000.0f; + info->prevOffset_ = -1050.0f; + info->currentOffset_ = -10000.0f; algo->Measure(AceType::RawPtr(frameNode_)); // as long as no crash happens - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.itemInfos_.size(), 0); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->itemInfos_.size(), 0); } /** @@ -1286,23 +1286,23 @@ HWTEST_F(WaterFlowSegmentTest, ChildrenCount002, TestSize.Level1) auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, -1); - EXPECT_EQ(info.segmentStartPos_.size(), 1); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, -1); + EXPECT_EQ(info->segmentStartPos_.size(), 1); - info.currentOffset_ = -10000.0f; + info->currentOffset_ = -10000.0f; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, -1); - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.itemInfos_.size(), 0); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, -1); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->itemInfos_.size(), 0); algo->Layout(AceType::RawPtr(frameNode_)); - info.jumpIndex_ = 70; - info.align_ = ScrollAlign::AUTO; + info->jumpIndex_ = 70; + info->align_ = ScrollAlign::AUTO; algo->Measure(AceType::RawPtr(frameNode_)); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, -1); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, -1); algo->Layout(AceType::RawPtr(frameNode_)); } @@ -1325,15 +1325,15 @@ HWTEST_F(WaterFlowSegmentTest, Add001, TestSize.Level1) MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 10); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 10); UpdateCurrentOffset(-2000.0f); - EXPECT_EQ(info.currentOffset_, -2000.0f); - EXPECT_EQ(info.startIndex_, 19); - EXPECT_EQ(info.endIndex_, 23); - EXPECT_EQ(info.segmentTails_.size(), 4); + EXPECT_EQ(info->currentOffset_, -2000.0f); + EXPECT_EQ(info->startIndex_, 19); + EXPECT_EQ(info->endIndex_, 23); + EXPECT_EQ(info->segmentTails_.size(), 4); AddItems(10); secObj->ChangeData(4, 0, ADD_SECTION_6); @@ -1343,20 +1343,20 @@ HWTEST_F(WaterFlowSegmentTest, Add001, TestSize.Level1) EXPECT_EQ(secObj->GetSectionInfo()[4].crossCount, 2); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.currentOffset_, -2000.0f); - EXPECT_EQ(info.startIndex_, 19); - EXPECT_EQ(info.endIndex_, 23); - EXPECT_EQ(info.segmentTails_.size(), 5); - EXPECT_EQ(info.childrenCount_, 70); + EXPECT_EQ(info->currentOffset_, -2000.0f); + EXPECT_EQ(info->startIndex_, 19); + EXPECT_EQ(info->endIndex_, 23); + EXPECT_EQ(info->segmentTails_.size(), 5); + EXPECT_EQ(info->childrenCount_, 70); UpdateCurrentOffset(-10000.0f); - EXPECT_EQ(info.currentOffset_, -6883.0f); - EXPECT_EQ(info.startIndex_, 63); - EXPECT_EQ(info.endIndex_, 69); - EXPECT_EQ(info.items_[4][1].size(), 4); - EXPECT_EQ(info.itemInfos_[60].mainOffset, 6658.0f); - EXPECT_EQ(info.itemInfos_[9].mainOffset, 306.0f); - EXPECT_EQ(info.itemInfos_[10].mainOffset, 511.0f); + EXPECT_EQ(info->currentOffset_, -6883.0f); + EXPECT_EQ(info->startIndex_, 63); + EXPECT_EQ(info->endIndex_, 69); + EXPECT_EQ(info->items_[4][1].size(), 4); + EXPECT_EQ(info->itemInfos_[60].mainOffset, 6658.0f); + EXPECT_EQ(info->itemInfos_[9].mainOffset, 306.0f); + EXPECT_EQ(info->itemInfos_[10].mainOffset, 511.0f); } /** @@ -1377,12 +1377,12 @@ HWTEST_F(WaterFlowSegmentTest, Splice001, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-300.0f); - EXPECT_EQ(info.segmentStartPos_[2], 613.0f); - EXPECT_EQ(info.startIndex_, 2); + EXPECT_EQ(info->segmentStartPos_[2], 613.0f); + EXPECT_EQ(info->startIndex_, 2); // replace second section secObj->ChangeData(1, 1, ADD_SECTION_6); @@ -1396,18 +1396,18 @@ HWTEST_F(WaterFlowSegmentTest, Splice001, TestSize.Level1) EXPECT_EQ(secObj->GetSectionInfo()[2].itemsCount, 30); EXPECT_TRUE(secObj->GetSectionInfo()[1].onGetItemMainSizeByIndex); - EXPECT_EQ(info.currentOffset_, -300.0f); - EXPECT_EQ(info.startIndex_, 2); - EXPECT_EQ(info.endIndex_, 10); - EXPECT_EQ(info.segmentStartPos_.size(), 2); - EXPECT_EQ(info.segmentStartPos_[0], 5.0f); - EXPECT_EQ(info.segmentStartPos_[1], 408.0f); - EXPECT_EQ(info.itemInfos_[5].mainOffset, 408.0f); - EXPECT_EQ(info.itemInfos_[5].crossIdx, 1); + EXPECT_EQ(info->currentOffset_, -300.0f); + EXPECT_EQ(info->startIndex_, 2); + EXPECT_EQ(info->endIndex_, 10); + EXPECT_EQ(info->segmentStartPos_.size(), 2); + EXPECT_EQ(info->segmentStartPos_[0], 5.0f); + EXPECT_EQ(info->segmentStartPos_[1], 408.0f); + EXPECT_EQ(info->itemInfos_[5].mainOffset, 408.0f); + EXPECT_EQ(info->itemInfos_[5].crossIdx, 1); UpdateCurrentOffset(-1000.0f); - EXPECT_EQ(info.startIndex_, 14); - EXPECT_EQ(info.endIndex_, 20); + EXPECT_EQ(info->startIndex_, 14); + EXPECT_EQ(info->endIndex_, 20); } /** @@ -1428,9 +1428,9 @@ HWTEST_F(WaterFlowSegmentTest, Splice002, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; - EXPECT_EQ(info.endIndex_, 6); - for (int i = 0; i < info.endIndex_; ++i) { + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + EXPECT_EQ(info->endIndex_, 6); + for (int i = 0; i < info->endIndex_; ++i) { EXPECT_EQ(GetChildHeight(frameNode_, i), 100.0f); } @@ -1440,12 +1440,12 @@ HWTEST_F(WaterFlowSegmentTest, Splice002, TestSize.Level1) frameNode_->ChildrenUpdatedFrom(37); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.endIndex_, 10); - for (int i = 0; i < info.endIndex_; ++i) { + EXPECT_EQ(info->endIndex_, 10); + for (int i = 0; i < info->endIndex_; ++i) { EXPECT_TRUE(GetChildFrameNode(frameNode_, i)->IsActive()); EXPECT_EQ(GetChildHeight(frameNode_, i), 100.0f); } - EXPECT_EQ(info.segmentStartPos_[0], 0.0f); + EXPECT_EQ(info->segmentStartPos_[0], 0.0f); // replace FIRST section secObj->ChangeData(0, 3, SECTION_7); @@ -1455,9 +1455,9 @@ HWTEST_F(WaterFlowSegmentTest, Splice002, TestSize.Level1) frameNode_->ChildrenUpdatedFrom(0); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.childrenCount_, 37); - EXPECT_EQ(info.endIndex_, 6); - EXPECT_EQ(info.segmentStartPos_[0], 5.0f); + EXPECT_EQ(info->childrenCount_, 37); + EXPECT_EQ(info->endIndex_, 6); + EXPECT_EQ(info->segmentStartPos_[0], 5.0f); } /** @@ -1478,10 +1478,10 @@ HWTEST_F(WaterFlowSegmentTest, Delete001, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-200.0f); - EXPECT_EQ(info.startIndex_, 1); + EXPECT_EQ(info->startIndex_, 1); secObj->ChangeData(1, 2, {}); for (int i = 0; i < 33; ++i) { @@ -1493,12 +1493,12 @@ HWTEST_F(WaterFlowSegmentTest, Delete001, TestSize.Level1) EXPECT_EQ(secObj->GetSectionInfo().size(), 1); EXPECT_EQ(secObj->GetSectionInfo()[0].itemsCount, 4); - EXPECT_EQ(info.childrenCount_, 4); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 3); - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.segmentStartPos_[0], 5.0f); + EXPECT_EQ(info->childrenCount_, 4); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 3); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->segmentStartPos_[0], 5.0f); EXPECT_EQ(GetChildHeight(frameNode_, 0), 100.0f); EXPECT_EQ(GetChildWidth(frameNode_, 0), 400.0f); } @@ -1521,11 +1521,11 @@ HWTEST_F(WaterFlowSegmentTest, Delete002, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-400.0f); - EXPECT_EQ(info.startIndex_, 3); - EXPECT_EQ(info.storedOffset_, -95.0f); + EXPECT_EQ(info->startIndex_, 3); + EXPECT_EQ(info->storedOffset_, -95.0f); secObj->ChangeData(0, 2, {}); for (int i = 0; i < 7; ++i) { @@ -1537,19 +1537,19 @@ HWTEST_F(WaterFlowSegmentTest, Delete002, TestSize.Level1) EXPECT_EQ(secObj->GetSectionInfo().size(), 1); EXPECT_EQ(secObj->GetSectionInfo()[0].itemsCount, 30); - EXPECT_EQ(info.currentOffset_, -406.0f); - EXPECT_EQ(info.storedOffset_, -95.0f); - EXPECT_EQ(info.startIndex_, 3); - EXPECT_EQ(info.endIndex_, 9); - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.segmentStartPos_[0], 5.0f); - EXPECT_EQ(info.itemInfos_[3].mainOffset, 311.0f); - EXPECT_EQ(info.itemInfos_[3].crossIdx, 0); + EXPECT_EQ(info->currentOffset_, -406.0f); + EXPECT_EQ(info->storedOffset_, -95.0f); + EXPECT_EQ(info->startIndex_, 3); + EXPECT_EQ(info->endIndex_, 9); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->segmentStartPos_[0], 5.0f); + EXPECT_EQ(info->itemInfos_[3].mainOffset, 311.0f); + EXPECT_EQ(info->itemInfos_[3].crossIdx, 0); UpdateCurrentOffset(-10000.0f); - EXPECT_EQ(info.currentOffset_, -2466.0f); - EXPECT_EQ(info.startIndex_, 24); - EXPECT_EQ(info.endIndex_, 29); + EXPECT_EQ(info->currentOffset_, -2466.0f); + EXPECT_EQ(info->startIndex_, 24); + EXPECT_EQ(info->endIndex_, 29); } /** @@ -1570,10 +1570,10 @@ HWTEST_F(WaterFlowSegmentTest, Replace001, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-205.0f); - EXPECT_EQ(info.startIndex_, 2); + EXPECT_EQ(info->startIndex_, 2); secObj->ChangeData(1, 2, ADD_SECTION_6); for (int i = 0; i < 23; ++i) { @@ -1585,25 +1585,25 @@ HWTEST_F(WaterFlowSegmentTest, Replace001, TestSize.Level1) EXPECT_EQ(secObj->GetSectionInfo().size(), 2); EXPECT_EQ(secObj->GetSectionInfo()[1].itemsCount, 10); - EXPECT_EQ(info.currentOffset_, -205.0f); - EXPECT_EQ(info.startIndex_, 2); - EXPECT_EQ(info.endIndex_, 9); - EXPECT_EQ(info.segmentStartPos_.size(), 2); - EXPECT_EQ(info.segmentStartPos_[1], 408.0f); - EXPECT_EQ(info.itemInfos_[3].mainOffset, 305.0f); - EXPECT_EQ(info.itemInfos_[3].crossIdx, 0); + EXPECT_EQ(info->currentOffset_, -205.0f); + EXPECT_EQ(info->startIndex_, 2); + EXPECT_EQ(info->endIndex_, 9); + EXPECT_EQ(info->segmentStartPos_.size(), 2); + EXPECT_EQ(info->segmentStartPos_[1], 408.0f); + EXPECT_EQ(info->itemInfos_[3].mainOffset, 305.0f); + EXPECT_EQ(info->itemInfos_[3].crossIdx, 0); UpdateCurrentOffset(-303.0f); - EXPECT_EQ(info.currentOffset_, -508.0f); - EXPECT_EQ(info.startIndex_, 5); - EXPECT_EQ(info.endIndex_, 13); - EXPECT_EQ(info.itemInfos_[7].mainOffset, 613.0f); - EXPECT_EQ(info.itemInfos_[7].crossIdx, 1); + EXPECT_EQ(info->currentOffset_, -508.0f); + EXPECT_EQ(info->startIndex_, 5); + EXPECT_EQ(info->endIndex_, 13); + EXPECT_EQ(info->itemInfos_[7].mainOffset, 613.0f); + EXPECT_EQ(info->itemInfos_[7].crossIdx, 1); UpdateCurrentOffset(1.0f); - EXPECT_EQ(info.currentOffset_, -507.0f); - EXPECT_EQ(info.startIndex_, 4); - EXPECT_EQ(info.endIndex_, 13); + EXPECT_EQ(info->currentOffset_, -507.0f); + EXPECT_EQ(info->startIndex_, 4); + EXPECT_EQ(info->endIndex_, 13); } /** @@ -1624,20 +1624,20 @@ HWTEST_F(WaterFlowSegmentTest, Replace002, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-300.0f); - EXPECT_EQ(info.startIndex_, 2); - EXPECT_EQ(info.storedOffset_, -95); + EXPECT_EQ(info->startIndex_, 2); + EXPECT_EQ(info->storedOffset_, -95); // relative offset to the first item should remain constant secObj->ChangeData(0, 3, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - - EXPECT_EQ(info.currentOffset_, -300.0f); - EXPECT_EQ(info.startIndex_, 2); - EXPECT_EQ(info.storedOffset_, -95); + + EXPECT_EQ(info->currentOffset_, -300.0f); + EXPECT_EQ(info->startIndex_, 2); + EXPECT_EQ(info->storedOffset_, -95); } /** @@ -1769,11 +1769,11 @@ HWTEST_F(WaterFlowSegmentTest, Illegal001, TestSize.Level1) secObj->ChangeData(0, 0, {}); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-205.0f); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, -1); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, -1); } /** @@ -1794,14 +1794,14 @@ HWTEST_F(WaterFlowSegmentTest, Illegal002, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_8); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); // user-defined negative size would be treated as 0 UpdateCurrentOffset(-205.0f); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.itemInfos_[0].mainSize, 0.0f); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 9); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->itemInfos_[0].mainSize, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 9); } /** diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 6299c8c3249..0e5297763fb 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -737,14 +737,14 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest011, TestSize.Level1) EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT), { 0, -ITEM_HEIGHT })); - - pattern_->layoutInfo_.startIndex_ = 0; - pattern_->layoutInfo_.currentOffset_ = ITEM_HEIGHT; + EXPECT_EQ(pattern_->layoutInfo_->offset(), -WATERFLOW_HEIGHT); + pattern_->layoutInfo_->startIndex_ = 0; + pattern_->layoutInfo_->UpdateOffset(ITEM_HEIGHT); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { ITEM_HEIGHT, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT * 2), { -ITEM_HEIGHT, 0 })); - pattern_->layoutInfo_.currentOffset_ = -ITEM_HEIGHT * 3; + pattern_->layoutInfo_->UpdateOffset(-ITEM_HEIGHT * 4); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT * 2), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT), { 0, 0 })); @@ -765,11 +765,14 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest012, TestSize.Level1) model.SetColumnsTemplate("1fr 1fr 1fr 1fr"); CreateItem(TOTAL_LINE_NUMBER); }); - EXPECT_TRUE(pattern_->layoutInfo_.itemStart_); - EXPECT_TRUE(pattern_->layoutInfo_.itemEnd_); - EXPECT_TRUE(pattern_->layoutInfo_.offsetEnd_); - EXPECT_EQ(pattern_->layoutInfo_.maxHeight_, 500); - EXPECT_EQ(pattern_->layoutInfo_.lastMainSize_, 800); + EXPECT_TRUE(pattern_->layoutInfo_->itemStart_); + EXPECT_TRUE(pattern_->layoutInfo_->itemEnd_); + EXPECT_TRUE(pattern_->layoutInfo_->offsetEnd_); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + if (info) { + EXPECT_EQ(info->maxHeight_, 500); + EXPECT_EQ(info->lastMainSize_, 800); + } EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { 100, 100 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(3 * ITEM_HEIGHT), { 300, 300 })); @@ -844,8 +847,8 @@ HWTEST_F(WaterFlowTestNg, WaterFlowPatternTest002, TestSize.Level1) model.SetLayoutDirection(FlexDirection::COLUMN_REVERSE); CreateItem(TOTAL_LINE_NUMBER * 4); }); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 0); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 21); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 21); /** * @tc.steps: step2. UpdateCurrentOffset -100.f. @@ -853,18 +856,18 @@ HWTEST_F(WaterFlowTestNg, WaterFlowPatternTest002, TestSize.Level1) */ HandleDrag(-100.f); pattern_->UpdateScrollBarOffset(); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, 0.f); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 0); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 21); + EXPECT_EQ(pattern_->layoutInfo_->offset(), 0.f); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 21); /** * @tc.steps: step3. UpdateCurrentOffset 200.f. * @tc.expected: startIndex_ = 5 endIndex_ = 27. */ HandleDrag(200.f); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, -ITEM_HEIGHT * 2); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 5); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 27); + EXPECT_EQ(pattern_->layoutInfo_->currentOffset_, -ITEM_HEIGHT * 2); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 5); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 27); } /** @@ -975,7 +978,7 @@ HWTEST_F(WaterFlowTestNg, WaterFlowFooterTest001, TestSize.Level1) * @tc.steps: step1. Run footer func. * @tc.expected: The return_value is correct. */ - auto footerRect = GetChildRect(frameNode_, pattern_->layoutInfo_.footerIndex_); + auto footerRect = GetChildRect(frameNode_, pattern_->layoutInfo_->footerIndex_); EXPECT_FLOAT_EQ(footerRect.GetY(), 200.f); } @@ -1062,7 +1065,7 @@ HWTEST_F(WaterFlowTestNg, Callback002, TestSize.Level1) EXPECT_EQ(effect->initLeadingCallback_(), 0); EXPECT_EQ(effect->currentPositionCallback_(), 0); - pattern_->layoutInfo_.Reset(); + pattern_->layoutInfo_->Reset(); FlushLayoutTask(frameNode_); EXPECT_EQ(effect->leadingCallback_(), 0); EXPECT_EQ(effect->initLeadingCallback_(), 0); @@ -1088,22 +1091,23 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest001, TestSize.Level1) } }); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); /** * @tc.steps: Test IsAllCrossReachEnd function * @tc.expected: step1. Check whether the return value is correct. */ - auto reached = pattern_->layoutInfo_.IsAllCrossReachEnd(ITEM_HEIGHT); + auto reached = info->IsAllCrossReachEnd(ITEM_HEIGHT); EXPECT_TRUE(reached); - reached = pattern_->layoutInfo_.IsAllCrossReachEnd(WATERFLOW_HEIGHT); + reached = info->IsAllCrossReachEnd(WATERFLOW_HEIGHT); EXPECT_TRUE(reached); /** * @tc.steps: Test GetEndIndexByOffset function * @tc.expected: step2. Check whether the return value is correct. */ - auto offset = pattern_->layoutInfo_.GetEndIndexByOffset(0); + auto offset = info->GetEndIndexByOffset(0); EXPECT_EQ(0, offset); - offset = pattern_->layoutInfo_.GetEndIndexByOffset(-100.f); + offset = info->GetEndIndexByOffset(-100.f); EXPECT_EQ(1, offset); } @@ -1140,8 +1144,8 @@ HWTEST_F(WaterFlowTestNg, WaterFlowPattern_distributed001, TestSize.Level1) * @tc.steps: step2. get pattern . * @tc.expected: function ProvideRestoreInfo is called. */ - pattern_->layoutInfo_.startIndex_ = 1; - pattern_->layoutInfo_.storedOffset_ = 1.0f; + pattern_->layoutInfo_->startIndex_ = 1; + pattern_->layoutInfo_->storedOffset_ = 1.0f; std::string ret = pattern_->ProvideRestoreInfo(); /** @@ -1150,8 +1154,8 @@ HWTEST_F(WaterFlowTestNg, WaterFlowPattern_distributed001, TestSize.Level1) */ // std::string restoreInfo = R"({"beginIndex":1,"offset":1.1})"; pattern_->OnRestoreInfo(ret); - EXPECT_EQ(pattern_->layoutInfo_.jumpIndex_, 1); - EXPECT_DOUBLE_EQ(pattern_->layoutInfo_.restoreOffset_, 1.0f); + EXPECT_EQ(pattern_->layoutInfo_->jumpIndex_, 1); + EXPECT_DOUBLE_EQ(pattern_->layoutInfo_->restoreOffset_, 1.0f); } /** @@ -1271,13 +1275,14 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest002, TestSize.Level1) * @tc.steps: Test GetStartMainPos and GetMainHeight * @tc.expected: step2. Check whether the return value is correct. */ - int32_t crossIndex = pattern_->layoutInfo_.items_[0].rbegin()->first; - int32_t itemIndex = pattern_->layoutInfo_.items_[0].rbegin()->second.rbegin()->first; - EXPECT_EQ(pattern_->layoutInfo_.GetStartMainPos(crossIndex + 1, itemIndex), 0.0f); - EXPECT_EQ(pattern_->layoutInfo_.GetMainHeight(crossIndex + 1, itemIndex), 0.0f); - - EXPECT_EQ(pattern_->layoutInfo_.GetStartMainPos(crossIndex, itemIndex + 1), 0.0f); - EXPECT_EQ(pattern_->layoutInfo_.GetMainHeight(crossIndex, itemIndex + 1), 0.0f); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + int32_t crossIndex = info->items_[0].rbegin()->first; + int32_t itemIndex = info->items_[0].rbegin()->second.rbegin()->first; + EXPECT_EQ(info->GetStartMainPos(crossIndex + 1, itemIndex), 0.0f); + EXPECT_EQ(info->GetMainHeight(crossIndex + 1, itemIndex), 0.0f); + + EXPECT_EQ(info->GetStartMainPos(crossIndex, itemIndex + 1), 0.0f); + EXPECT_EQ(info->GetMainHeight(crossIndex, itemIndex + 1), 0.0f); } /** @@ -1293,20 +1298,22 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest003, TestSize.Level1) * @tc.steps: Test GetMainCount function * @tc.expected: step2. Check whether the size is correct. */ - std::size_t waterFlowItemsSize = pattern_->layoutInfo_.items_[0].size(); - int32_t mainCount = pattern_->layoutInfo_.GetMainCount(); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); - int32_t index = pattern_->layoutInfo_.items_[0].rbegin()->first; - pattern_->layoutInfo_.items_[0][index + 1] = std::map>(); - EXPECT_EQ(pattern_->layoutInfo_.items_[0].size(), waterFlowItemsSize + 1); - EXPECT_EQ(pattern_->layoutInfo_.GetMainCount(), mainCount); + std::size_t waterFlowItemsSize = info->items_[0].size(); + int32_t mainCount = info->GetMainCount(); - auto lastItem = pattern_->layoutInfo_.items_[0].begin()->second.rbegin(); + int32_t index = info->items_[0].rbegin()->first; + info->items_[0][index + 1] = std::map>(); + EXPECT_EQ(info->items_[0].size(), waterFlowItemsSize + 1); + EXPECT_EQ(info->GetMainCount(), mainCount); + + auto lastItem = info->items_[0].begin()->second.rbegin(); float mainSize = lastItem->second.first + lastItem->second.second - 1.0f; - EXPECT_FALSE(pattern_->layoutInfo_.IsAllCrossReachEnd(mainSize)); + EXPECT_FALSE(info->IsAllCrossReachEnd(mainSize)); - pattern_->layoutInfo_.ClearCacheAfterIndex(index + 1); - EXPECT_EQ(pattern_->layoutInfo_.items_[0].size(), waterFlowItemsSize + 1); + info->ClearCacheAfterIndex(index + 1); + EXPECT_EQ(info->items_[0].size(), waterFlowItemsSize + 1); } /** @@ -1322,12 +1329,14 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest004, TestSize.Level1) * @tc.steps: Test Reset function * @tc.expected: step2. Check whether the endIndex_ is correct. */ - int32_t resetFrom = pattern_->layoutInfo_.endIndex_; - pattern_->layoutInfo_.Reset(resetFrom + 1); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, resetFrom); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + + int32_t resetFrom = pattern_->layoutInfo_->endIndex_; + info->Reset(resetFrom + 1); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, resetFrom); - pattern_->layoutInfo_.Reset(resetFrom - 1); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, resetFrom); + info->Reset(resetFrom - 1); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, resetFrom); } /** @@ -1343,19 +1352,21 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest005, TestSize.Level1) * @tc.steps: Test GetMaxMainHeight function * @tc.expected: step2. Check whether the return value is correct. */ - float maxMainHeight = pattern_->layoutInfo_.GetMaxMainHeight(); - int32_t crossIndex = pattern_->layoutInfo_.items_[0].rbegin()->first; - pattern_->layoutInfo_.items_[0][crossIndex + 1][0] = std::pair(1.0f, maxMainHeight); - pattern_->layoutInfo_.itemInfos_.clear(); - pattern_->layoutInfo_.endPosArray_.clear(); - EXPECT_EQ(pattern_->layoutInfo_.GetMaxMainHeight(), maxMainHeight + 1.0f); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + + float maxMainHeight = info->GetMaxMainHeight(); + int32_t crossIndex = info->items_[0].rbegin()->first; + info->items_[0][crossIndex + 1][0] = std::pair(1.0f, maxMainHeight); + info->itemInfos_.clear(); + info->endPosArray_.clear(); + EXPECT_EQ(info->GetMaxMainHeight(), maxMainHeight + 1.0f); /** * @tc.steps: Test GetCrossIndexForNextItem function * @tc.expected: step3. Check whether the return value is correct. */ - pattern_->layoutInfo_.items_[0][crossIndex + 1][1] = std::pair(0.0f, 0.0f); - FlowItemIndex position = pattern_->layoutInfo_.GetCrossIndexForNextItem(0); + info->items_[0][crossIndex + 1][1] = std::pair(0.0f, 0.0f); + FlowItemIndex position = info->GetCrossIndexForNextItem(0); EXPECT_EQ(position.crossIndex, crossIndex + 1); EXPECT_EQ(position.lastItemIndex, 1); } @@ -1413,7 +1424,7 @@ HWTEST_F(WaterFlowTestNg, MeasureForAnimation001, TestSize.Level1) * @tc.steps: step2. Get value from pattern_ -> LayoutInfo_ . * @tc.expected: return value(crossIndex) is not -1. */ - auto crossIndex = pattern_->layoutInfo_.GetCrossIndex(10); + auto crossIndex = pattern_->layoutInfo_->GetCrossIndex(10); EXPECT_FALSE(IsEqual(crossIndex, -1)); } -- Gitee From 9b460b442ec3d075eb1c66f1b294d9c06c3250ac Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 15 Apr 2024 21:01:55 +0800 Subject: [PATCH 05/31] implement info Signed-off-by: Tianer Zhou Change-Id: I91c064fa785986e9736a52eb448f3f341f26a591 --- .../water_flow_layout_info_sw.cpp | 159 ++++++++++++++++++ .../water_flow_layout_info_sw.h | 72 ++++---- .../sliding_window/water_flow_sw_layout.cpp | 59 +++++-- .../sliding_window/water_flow_sw_layout.h | 5 +- .../waterflow/water_flow_layout_info.cpp | 4 +- .../waterflow/water_flow_layout_info.h | 1 - .../waterflow/water_flow_layout_info_base.h | 6 +- 7 files changed, 253 insertions(+), 53 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp index a46a7f5d487..e61617b64a7 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp @@ -17,6 +17,8 @@ #include #include +#include "base/utils/utils.h" + namespace OHOS::Ace::NG { void WaterFlowLayoutInfoSW::SyncRange() { @@ -60,8 +62,165 @@ float WaterFlowLayoutInfoSW::DistanceToBottom(int32_t itemIdx, float mainSize, f } return dist; } + float WaterFlowLayoutInfoSW::offset() const { + if (lanes_.empty()) { + return 0.0f; + } return lanes_[0].startPos; } + +bool WaterFlowLayoutInfoSW::OutOfBounds() const +{ + if (lanes_.empty()) { + return false; + } + // checking first lane is enough because re-align automatically happens when reaching start + if (itemStart_ && Positive(lanes_[0].startPos)) { + return true; + } + if (itemEnd_) { + return std::all_of(lanes_.begin(), lanes_.end(), + [mainSize = lastMainSize_](const Lane& lane) { return LessNotEqual(lane.endPos, mainSize); }); + } + return false; +} + +OverScrollOffset WaterFlowLayoutInfoSW::GetOverScrolledDelta(float delta) const +{ + OverScrollOffset res {}; + if (lanes_.empty()) { + return res; + } + + if (startIndex_ == 0) { + float disToTop = -StartPos(); + if (!itemStart_) { + res.start = std::max(0.0f, delta - disToTop); + } else if (Positive(delta)) { + res.start = delta; + } else { + res.start = std::max(delta, disToTop); + } + } + + if (endIndex_ < childrenCount_ - 1) { + return res; + } + float disToBot = EndPos() - lastMainSize_; + if (!itemEnd_) { + res.end = std::min(0.0f, disToBot + delta); + } else if (Negative(delta)) { + res.end = delta; + } else { + res.end = std::min(delta, -disToBot); + } + return res; +} + +float WaterFlowLayoutInfoSW::CalcOverScroll(float mainSize, float delta) const +{ + if (lanes_.empty()) { + return 0.0f; + } + float res = 0.0f; + if (itemStart_) { + res = lanes_[0].startPos + delta; + } + if (itemEnd_) { + res = mainSize - (EndPos() + delta); + } + return res; +} + +inline float WaterFlowLayoutInfoSW::EndPos() const +{ + return std::max_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { + return LessNotEqual(left.endPos, right.endPos); + })->endPos; +} +inline float WaterFlowLayoutInfoSW::StartPos() const +{ + return std::min_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { + return LessNotEqual(left.startPos, right.startPos); + })->startPos; +} + +bool WaterFlowLayoutInfoSW::ReachStart(float prevPos, bool firstLayout) const +{ + if (firstLayout || !itemStart_ || lanes_.empty()) { + return false; + } + return Negative(prevPos); +} + +bool WaterFlowLayoutInfoSW::ReachEnd(float prevPos) const +{ + if (!itemEnd_ || lanes_.empty()) { + return false; + } + float prevEndPos = EndPos() - offset() + prevPos; + return GreatNotEqual(prevEndPos, lastMainSize_); +} + +float WaterFlowLayoutInfoSW::GetContentHeight() const +{ + if (lanes_.empty()) { + return 0.0f; + } + return EndPos() - StartPos(); +} + +int32_t WaterFlowLayoutInfoSW::GetMainCount() const +{ + if (lanes_.empty()) { + return 0; + } + return static_cast(std::max_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { + return left.items_.size() < right.items_.size(); + })->items_.size()); +} + +float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx */) const +{ + if (!idxToLane_.count(idx)) { + return Infinity(); + } + const auto& lane = lanes_[idxToLane_.at(idx)]; + float pos = 0.0f; // main-axis position of the item's top edge relative to viewport top. Positive if below viewport + float itemSize = 0.0f; + if (idx <= endIndex_) { + pos = DistanceToTop(idx, mainGap_); + auto it = std::find_if( + lane.items_.begin(), lane.items_.end(), [idx](const ItemInfo& item) { return item.idx == idx; }); + if (it == lane.items_.end()) { + std::abort(); + } + itemSize = it->mainSize; + } else { + pos = -DistanceToBottom(idx, lastMainSize_, mainGap_) - lastMainSize_; + if (lane.items_.back().idx != idx) { + std::abort(); + } + itemSize = lane.items_.back().mainSize; + } + switch (align_) { + case ScrollAlign::START: + return pos; + case ScrollAlign::END: + return pos - lastMainSize_ + itemSize; + case ScrollAlign::AUTO: + if (Negative(pos)) { + return pos; + } else if (GreatNotEqual(pos + itemSize, lastMainSize_)) { + return pos - lastMainSize_ + itemSize; + } + return 0.0f; // already in viewport, no movement needed + case ScrollAlign::CENTER: + return pos - (lastMainSize_ - itemSize) / 2; + default: + return 0.0f; + } +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h index e9f31aa5100..656cf1e7644 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h @@ -28,49 +28,58 @@ namespace OHOS::Ace::NG { */ class WaterFlowLayoutInfoSW : public WaterFlowLayoutInfoBase { DECLARE_ACE_TYPE(WaterFlowLayoutInfoSW, WaterFlowLayoutInfoBase); -public: +public: float offset() const override; - int32_t firstIdx() const override {return startIndex_;} - - void UpdateOffset(float delta) override {} + int32_t firstIdx() const override + { + return startIndex_; + } - int32_t GetCrossIndex(int32_t itemIndex) const override {} + void UpdateOffset(float delta) override + { + delta_ = delta; + } + int32_t GetCrossIndex(int32_t itemIndex) const override + { + if (idxToLane_.count(itemIndex)) { + return static_cast(idxToLane_.at(itemIndex)); + } + return -1; + } - OverScrollOffset GetOverScrolledDelta(float delta) const override {} + OverScrollOffset GetOverScrolledDelta(float delta) const override; - float CalcOverScroll(float mainSize, float delta) const override {} + float CalcOverScroll(float mainSize, float delta) const override; - bool ReachStart(float prevPos, bool firstLayout) const override {} + bool ReachStart(float prevPos, bool firstLayout) const override; - bool ReachEnd(float prevPos) const override {} + bool ReachEnd(float prevPos) const override; - bool OutOfBounds() const override {} + bool OutOfBounds() const override; - /** - * @return total height of all recorded items. - */ - float GetContentHeight() const override {} + float GetContentHeight() const override; - /** - * @brief Get target item's position in order to perform scrollTo animation. - * - * @param idx item's index. - * @param crossIdx item's cross-axis lane index. - * @return position - */ - float CalcTargetPosition(int32_t idx, int32_t crossIdx) const override {} + float CalcTargetPosition(int32_t idx, int32_t crossIdx) const override; - /** - * @return change in position, comparing to [prevPos] - */ - float GetDelta(float prevPos) const override {return delta_;} + float GetDelta(float prevPos) const override + { + return delta_; + } - int32_t GetMainCount() const override {} - int32_t GetCrossCount() const override {} + int32_t GetMainCount() const override; + int32_t GetCrossCount() const override + { + return lanes_.size(); + } - void Reset() override {} + void Reset() override + { + lanes_.clear(); + idxToLane_.clear(); + delta_ = 0.0f; + } void SyncRange(); @@ -98,8 +107,13 @@ public: std::unordered_map idxToLane_; float delta_ = 0.0f; + float mainGap_ = 0.0f; // update this at the end of a layout struct ItemInfo; + +private: + inline float EndPos() const; + inline float StartPos() const; }; struct WaterFlowLayoutInfoSW::ItemInfo { diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp index 3be3a125221..dcbb00f0714 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp @@ -36,6 +36,7 @@ void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) auto [idealSize, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_); Init(idealSize); + CheckReset(); float mainSize = idealSize.MainSize(axis_); if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { @@ -90,7 +91,8 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) mainGap_ = { axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap }; crossGap_ = { axis_ == Axis::VERTICAL ? columnsGap : rowsGap }; - auto crossSize = frameSize.CrossSize(axis_); + mainSize_ = frameSize.MainSize(axis_); + float crossSize = frameSize.CrossSize(axis_); std::pair, bool> cross; auto rowsTemplate = props->GetRowsTemplate().value_or("1fr"); auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr"); @@ -113,6 +115,17 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) } } +void WaterFlowSWLayout::CheckReset() +{ + int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated(); + if (updateIdx != -1) { + if (updateIdx <= info_->endIndex_ && updateIdx >= info_->startIndex_) { + info_->jumpIndex_ = info_->startIndex_; + } + wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1); + } +} + void WaterFlowSWLayout::ApplyOffset(float mainSize, float offset) { for (auto& lane : info_->lanes_) { @@ -156,11 +169,11 @@ void WaterFlowSWLayout::FillBack(float viewportBound, int32_t maxChildIdx) } int32_t idx = info_->endIndex_ + 1; - + auto props = DynamicCast(wrapper_->GetLayoutProperty()); while (!q.empty() && idx <= maxChildIdx) { auto [endPos, laneIdx] = q.top(); q.pop(); - auto child = MeasureChild(idx); + auto child = MeasureChild(props, idx); float size = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); endPos += mainGap_ + size; @@ -188,10 +201,11 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t minChildIdx) int32_t idx = info_->startIndex_ - 1; + auto props = DynamicCast(wrapper_->GetLayoutProperty()); while (!q.empty() && idx >= minChildIdx) { auto [startPos, laneIdx] = q.top(); q.pop(); - auto child = MeasureChild(idx); + auto child = MeasureChild(props, idx); float size = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); startPos -= mainGap_ + size; @@ -281,10 +295,10 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float if (inView || closeToView) { ApplyOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_)); } else { - std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [](auto& it) { - it->items_.clear(); - it->startPos = 0.0f; - it->endPos = 0.0f; + std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [](auto& lane) { + lane.items_.clear(); + lane.startPos = 0.0f; + lane.endPos = 0.0f; }); info_->idxToLane_.clear(); info_->endIndex_ = jumpIdx - 1; @@ -293,15 +307,15 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float break; } case ScrollAlign::CENTER: { - auto child = MeasureChild(jumpIdx); + auto child = MeasureChild(DynamicCast(wrapper_->GetLayoutProperty()), jumpIdx); float itemH = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); if (inView || closeToView) { ApplyOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize - itemH) / 2.0f); } else { - std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize, itemH](auto& it) { - it->items_.clear(); - it->startPos = (mainSize - itemH) / 2.0f; - it->endPos = (mainSize + itemH) / 2.0f; + std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize, itemH](auto& lane) { + lane.items_.clear(); + lane.startPos = (mainSize - itemH) / 2.0f; + lane.endPos = (mainSize + itemH) / 2.0f; }); auto& lane = info_->lanes_[0]; lane.items_.push_back({ jumpIdx, itemH }); @@ -316,10 +330,10 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float if (inView || closeToView) { ApplyOffset(mainSize, info_->DistanceToBottom(jumpIdx, mainSize, mainGap_)); } else { - std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize](auto& it) { - it->items_.clear(); - it->startPos = mainSize; - it->endPos = mainSize; + std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize](auto& lane) { + lane.items_.clear(); + lane.startPos = mainSize; + lane.endPos = mainSize; }); info_->idxToLane_.clear(); info_->startIndex_ = jumpIdx + 1; @@ -350,4 +364,13 @@ void WaterFlowSWLayout::AdjustOverScroll(float mainSize) ApplyOffset(mainSize, mainSize - maxEnd->endPos); } } -} // namespace OHOS::Ace::NG \ No newline at end of file + +RefPtr WaterFlowSWLayout::MeasureChild(const RefPtr& props, int32_t idx) +{ + auto child = wrapper_->GetOrCreateChildByIndex(idx); + CHECK_NULL_RETURN(child, nullptr); + child->Measure(WaterFlowLayoutUtils::CreateChildConstraint( + { itemCrossSize_[info_->idxToLane_.at(idx)], mainSize_, axis_ }, props, child)); + return child; +} +} // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h index 9878f868fdb..fb6990ccc28 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h @@ -76,13 +76,16 @@ private: void AdjustOverScroll(float mainSize); - RefPtr MeasureChild(int32_t idx); + RefPtr MeasureChild(const RefPtr& props, int32_t idx); + + void CheckReset(); LayoutWrapper* wrapper_ {}; RefPtr info_; Axis axis_ {}; std::vector itemCrossSize_; + float mainSize_ = 0.0f; float mainGap_ = 0.0f; float crossGap_ = 0.0f; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 7d232f237d1..4c491d7fb5e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -576,10 +576,10 @@ float WaterFlowLayoutInfo::CalcOverScroll(float mainSize, float delta) const { float res = 0; if (itemStart_) { - res = offset() + delta; + res = currentOffset_ + delta; } if (offsetEnd_) { - res = mainSize - (GetMaxMainHeight() + offset() - delta); + res = mainSize - (GetMaxMainHeight() + currentOffset_ - delta); } return res; } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index 71541a39176..79a0eefdc47 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -163,7 +163,6 @@ public: float currentOffset_ = 0.0f; float prevOffset_ = 0.0f; - float lastMainSize_ = 0.0f; // 0.0f until itemEnd_ is true float maxHeight_ = 0.0f; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index 5f2765cdd65..804f031fce6 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -106,12 +106,12 @@ public: // for compatibility virtual void UpdateStartIndex() {}; - Axis axis_ = Axis::VERTICAL; - bool itemStart_ = false; bool itemEnd_ = false; // last item is partially in viewport bool offsetEnd_ = false; // last item's bottom is in viewport + Axis axis_ = Axis::VERTICAL; + int32_t jumpIndex_ = EMPTY_JUMP_INDEX; ScrollAlign align_ = ScrollAlign::START; std::optional targetIndex_; @@ -121,6 +121,8 @@ public: int32_t footerIndex_ = -1; int32_t childrenCount_ = 0; + float lastMainSize_ = 0.0f; + // store offset for distributed migration float storedOffset_ = 0.0f; float restoreOffset_ = 0.0f; -- Gitee From e6a4c187708f9672e581c0b4f254f4bdc0f51cbc Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Thu, 18 Apr 2024 11:02:33 +0800 Subject: [PATCH 06/31] use test Signed-off-by: Tianer Zhou Change-Id: Ic10bb995924a91009a153f0b7513700c3500972b --- test/unittest/core/BUILD.gn | 3 +- test/unittest/core/pattern/BUILD.gn | 1 + test/unittest/core/pattern/waterflow/BUILD.gn | 6 +++ .../water_flow_segment_layout_test.cpp | 54 +++++++++++-------- .../pattern/waterflow/water_flow_test_ng.cpp | 3 ++ 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/test/unittest/core/BUILD.gn b/test/unittest/core/BUILD.gn index eda93b44ace..4f288be2312 100644 --- a/test/unittest/core/BUILD.gn +++ b/test/unittest/core/BUILD.gn @@ -167,8 +167,7 @@ group("linux_core_unittest") { # "pattern/waterflow:water_flow_test_ng", "pattern/waterflow:water_flow_test_old", - - # "pattern/xcomponent:xcomponent_test_ng", + "pattern/waterflow:water_flow_test_sw", "property:core_property_unittest", "render:core_render_unittest", "syntax:core_syntax_unittest", diff --git a/test/unittest/core/pattern/BUILD.gn b/test/unittest/core/pattern/BUILD.gn index 0a02833d65a..e4ce9d3a06a 100644 --- a/test/unittest/core/pattern/BUILD.gn +++ b/test/unittest/core/pattern/BUILD.gn @@ -104,6 +104,7 @@ group("core_pattern_unittest") { "video:video_test_ng", "waterflow:water_flow_test_ng", "waterflow:water_flow_test_old", + "waterflow:water_flow_test_sw", "xcomponent:xcomponent_test_ng", ] } diff --git a/test/unittest/core/pattern/waterflow/BUILD.gn b/test/unittest/core/pattern/waterflow/BUILD.gn index b860f6a0560..d20da5df349 100644 --- a/test/unittest/core/pattern/waterflow/BUILD.gn +++ b/test/unittest/core/pattern/waterflow/BUILD.gn @@ -28,3 +28,9 @@ ace_unittest("water_flow_test_ng") { "water_flow_test_ng.cpp", ] } + +ace_unittest("water_flow_test_sw") { + type = "new" + defines = [ "TEST_WATER_FLOW_SW" ] + sources = [ "water_flow_test_ng.cpp" ] +} diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp index 8bca536a9d6..f8999f111df 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -121,7 +121,7 @@ HWTEST_F(WaterFlowSegmentTest, Fill001, TestSize.Level1) }, false); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); algo->wrapper_ = AceType::RawPtr(frameNode_); algo->mainSize_ = 2000.0f; algo->itemsCrossSize_ = { { 50.0f, 50.0f, 50.0f, 50.0f }, {}, { 70.0f, 70.0f, 70.0f } }; @@ -167,7 +167,7 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnOffset001, TestSize.Level1) layoutProperty_->layoutConstraint_ = constraint; layoutProperty_->contentConstraint_ = constraint; - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -306,7 +306,7 @@ HWTEST_F(WaterFlowSegmentTest, Layout001, TestSize.Level1) { SetUpConfig1(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -337,7 +337,7 @@ HWTEST_F(WaterFlowSegmentTest, Layout002, TestSize.Level1) { SetUpConfig1(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -370,7 +370,7 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnOffset002, TestSize.Level1) { SetUpConfig2(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -430,7 +430,7 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnJump001, TestSize.Level1) { SetUpConfig2(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -470,7 +470,7 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnJump002, TestSize.Level1) { SetUpConfig2(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -522,7 +522,7 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnJump003, TestSize.Level1) { SetUpConfig2(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -563,7 +563,7 @@ HWTEST_F(WaterFlowSegmentTest, MeasureOnJump004, TestSize.Level1) { SetUpConfig2(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -612,7 +612,7 @@ HWTEST_F(WaterFlowSegmentTest, Reset001, TestSize.Level1) { SetUpConfig2(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -645,7 +645,7 @@ HWTEST_F(WaterFlowSegmentTest, Reset002, TestSize.Level1) { SetUpConfig2(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -705,7 +705,7 @@ HWTEST_F(WaterFlowSegmentTest, Reset003, TestSize.Level1) { SetUpConfig2(); - auto algo = AceType::MakeRefPtr(WaterFlowLayoutInfo {}); + auto algo = AceType::MakeRefPtr(AceType::MakeRefPtr()); auto& info = algo->info_; info->footerIndex_ = 0; @@ -840,7 +840,8 @@ HWTEST_F(WaterFlowSegmentTest, Segmented001, TestSize.Level1) layoutProperty_->layoutConstraint_ = constraint; layoutProperty_->contentConstraint_ = constraint; - auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); + auto algo = + AceType::MakeRefPtr(AceType::DynamicCast(pattern_->layoutInfo_)); auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); @@ -889,7 +890,8 @@ HWTEST_F(WaterFlowSegmentTest, Segmented005, TestSize.Level1) layoutProperty_->layoutConstraint_ = constraint; layoutProperty_->contentConstraint_ = constraint; - auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); + auto algo = + AceType::MakeRefPtr(AceType::DynamicCast(pattern_->layoutInfo_)); auto& info = algo->info_; info->jumpIndex_ = 50; info->align_ = ScrollAlign::END; @@ -903,7 +905,7 @@ HWTEST_F(WaterFlowSegmentTest, Segmented005, TestSize.Level1) secObj->ChangeData(0, 3, SECTION_5); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); EXPECT_EQ(secObj->GetSectionInfo().size(), 4); - info = pattern_->layoutInfo_; + info = AceType::DynamicCast(pattern_->layoutInfo_); EXPECT_EQ(info->startIndex_, 47); EXPECT_EQ(info->segmentTails_.size(), 4); EXPECT_EQ(info->segmentTails_[3], 59); @@ -927,7 +929,8 @@ HWTEST_F(WaterFlowSegmentTest, Segmented002, TestSize.Level1) { SetUpConfig5(); - auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); + auto algo = + AceType::MakeRefPtr(AceType::DynamicCast(pattern_->layoutInfo_)); auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); @@ -976,7 +979,8 @@ HWTEST_F(WaterFlowSegmentTest, Segmented002, TestSize.Level1) HWTEST_F(WaterFlowSegmentTest, Segmented003, TestSize.Level1) { SetUpConfig5(); - auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); + auto algo = + AceType::MakeRefPtr(AceType::DynamicCast(pattern_->layoutInfo_)); auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); @@ -1029,7 +1033,8 @@ HWTEST_F(WaterFlowSegmentTest, Segmented004, TestSize.Level1) { SetUpConfig5(); - auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); + auto algo = + AceType::MakeRefPtr(AceType::DynamicCast(pattern_->layoutInfo_)); auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); @@ -1051,7 +1056,7 @@ HWTEST_F(WaterFlowSegmentTest, Segmented004, TestSize.Level1) secObj->ChangeData(4, 0, ADD_SECTION_6); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); AddItems(10); - info = pattern_->layoutInfo_; + info = AceType::DynamicCast(pattern_->layoutInfo_); EXPECT_EQ(info->itemInfos_.size(), 16); algo->Measure(AceType::RawPtr(frameNode_)); @@ -1201,7 +1206,8 @@ HWTEST_F(WaterFlowSegmentTest, TargetIndex001, TestSize.Level1) { SetUpConfig5(); - auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); + auto algo = + AceType::MakeRefPtr(AceType::DynamicCast(pattern_->layoutInfo_)); auto& info = algo->info_; info->targetIndex_ = 50; @@ -1234,7 +1240,8 @@ HWTEST_F(WaterFlowSegmentTest, ChildrenCount001, TestSize.Level1) layoutProperty_->layoutConstraint_ = constraint; layoutProperty_->contentConstraint_ = constraint; - auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); + auto algo = + AceType::MakeRefPtr(AceType::DynamicCast(pattern_->layoutInfo_)); // cause layout abort auto& info = algo->info_; @@ -1282,7 +1289,8 @@ HWTEST_F(WaterFlowSegmentTest, ChildrenCount002, TestSize.Level1) layoutProperty_->layoutConstraint_ = constraint; layoutProperty_->contentConstraint_ = constraint; - auto algo = AceType::MakeRefPtr(pattern_->layoutInfo_); + auto algo = + AceType::MakeRefPtr(AceType::DynamicCast(pattern_->layoutInfo_)); auto& info = algo->info_; algo->Measure(AceType::RawPtr(frameNode_)); @@ -1634,7 +1642,7 @@ HWTEST_F(WaterFlowSegmentTest, Replace002, TestSize.Level1) secObj->ChangeData(0, 3, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - + EXPECT_EQ(info->currentOffset_, -300.0f); EXPECT_EQ(info->startIndex_, 2); EXPECT_EQ(info->storedOffset_, -95); diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 0e5297763fb..ab6136d04e7 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -121,6 +121,9 @@ void WaterFlowTestNg::Create(const std::function& callba callback(model); } GetInstance(); +#ifdef TEST_WATER_FLOW_SW + pattern_->layoutMode_ = WaterFlowLayoutMode::SLIDING_WINDOW; +#endif if (flushLayout) { FlushLayoutTask(frameNode_); } -- Gitee From 8872f41a68321cdab8fe4c6bbf1f75ebc49fec61 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 22 Apr 2024 15:39:50 +0800 Subject: [PATCH 07/31] implement frontend Signed-off-by: Tianer Zhou Change-Id: I7111183decaff7b4c3e21fcb7d2103093ad716d8 --- .../jsview/js_water_flow.cpp | 12 ++++++++ .../jsview/models/water_flow_model_impl.h | 1 + .../core/components_ng/pattern/BUILD.gn | 4 +-- .../water_flow_layout_info_sw.cpp | 2 +- .../water_flow_layout_info_sw.h | 6 ++++ .../sliding_window/water_flow_sw_layout.cpp | 2 +- .../sliding_window/water_flow_sw_layout.h | 2 +- .../waterflow/water_flow_layout_algorithm.h | 11 +------ .../water_flow_layout_algorithm_base.h | 30 +++++++++++++++++++ .../waterflow/water_flow_layout_info.cpp | 12 ++++++++ .../waterflow/water_flow_layout_info.h | 5 ++++ .../waterflow/water_flow_layout_info_base.h | 6 ++++ .../pattern/waterflow/water_flow_model.h | 2 ++ .../pattern/waterflow/water_flow_model_ng.cpp | 7 +++++ .../pattern/waterflow/water_flow_model_ng.h | 1 + .../pattern/waterflow/water_flow_pattern.cpp | 12 +++++++- .../pattern/waterflow/water_flow_pattern.h | 5 +--- 17 files changed, 100 insertions(+), 20 deletions(-) rename frameworks/core/components_ng/pattern/waterflow/{ => layout}/sliding_window/water_flow_layout_info_sw.cpp (98%) rename frameworks/core/components_ng/pattern/waterflow/{ => layout}/sliding_window/water_flow_layout_info_sw.h (94%) rename frameworks/core/components_ng/pattern/waterflow/{ => layout}/sliding_window/water_flow_sw_layout.cpp (99%) rename frameworks/core/components_ng/pattern/waterflow/{ => layout}/sliding_window/water_flow_sw_layout.h (97%) create mode 100644 frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h diff --git a/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp b/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp index 5504dcb4429..bece88496bd 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp @@ -147,6 +147,18 @@ void JSWaterFlow::Create(const JSCallbackInfo& args) } JSRef obj = JSRef::Cast(args[0]); + // Set layout mode first because SetFooter is dependent to it + using LayoutMode = NG::WaterFlowLayoutMode; + auto mode = LayoutMode::SLIDING_WINDOW; + auto jsMode = obj->GetProperty("layoutMode"); + if (jsMode->IsNumber()) { + mode = static_cast(jsMode->ToNumber()); + if (mode < LayoutMode::TOP_DOWN || mode > LayoutMode::SLIDING_WINDOW) { + mode = LayoutMode::TOP_DOWN; + } + } + WaterFlowModel::GetInstance()->SetLayoutMode(mode); + auto scroller = obj->GetProperty("scroller"); if (scroller->IsObject()) { auto* jsScroller = JSRef::Cast(scroller)->Unwrap(); diff --git a/frameworks/bridge/declarative_frontend/jsview/models/water_flow_model_impl.h b/frameworks/bridge/declarative_frontend/jsview/models/water_flow_model_impl.h index 1779c2d7e15..5e780519c37 100644 --- a/frameworks/bridge/declarative_frontend/jsview/models/water_flow_model_impl.h +++ b/frameworks/bridge/declarative_frontend/jsview/models/water_flow_model_impl.h @@ -26,6 +26,7 @@ public: RefPtr CreateScrollController() override; RefPtr CreateScrollBarProxy() override; void SetScroller(RefPtr scroller, RefPtr proxy) override; + void SetLayoutMode(NG::WaterFlowLayoutMode mode) override {} void SetColumnsTemplate(const std::string& value) override; void SetRowsTemplate(const std::string& value) override; diff --git a/frameworks/core/components_ng/pattern/BUILD.gn b/frameworks/core/components_ng/pattern/BUILD.gn index 5e393d0f703..e167f73d362 100644 --- a/frameworks/core/components_ng/pattern/BUILD.gn +++ b/frameworks/core/components_ng/pattern/BUILD.gn @@ -524,8 +524,8 @@ build_component_ng("pattern_ng") { "video/video_node.cpp", "video/video_pattern.cpp", "view_context/view_context_model_ng.cpp", - "waterflow/sliding_window/water_flow_layout_info_sw.cpp", - "waterflow/sliding_window/water_flow_sw_layout.cpp", + "waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp", + "waterflow/layout/sliding_window/water_flow_sw_layout.cpp", "waterflow/water_flow_accessibility_property.cpp", "waterflow/water_flow_content_modifier.cpp", "waterflow/water_flow_item_model_ng.cpp", diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp similarity index 98% rename from frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp rename to frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index e61617b64a7..c40c7a96569 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h" +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" #include #include diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h similarity index 94% rename from frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h rename to frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 656cf1e7644..0efa02e148a 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -19,6 +19,7 @@ #include #include +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" namespace OHOS::Ace::NG { @@ -30,6 +31,11 @@ class WaterFlowLayoutInfoSW : public WaterFlowLayoutInfoBase { DECLARE_ACE_TYPE(WaterFlowLayoutInfoSW, WaterFlowLayoutInfoBase); public: + WaterFlowLayoutMode mode() const override + { + return WaterFlowLayoutMode::SLIDING_WINDOW; + } + float offset() const override; int32_t firstIdx() const override { diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp similarity index 99% rename from frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp rename to frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index dcbb00f0714..076dbe98a9b 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h" +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h" #include #include diff --git a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h similarity index 97% rename from frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h rename to frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index fb6990ccc28..c2eb0a6dc45 100644 --- a/frameworks/core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -18,7 +18,7 @@ #include "core/components/scroll/scroll_controller_base.h" #include "core/components_ng/layout/layout_wrapper.h" -#include "core/components_ng/pattern/waterflow/sliding_window/water_flow_layout_info_sw.h" +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" namespace OHOS::Ace::NG { diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h index 38b9eef3325..198b1c23363 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h @@ -16,20 +16,11 @@ #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_ALGORITHM_H #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_ALGORITHM_H -#include "core/components_ng/layout/layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" namespace OHOS::Ace::NG { -class WaterFlowLayoutBase : public LayoutAlgorithm { - DECLARE_ACE_TYPE(WaterFlowLayoutBase, LayoutAlgorithm); - -public: - virtual void SetCanOverScroll(bool canOverScroll) = 0; -}; - -enum class WaterFlowLayoutMode { TOP_DOWN = 0, SLIDING_WINDOW = 1 }; - class ACE_EXPORT WaterFlowLayoutAlgorithm : public WaterFlowLayoutBase { DECLARE_ACE_TYPE(WaterFlowLayoutAlgorithm, WaterFlowLayoutBase); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h new file mode 100644 index 00000000000..4466c531fc4 --- /dev/null +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h @@ -0,0 +1,30 @@ +/* + * 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_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_BASE_H +#define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_BASE_H +#include "core/components_ng/layout/layout_algorithm.h" + +namespace OHOS::Ace::NG { +class WaterFlowLayoutBase : public LayoutAlgorithm { + DECLARE_ACE_TYPE(WaterFlowLayoutBase, LayoutAlgorithm); + +public: + virtual void SetCanOverScroll(bool canOverScroll) = 0; +}; + +enum class WaterFlowLayoutMode { TOP_DOWN = 0, SLIDING_WINDOW = 1 }; +} // namespace OHOS::Ace::NG +#endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_BASE_H diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 4c491d7fb5e..5ae5ab47d61 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -17,6 +17,8 @@ #include +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/property/calc_length.h" #include "core/components_ng/property/measure_property.h" #include "core/components_ng/property/measure_utils.h" @@ -24,6 +26,16 @@ constexpr float HALF = 0.5f; namespace OHOS::Ace::NG { +RefPtr WaterFlowLayoutInfoBase::Create(WaterFlowLayoutMode mode) +{ + switch (mode) { + case WaterFlowLayoutMode::SLIDING_WINDOW: + return MakeRefPtr(); + default: + return MakeRefPtr(); + } +} + int32_t WaterFlowLayoutInfo::GetCrossIndex(int32_t itemIndex) const { if (static_cast(itemIndex) < itemInfos_.size()) { diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index 79a0eefdc47..036979326a0 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -22,6 +22,7 @@ #include #include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_sections.h" #include "core/components_ng/property/measure_property.h" @@ -44,6 +45,10 @@ public: WaterFlowLayoutInfo() = default; ~WaterFlowLayoutInfo() override = default; + WaterFlowLayoutMode mode() const override + { + return WaterFlowLayoutMode::TOP_DOWN; + } float offset() const override { return currentOffset_; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index 804f031fce6..de61958bdbe 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -25,6 +25,8 @@ namespace OHOS::Ace::NG { constexpr int32_t EMPTY_JUMP_INDEX = -2; +enum class WaterFlowLayoutMode; + class WaterFlowLayoutInfoBase : public AceType { DECLARE_ACE_TYPE(WaterFlowLayoutInfoBase, AceType); @@ -32,7 +34,11 @@ public: WaterFlowLayoutInfoBase() = default; ~WaterFlowLayoutInfoBase() override = default; + /* Factory method */ + static RefPtr Create(WaterFlowLayoutMode mode); + /* PURE GETTERs */ + virtual WaterFlowLayoutMode mode() const = 0; virtual float offset() const = 0; virtual int32_t firstIdx() const = 0; // for compatibility diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_model.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_model.h index 6f2f01b4862..665f1dd132d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_model.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_model.h @@ -22,6 +22,7 @@ #include "core/components/scroll/scroll_controller_base.h" #include "core/components/scroll_bar/scroll_proxy.h" #include "core/components_ng/pattern/scrollable/scrollable_properties.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/pattern/waterflow/water_flow_sections.h" namespace OHOS::Ace { @@ -35,6 +36,7 @@ public: virtual RefPtr CreateScrollController() = 0; virtual RefPtr CreateScrollBarProxy() = 0; virtual void SetScroller(RefPtr scroller, RefPtr proxy) = 0; + virtual void SetLayoutMode(NG::WaterFlowLayoutMode mode) = 0; virtual void SetColumnsTemplate(const std::string& value) = 0; virtual void SetRowsTemplate(const std::string& value) = 0; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.cpp index 7dc16041ed1..7787f65df23 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.cpp @@ -90,6 +90,13 @@ void WaterFlowModelNG::SetScroller(RefPtr scroller, RefPtr waterFlow->SetScrollBarProxy(AceType::DynamicCast(proxy)); } +void WaterFlowModelNG::SetLayoutMode(WaterFlowLayoutMode mode) +{ + auto waterFlow = ViewStackProcessor::GetInstance()->GetMainFrameNodePattern(); + CHECK_NULL_VOID(waterFlow); + waterFlow->SetLayoutMode(mode); +} + void WaterFlowModelNG::SetColumnsTemplate(const std::string& value) { if (value.empty()) { diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.h index 0b71db6d55d..c2adbf58f87 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_model_ng.h @@ -28,6 +28,7 @@ public: RefPtr CreateScrollController() override; RefPtr CreateScrollBarProxy() override; void SetScroller(RefPtr scroller, RefPtr proxy) override; + void SetLayoutMode(WaterFlowLayoutMode mode) override; void SetColumnsTemplate(const std::string& value) override; void SetRowsTemplate(const std::string& value) override; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index dbbff545a24..9f987ccaacd 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -17,9 +17,10 @@ #include "base/utils/utils.h" #include "core/components/scroll/scroll_controller_base.h" -#include "core/components_ng/pattern/waterflow/sliding_window/water_flow_sw_layout.h" +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_paint_method.h" #include "core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" @@ -564,4 +565,13 @@ void WaterFlowPattern::AddFooter(const RefPtr& footer) footer_ = footer; footer->SetActive(false); } + +void WaterFlowPattern::SetLayoutMode(WaterFlowLayoutMode mode) +{ + layoutMode_ = mode; + + if (!layoutInfo_ || mode != layoutInfo_->mode()) { + layoutInfo_ = WaterFlowLayoutInfoBase::Create(mode); + } +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index f7d4d9258d7..e123cd66dcf 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -42,10 +42,7 @@ public: OverScrollOffset GetOverScrollOffset(double delta) const override; void UpdateScrollBarOffset() override; - void SetLayoutMode(WaterFlowLayoutMode mode) - { - layoutMode_ = mode; - } + void SetLayoutMode(WaterFlowLayoutMode mode); RefPtr CreateLayoutAlgorithm() override; -- Gitee From 881bbe8201ce75882ad8a9ddb38ce37d990fe3f9 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 22 Apr 2024 17:26:37 +0800 Subject: [PATCH 08/31] adjust tests Signed-off-by: Tianer Zhou Change-Id: I1b23d8c4b3ac64505cdd7e63776779896e66f2c6 --- .../water_flow_layout_info_sw.cpp | 17 ++- .../water_flow_layout_info_sw.h | 13 +- .../sliding_window/water_flow_sw_layout.cpp | 67 +++++---- .../sliding_window/water_flow_sw_layout.h | 2 +- .../pattern/waterflow/water_flow_pattern.cpp | 17 ++- .../pattern/waterflow/water_flow_pattern.h | 4 +- test/unittest/BUILD.gn | 2 + test/unittest/core/pattern/waterflow/BUILD.gn | 11 +- .../waterflow/water_flow_sw_layout_test.cpp | 0 .../pattern/waterflow/water_flow_test_ng.cpp | 6 +- .../waterflow/water_flow_top_down_test.cpp | 140 ++++++++++++++++++ 11 files changed, 226 insertions(+), 53 deletions(-) create mode 100644 test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp create mode 100644 test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index c40c7a96569..b295231307d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -20,7 +20,7 @@ #include "base/utils/utils.h" namespace OHOS::Ace::NG { -void WaterFlowLayoutInfoSW::SyncRange() +void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) { for (const auto& lane : lanes_) { if (lane.items_.empty()) { @@ -29,6 +29,13 @@ void WaterFlowLayoutInfoSW::SyncRange() startIndex_ = std::min(startIndex_, lane.items_.front().idx); endIndex_ = std::max(endIndex_, lane.items_.back().idx); } + delta_ = 0.0f; + lastMainSize_ = mainSize; + mainGap_ = mainGap; + + itemStart_ = startIndex_ == 0 && NonNegative(DistanceToTop(0, mainGap_)); + itemEnd_ = endIndex_ == childrenCount_ - 1; + offsetEnd_ = itemEnd_ && NonNegative(DistanceToBottom(endIndex_, mainSize, mainGap)); } float WaterFlowLayoutInfoSW::DistanceToTop(int32_t itemIdx, float mainGap) const @@ -223,4 +230,12 @@ float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx return 0.0f; } } + +void WaterFlowLayoutInfoSW::Reset() +{ + jumpIndex_ = startIndex_; + delta_ = DistanceToTop(startIndex_, mainGap_); + lanes_.clear(); + idxToLane_.clear(); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 0efa02e148a..7d501ad4566 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -71,7 +71,7 @@ public: float GetDelta(float prevPos) const override { - return delta_; + return offset() - prevPos; } int32_t GetMainCount() const override; @@ -80,14 +80,9 @@ public: return lanes_.size(); } - void Reset() override - { - lanes_.clear(); - idxToLane_.clear(); - delta_ = 0.0f; - } + void Reset() override; - void SyncRange(); + void Sync(float mainSize, float mainGap); /** * @brief Calculates distance from the item's top edge to the top of the viewport. @@ -104,7 +99,7 @@ public: * @param item idx * @param mainSize of the viewport * @param mainGap - * @return positive result when it's bottom edge is above viewport. + * @return positive result when item's bottom edge is above viewport. */ float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index 076dbe98a9b..14ef828022f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -30,6 +30,7 @@ namespace OHOS::Ace::NG { void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) { + LOGI("ZTE sw"); wrapper_ = wrapper; auto props = DynamicCast(wrapper->GetLayoutProperty()); axis_ = axis_ = props->GetAxis(); @@ -38,23 +39,27 @@ void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) Init(idealSize); CheckReset(); - float mainSize = idealSize.MainSize(axis_); if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { - MeasureOnJump(info_->jumpIndex_, info_->align_, mainSize); + MeasureOnJump(info_->jumpIndex_, info_->align_, mainSize_); + overScroll_ = false; } else if (info_->targetIndex_) { MeasureToTarget(*info_->targetIndex_); - } else { - ApplyOffset(mainSize, info_->delta_); - if (!overScroll_) { - AdjustOverScroll(mainSize); - } } + LOGI("ZTE delta = %f", info_->delta_); + ApplyOffset(mainSize_, info_->delta_); + if (!overScroll_) { + AdjustOverScroll(mainSize_); + } + // if (matchChildren) { + // PostMeasureSelf(); + // } + + info_->Sync(mainSize_, mainGap_); wrapper->SetCacheCount(props->GetCachedCountValue(1)); } void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) { - wrapper->RemoveAllChildInRenderTree(); if (info_->lanes_.empty()) { return; } @@ -80,6 +85,7 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) } crossPos += itemCrossSize_[i] + crossGap_; } + wrapper->SetActiveChildRange(info_->startIndex_, info_->endIndex_); } void WaterFlowSWLayout::Init(const SizeF& frameSize) @@ -107,6 +113,10 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) crossGap_ = 0.0f; } + if (info_->lanes_.empty()) { + info_->lanes_.resize(cross.first.size()); + } + for (const auto& len : cross.first) { itemCrossSize_.push_back(static_cast(len)); } @@ -142,8 +152,6 @@ void WaterFlowSWLayout::ApplyOffset(float mainSize, float offset) ClearFront(); FillBack(mainSize, info_->childrenCount_ - 1); } - - info_->SyncRange(); } void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) @@ -155,7 +163,7 @@ void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) } } -using lanePos = std::pair; +using lanePos = std::pair; void WaterFlowSWLayout::FillBack(float viewportBound, int32_t maxChildIdx) { @@ -173,19 +181,18 @@ void WaterFlowSWLayout::FillBack(float viewportBound, int32_t maxChildIdx) while (!q.empty() && idx <= maxChildIdx) { auto [endPos, laneIdx] = q.top(); q.pop(); - auto child = MeasureChild(props, idx); - - float size = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); - endPos += mainGap_ + size; + float mainLen = MeasureChild(props, idx, laneIdx); + endPos += mainGap_ + mainLen; auto& lane = info_->lanes_[laneIdx]; lane.endPos = endPos; info_->idxToLane_[idx] = laneIdx; - lane.items_.push_back({ idx++, size }); + lane.items_.push_back({ idx++, mainLen }); if (LessNotEqual(endPos, viewportBound)) { q.push({ endPos, laneIdx }); } } + LOGI("ZTE deque size = %d", info_->lanes_[0].items_.size()); } void WaterFlowSWLayout::FillFront(float viewportBound, int32_t minChildIdx) @@ -205,15 +212,13 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t minChildIdx) while (!q.empty() && idx >= minChildIdx) { auto [startPos, laneIdx] = q.top(); q.pop(); - auto child = MeasureChild(props, idx); - - float size = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); - startPos -= mainGap_ + size; + float mainLen = MeasureChild(props, idx, laneIdx); + startPos -= mainGap_ + mainLen; auto& lane = info_->lanes_[laneIdx]; info_->idxToLane_[idx] = laneIdx; lane.startPos = startPos; - lane.items_.push_front({ idx--, size }); + lane.items_.push_front({ idx--, mainLen }); if (GreatNotEqual(startPos - mainGap_, viewportBound)) { q.push({ startPos, laneIdx }); } @@ -307,11 +312,15 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float break; } case ScrollAlign::CENTER: { - auto child = MeasureChild(DynamicCast(wrapper_->GetLayoutProperty()), jumpIdx); - float itemH = child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); + auto props = DynamicCast(wrapper_->GetLayoutProperty()); if (inView || closeToView) { + std::cout << "jumpIdx = " + << " map size = " << info_->idxToLane_.size() << " children size = " << info_->childrenCount_ + << std::endl; + float itemH = MeasureChild(props, jumpIdx, info_->idxToLane_.at(jumpIdx)); ApplyOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize - itemH) / 2.0f); } else { + float itemH = MeasureChild(props, jumpIdx, 0); std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize, itemH](auto& lane) { lane.items_.clear(); lane.startPos = (mainSize - itemH) / 2.0f; @@ -344,8 +353,6 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float default: break; } - info_->SyncRange(); - AdjustOverScroll(mainSize); } void WaterFlowSWLayout::AdjustOverScroll(float mainSize) @@ -365,12 +372,12 @@ void WaterFlowSWLayout::AdjustOverScroll(float mainSize) } } -RefPtr WaterFlowSWLayout::MeasureChild(const RefPtr& props, int32_t idx) +float WaterFlowSWLayout::MeasureChild(const RefPtr& props, int32_t idx, size_t lane) { auto child = wrapper_->GetOrCreateChildByIndex(idx); - CHECK_NULL_RETURN(child, nullptr); - child->Measure(WaterFlowLayoutUtils::CreateChildConstraint( - { itemCrossSize_[info_->idxToLane_.at(idx)], mainSize_, axis_ }, props, child)); - return child; + CHECK_NULL_RETURN(child, 0.0f); + child->Measure( + WaterFlowLayoutUtils::CreateChildConstraint({ itemCrossSize_[lane], mainSize_, axis_ }, props, child)); + return child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); } } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index c2eb0a6dc45..340fb0f2ee5 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -76,7 +76,7 @@ private: void AdjustOverScroll(float mainSize); - RefPtr MeasureChild(const RefPtr& props, int32_t idx); + float MeasureChild(const RefPtr& props, int32_t idx, size_t lane); void CheckReset(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 9f987ccaacd..d74910b68df 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -19,12 +19,15 @@ #include "core/components/scroll/scroll_controller_base.h" #include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_paint_method.h" #include "core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" namespace OHOS::Ace::NG { +using LayoutMode = WaterFlowLayoutMode; + SizeF WaterFlowPattern::GetContentSize() const { auto host = GetHost(); @@ -62,7 +65,8 @@ bool WaterFlowPattern::UpdateCurrentOffset(float delta, int32_t source) if (layoutInfo_->offsetEnd_ && delta < 0) { return false; } - if (layoutMode_ == WaterFlowLayoutMode::TOP_DOWN && GreatNotEqual(delta, 0.0f)) { + if (layoutMode_ == LayoutMode::TOP_DOWN && GreatNotEqual(delta, 0.0f)) { + // adjust top overScroll delta = std::min(delta, -layoutInfo_->offset()); } } @@ -99,6 +103,9 @@ OverScrollOffset WaterFlowPattern::GetOverScrollOffset(double delta) const void WaterFlowPattern::UpdateScrollBarOffset() { + if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + return; + } if (!GetScrollBar() && !GetScrollBarProxy()) { return; } @@ -127,7 +134,7 @@ RefPtr WaterFlowPattern::CreateLayoutAlgorithm() RefPtr algorithm; if (sections_ || SystemProperties::WaterFlowUseSegmentedLayout()) { algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); - } else if (layoutMode_ == WaterFlowLayoutMode::SLIDING_WINDOW) { + } else if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); } else { int32_t footerIndex = -1; @@ -177,7 +184,7 @@ void WaterFlowPattern::OnModifyDone() auto paintProperty = GetPaintProperty(); CHECK_NULL_VOID(paintProperty); - if (layoutMode_ != WaterFlowLayoutMode::SLIDING_WINDOW && paintProperty->GetScrollBarProperty()) { + if (layoutMode_ != LayoutMode::SLIDING_WINDOW && paintProperty->GetScrollBarProperty()) { SetScrollBar(paintProperty->GetScrollBarProperty()); } SetAccessibilityAction(); @@ -385,7 +392,7 @@ Rect WaterFlowPattern::GetItemRect(int32_t index) const RefPtr WaterFlowPattern::GetOrCreateWaterFlowSections() { - if (layoutMode_ == WaterFlowLayoutMode::SLIDING_WINDOW) { + if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { return nullptr; } if (sections_) { @@ -566,7 +573,7 @@ void WaterFlowPattern::AddFooter(const RefPtr& footer) footer->SetActive(false); } -void WaterFlowPattern::SetLayoutMode(WaterFlowLayoutMode mode) +void WaterFlowPattern::SetLayoutMode(LayoutMode mode) { layoutMode_ = mode; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index e123cd66dcf..5d42f6bf5a9 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -159,10 +159,10 @@ private: bool ScrollToTargetIndex(int32_t index); bool NeedRender(); std::optional targetIndex_; - RefPtr layoutInfo_; + WaterFlowLayoutMode layoutMode_ = WaterFlowLayoutMode::TOP_DOWN; + RefPtr layoutInfo_ = WaterFlowLayoutInfoBase::Create(layoutMode_); RefPtr sections_; - WaterFlowLayoutMode layoutMode_ = WaterFlowLayoutMode::TOP_DOWN; float prevOffset_ = 0.0f; SizeF lastSize_; std::pair itemRange_ = { -1, -1 }; diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index e36bd592dcd..5b7602990e6 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -1046,6 +1046,8 @@ ohos_source_set("ace_components_pattern") { "$ace_root/frameworks/core/components_ng/pattern/video/video_node.cpp", "$ace_root/frameworks/core/components_ng/pattern/video/video_pattern.cpp", "$ace_root/frameworks/core/components_ng/pattern/view_context/view_context_model_ng.cpp", + "$ace_root/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp", + "$ace_root/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_accessibility_property.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_content_modifier.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_item_model_ng.cpp", diff --git a/test/unittest/core/pattern/waterflow/BUILD.gn b/test/unittest/core/pattern/waterflow/BUILD.gn index d20da5df349..a9a66e4d84b 100644 --- a/test/unittest/core/pattern/waterflow/BUILD.gn +++ b/test/unittest/core/pattern/waterflow/BUILD.gn @@ -15,7 +15,10 @@ import("//foundation/arkui/ace_engine/test/unittest/ace_unittest.gni") ace_unittest("water_flow_test_old") { type = "new" - sources = [ "water_flow_test_ng.cpp" ] + sources = [ + "water_flow_test_ng.cpp", + "water_flow_top_down_test.cpp", + ] } ace_unittest("water_flow_test_ng") { @@ -26,11 +29,15 @@ ace_unittest("water_flow_test_ng") { "water_flow_scroller_test_ng.cpp", "water_flow_segment_layout_test.cpp", "water_flow_test_ng.cpp", + "water_flow_top_down_test.cpp", ] } ace_unittest("water_flow_test_sw") { type = "new" defines = [ "TEST_WATER_FLOW_SW" ] - sources = [ "water_flow_test_ng.cpp" ] + sources = [ + "water_flow_sw_layout_test.cpp", + "water_flow_test_ng.cpp", + ] } diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index ab6136d04e7..9b54a2323a6 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -120,10 +120,10 @@ void WaterFlowTestNg::Create(const std::function& callba if (callback) { callback(model); } - GetInstance(); #ifdef TEST_WATER_FLOW_SW - pattern_->layoutMode_ = WaterFlowLayoutMode::SLIDING_WINDOW; + model.SetLayoutMode(WaterFlowLayoutMode::SLIDING_WINDOW); #endif + GetInstance(); if (flushLayout) { FlushLayoutTask(frameNode_); } @@ -774,8 +774,8 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest012, TestSize.Level1) auto info = AceType::DynamicCast(pattern_->layoutInfo_); if (info) { EXPECT_EQ(info->maxHeight_, 500); - EXPECT_EQ(info->lastMainSize_, 800); } + EXPECT_EQ(pattern_->layoutInfo_->lastMainSize_, 800); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { 100, 100 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(3 * ITEM_HEIGHT), { 300, 300 })); diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp new file mode 100644 index 00000000000..fca8e5465e7 --- /dev/null +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -0,0 +1,140 @@ +/* + * 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. + */ +#include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" + +#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" + +namespace OHOS::Ace::NG { +/** + * @tc.name: WaterFlowLayoutInfoTest002 + * @tc.desc: Test functions in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Init Waterflow node + */ + CreateWithItem([](WaterFlowModelNG model) {}); + + /** + * @tc.steps: Test GetStartMainPos and GetMainHeight + * @tc.expected: step2. Check whether the return value is correct. + */ + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + int32_t crossIndex = info->items_[0].rbegin()->first; + int32_t itemIndex = info->items_[0].rbegin()->second.rbegin()->first; + EXPECT_EQ(info->GetStartMainPos(crossIndex + 1, itemIndex), 0.0f); + EXPECT_EQ(info->GetMainHeight(crossIndex + 1, itemIndex), 0.0f); + + EXPECT_EQ(info->GetStartMainPos(crossIndex, itemIndex + 1), 0.0f); + EXPECT_EQ(info->GetMainHeight(crossIndex, itemIndex + 1), 0.0f); +} + +/** + * @tc.name: WaterFlowLayoutInfoTest003 + * @tc.desc: Test functions in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest003, TestSize.Level1) +{ + /** + * @tc.steps: step1. Init Waterflow node + */ + CreateWithItem([](WaterFlowModelNG model) {}); + + /** + * @tc.steps: Test GetMainCount function + * @tc.expected: step2. Check whether the size is correct. + */ + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + + std::size_t waterFlowItemsSize = info->items_[0].size(); + int32_t mainCount = info->GetMainCount(); + + int32_t index = info->items_[0].rbegin()->first; + info->items_[0][index + 1] = std::map>(); + EXPECT_EQ(info->items_[0].size(), waterFlowItemsSize + 1); + EXPECT_EQ(info->GetMainCount(), mainCount); + + auto lastItem = info->items_[0].begin()->second.rbegin(); + float mainSize = lastItem->second.first + lastItem->second.second - 1.0f; + EXPECT_FALSE(info->IsAllCrossReachEnd(mainSize)); + + info->ClearCacheAfterIndex(index + 1); + EXPECT_EQ(info->items_[0].size(), waterFlowItemsSize + 1); +} + +/** + * @tc.name: WaterFlowLayoutInfoTest004 + * @tc.desc: Test Reset functions in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest004, TestSize.Level1) +{ + /** + * @tc.steps: step1. Init Waterflow node + */ + CreateWithItem([](WaterFlowModelNG model) {}); + + /** + * @tc.steps: Test Reset function + * @tc.expected: step2. Check whether the endIndex_ is correct. + */ + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + + int32_t resetFrom = pattern_->layoutInfo_->endIndex_; + info->Reset(resetFrom + 1); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, resetFrom); + + info->Reset(resetFrom - 1); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, -1); +} + +/** + * @tc.name: WaterFlowLayoutInfoTest005 + * @tc.desc: Test functions in WaterFlowLayoutInfo. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest005, TestSize.Level1) +{ + /** + * @tc.steps: step1. Init Waterflow node + */ + CreateWithItem([](WaterFlowModelNG model) {}); + + /** + * @tc.steps: Test GetMaxMainHeight function + * @tc.expected: step2. Check whether the return value is correct. + */ + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + + float maxMainHeight = info->GetMaxMainHeight(); + int32_t crossIndex = info->items_[0].rbegin()->first; + info->items_[0][crossIndex + 1][0] = std::pair(1.0f, maxMainHeight); + info->itemInfos_.clear(); + info->endPosArray_.clear(); + EXPECT_EQ(info->GetMaxMainHeight(), maxMainHeight + 1.0f); + + /** + * @tc.steps: Test GetCrossIndexForNextItem function + * @tc.expected: step3. Check whether the return value is correct. + */ + info->items_[0][crossIndex + 1][1] = std::pair(0.0f, 0.0f); + FlowItemIndex position = info->GetCrossIndexForNextItem(0); + EXPECT_EQ(position.crossIndex, crossIndex + 1); + EXPECT_EQ(position.lastItemIndex, 1); +} +} // namespace OHOS::Ace::NG -- Gitee From 0a6a8de6eb720a2339012405152e3193f037b4b9 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 23 Apr 2024 11:22:58 +0800 Subject: [PATCH 09/31] fix multiple bugs and add tests Signed-off-by: Tianer Zhou Change-Id: I951f2549640c53469dd98e16dbf2c57b28b8a518 --- .../jsview/js_water_flow.cpp | 2 +- .../water_flow_layout_info_sw.cpp | 74 +++++--- .../water_flow_layout_info_sw.h | 30 +++- .../sliding_window/water_flow_sw_layout.cpp | 160 +++++++++++------- .../sliding_window/water_flow_sw_layout.h | 34 ++-- .../pattern/waterflow/water_flow_pattern.cpp | 2 + .../waterflow/water_flow_sw_layout_test.cpp | 92 ++++++++++ .../pattern/waterflow/water_flow_test_ng.cpp | 25 +-- .../pattern/waterflow/water_flow_test_ng.h | 2 +- .../waterflow/water_flow_top_down_test.cpp | 20 +++ 10 files changed, 321 insertions(+), 120 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp b/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp index bece88496bd..10b286c5d63 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp @@ -147,7 +147,7 @@ void JSWaterFlow::Create(const JSCallbackInfo& args) } JSRef obj = JSRef::Cast(args[0]); - // Set layout mode first because SetFooter is dependent to it + // set layout mode first. SetFooter is dependent to it using LayoutMode = NG::WaterFlowLayoutMode; auto mode = LayoutMode::SLIDING_WINDOW; auto jsMode = obj->GetProperty("layoutMode"); diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index b295231307d..8ec5906e399 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -22,12 +22,10 @@ namespace OHOS::Ace::NG { void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) { + startIndex_ = MinIdxInLanes(); + endIndex_ = MaxIdxInLanes(); for (const auto& lane : lanes_) { - if (lane.items_.empty()) { - continue; - } - startIndex_ = std::min(startIndex_, lane.items_.front().idx); - endIndex_ = std::max(endIndex_, lane.items_.back().idx); + std::cout << "SYNC lane = " << lane.ToString() << std::endl; } delta_ = 0.0f; lastMainSize_ = mainSize; @@ -35,7 +33,8 @@ void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) itemStart_ = startIndex_ == 0 && NonNegative(DistanceToTop(0, mainGap_)); itemEnd_ = endIndex_ == childrenCount_ - 1; - offsetEnd_ = itemEnd_ && NonNegative(DistanceToBottom(endIndex_, mainSize, mainGap)); + offsetEnd_ = itemEnd_ && std::all_of(lanes_.begin(), lanes_.end(), + [mainSize](const Lane& lane) { return LessOrEqual(lane.endPos, mainSize); }); } float WaterFlowLayoutInfoSW::DistanceToTop(int32_t itemIdx, float mainGap) const @@ -61,23 +60,15 @@ float WaterFlowLayoutInfoSW::DistanceToBottom(int32_t itemIdx, float mainSize, f } const auto& lane = lanes_[idxToLane_.at(itemIdx)]; float dist = mainSize - lane.endPos; - for (const auto& item : lane.items_) { - if (item.idx == itemIdx) { + for (auto item = lane.items_.rbegin(); item != lane.items_.rend(); ++item) { + if (item->idx == itemIdx) { break; } - dist += item.mainSize + mainGap; + dist += item->mainSize + mainGap; } return dist; } -float WaterFlowLayoutInfoSW::offset() const -{ - if (lanes_.empty()) { - return 0.0f; - } - return lanes_[0].startPos; -} - bool WaterFlowLayoutInfoSW::OutOfBounds() const { if (lanes_.empty()) { @@ -112,7 +103,7 @@ OverScrollOffset WaterFlowLayoutInfoSW::GetOverScrolledDelta(float delta) const } } - if (endIndex_ < childrenCount_ - 1) { + if (!itemEnd_) { return res; } float disToBot = EndPos() - lastMainSize_; @@ -141,13 +132,13 @@ float WaterFlowLayoutInfoSW::CalcOverScroll(float mainSize, float delta) const return res; } -inline float WaterFlowLayoutInfoSW::EndPos() const +float WaterFlowLayoutInfoSW::EndPos() const { return std::max_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { return LessNotEqual(left.endPos, right.endPos); })->endPos; } -inline float WaterFlowLayoutInfoSW::StartPos() const +float WaterFlowLayoutInfoSW::StartPos() const { return std::min_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { return LessNotEqual(left.startPos, right.startPos); @@ -197,6 +188,9 @@ float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx const auto& lane = lanes_[idxToLane_.at(idx)]; float pos = 0.0f; // main-axis position of the item's top edge relative to viewport top. Positive if below viewport float itemSize = 0.0f; + std::cout << "lane start pos: " << lane.startPos << " lane end pos: " << lane.endPos + << " item count = " << lane.items_.size() << " first item " << lane.items_.front().idx + << " back = " << lane.items_.back().idx << " lane indx = " << idxToLane_.at(idx) << std::endl; if (idx <= endIndex_) { pos = DistanceToTop(idx, mainGap_); auto it = std::find_if( @@ -238,4 +232,44 @@ void WaterFlowLayoutInfoSW::Reset() lanes_.clear(); idxToLane_.clear(); } + +int32_t WaterFlowLayoutInfoSW::MaxIdxInLanes() const +{ + int32_t maxIdx = -1; + for (const auto& lane : lanes_) { + if (lane.items_.empty()) { + continue; + } + maxIdx = std::max(maxIdx, lane.items_.back().idx); + } + return maxIdx; +} + +int32_t WaterFlowLayoutInfoSW::MinIdxInLanes() const +{ + int32_t minIdx = childrenCount_; + for (const auto& lane : lanes_) { + if (lane.items_.empty()) { + continue; + } + minIdx = std::min(minIdx, lane.items_.front().idx); + } + return minIdx; +} + +std::string WaterFlowLayoutInfoSW::Lane::ToString() const +{ + std::string res = "{StartPos: " + std::to_string(startPos) + " EndPos: " + std::to_string(endPos) + " "; + if (items_.empty()) { + res += "empty"; + } else { + res += "Items ["; + for (const auto& item : items_) { + res += std::to_string(item.idx) + " "; + } + res += "] "; + } + res += "}"; + return res; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 7d501ad4566..a95c288af4d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -16,6 +16,7 @@ #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_SW_H #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_SW_H +#include #include #include @@ -36,7 +37,10 @@ public: return WaterFlowLayoutMode::SLIDING_WINDOW; } - float offset() const override; + float offset() const override + { + return totalOffset_; + } int32_t firstIdx() const override { return startIndex_; @@ -71,7 +75,7 @@ public: float GetDelta(float prevPos) const override { - return offset() - prevPos; + return prevPos - totalOffset_; } int32_t GetMainCount() const override; @@ -103,18 +107,28 @@ public: */ float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; + int32_t MaxIdxInLanes() const; + int32_t MinIdxInLanes() const; + + /** + * @return maximum end position of items in lanes_. + */ + float EndPos() const; + /** + * @return minimum start position of items in lanes_. + */ + float StartPos() const; + struct Lane; std::vector lanes_; + // only contains items currently in lane. std::unordered_map idxToLane_; float delta_ = 0.0f; - float mainGap_ = 0.0f; // update this at the end of a layout + float totalOffset_ = 0.0f; // record total offset when continuously scrolling. Reset when jumped + float mainGap_ = 0.0f; // update this at the end of a layout struct ItemInfo; - -private: - inline float EndPos() const; - inline float StartPos() const; }; struct WaterFlowLayoutInfoSW::ItemInfo { @@ -123,6 +137,8 @@ struct WaterFlowLayoutInfoSW::ItemInfo { }; struct WaterFlowLayoutInfoSW::Lane { + std::string ToString() const; + float startPos = 0.0f; float endPos = 0.0f; std::deque items_; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index 14ef828022f..82ccad0edb8 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -30,7 +30,6 @@ namespace OHOS::Ace::NG { void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) { - LOGI("ZTE sw"); wrapper_ = wrapper; auto props = DynamicCast(wrapper->GetLayoutProperty()); axis_ = axis_ = props->GetAxis(); @@ -40,15 +39,15 @@ void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) CheckReset(); if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { - MeasureOnJump(info_->jumpIndex_, info_->align_, mainSize_); - overScroll_ = false; + MeasureOnJump(info_->jumpIndex_, info_->align_); + if (!NearZero(info_->delta_)) { + overScroll_ = false; + MeasureOnOffset(info_->delta_); + } } else if (info_->targetIndex_) { MeasureToTarget(*info_->targetIndex_); - } - LOGI("ZTE delta = %f", info_->delta_); - ApplyOffset(mainSize_, info_->delta_); - if (!overScroll_) { - AdjustOverScroll(mainSize_); + } else { + MeasureOnOffset(info_->delta_); } // if (matchChildren) { // PostMeasureSelf(); @@ -86,10 +85,18 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) crossPos += itemCrossSize_[i] + crossGap_; } wrapper->SetActiveChildRange(info_->startIndex_, info_->endIndex_); + for (int32_t i = info_->startIndex_; i <= info_->endIndex_; ++i) { + if (!info_->idxToLane_.count(i)) { + wrapper->RemoveChildInRenderTree(i); + } + } } void WaterFlowSWLayout::Init(const SizeF& frameSize) { + info_->footerIndex_ = 0; + info_->childrenCount_ = wrapper_->GetTotalChildCount(); + auto props = DynamicCast(wrapper_->GetLayoutProperty()); auto scale = props->GetLayoutConstraint()->scaleProperty; auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0); @@ -116,7 +123,6 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) if (info_->lanes_.empty()) { info_->lanes_.resize(cross.first.size()); } - for (const auto& len : cross.first) { itemCrossSize_.push_back(static_cast(len)); } @@ -136,53 +142,69 @@ void WaterFlowSWLayout::CheckReset() } } -void WaterFlowSWLayout::ApplyOffset(float mainSize, float offset) +void WaterFlowSWLayout::MeasureOnOffset(float delta) { + ApplyDelta(delta); + for (const auto& lane : info_->lanes_) { + std::cout << "after APPLY delta lane = " << lane.ToString() << std::endl; + } + if (!overScroll_) { + AdjustOverScroll(); + } +} + +void WaterFlowSWLayout::ApplyDelta(float delta) +{ + std::cout << "offset = " << delta << std::endl; + info_->totalOffset_ += delta; for (auto& lane : info_->lanes_) { - lane.startPos += offset; - lane.endPos += offset; + lane.startPos += delta; + lane.endPos += delta; } // clear out items outside viewport after position change - if (Positive(offset)) { + if (Positive(delta)) { // positive offset is scrolling upwards - ClearBack(mainSize); - FillFront(0.0f, 0); + FillFront(0.0f, info_->MinIdxInLanes() - 1, 0); + ClearBack(mainSize_); } else { + FillBack(mainSize_, info_->MaxIdxInLanes() + 1, info_->childrenCount_ - 1); ClearFront(); - FillBack(mainSize, info_->childrenCount_ - 1); } } void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) { + std::cout << "measure to target " << targetIdx << " start = " << info_->startIndex_ << " end " << info_->endIndex_ + << std::endl; if (targetIdx < info_->startIndex_) { - FillFront(-FLT_MAX, targetIdx); + FillFront(-FLT_MAX, info_->startIndex_ - 1, targetIdx); } else if (targetIdx > info_->endIndex_) { - FillBack(FLT_MAX, targetIdx); + FillBack(FLT_MAX, info_->endIndex_ + 1, targetIdx); } } +// [lane start/end position, lane index] using lanePos = std::pair; -void WaterFlowSWLayout::FillBack(float viewportBound, int32_t maxChildIdx) +void WaterFlowSWLayout::FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx) { maxChildIdx = std::min(maxChildIdx, info_->childrenCount_ - 1); - std::priority_queue q; + std::priority_queue, std::greater<>> q; for (size_t i = 0; i < info_->lanes_.size(); ++i) { + std::cout << "Lane " << i << " = " << info_->lanes_[i].ToString() << std::endl; float endPos = info_->lanes_[i].endPos; if (LessNotEqual(endPos + mainGap_, viewportBound)) { q.push({ endPos, i }); } } - int32_t idx = info_->endIndex_ + 1; auto props = DynamicCast(wrapper_->GetLayoutProperty()); while (!q.empty() && idx <= maxChildIdx) { auto [endPos, laneIdx] = q.top(); q.pop(); float mainLen = MeasureChild(props, idx, laneIdx); - endPos += mainGap_ + mainLen; + endPos += mainLen + mainGap_; auto& lane = info_->lanes_[laneIdx]; lane.endPos = endPos; @@ -192,13 +214,25 @@ void WaterFlowSWLayout::FillBack(float viewportBound, int32_t maxChildIdx) q.push({ endPos, laneIdx }); } } - LOGI("ZTE deque size = %d", info_->lanes_[0].items_.size()); } -void WaterFlowSWLayout::FillFront(float viewportBound, int32_t minChildIdx) +namespace { +// max heap but with smaller laneIdx at the top +struct MaxHeapCmp { + bool operator()(const lanePos& left, const lanePos& right) + { + if (NearEqual(left.first, right.first)) { + return left.second > right.second; + } + return LessNotEqual(left.first, right.first); + } +}; +} // namespace + +void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minChildIdx) { minChildIdx = std::max(minChildIdx, 0); - std::priority_queue, std::greater<>> q; + std::priority_queue, MaxHeapCmp> q; for (size_t i = 0; i < info_->lanes_.size(); ++i) { float startPos = info_->lanes_[i].startPos; if (GreatNotEqual(startPos - mainGap_, viewportBound)) { @@ -206,8 +240,6 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t minChildIdx) } } - int32_t idx = info_->startIndex_ - 1; - auto props = DynamicCast(wrapper_->GetLayoutProperty()); while (!q.empty() && idx >= minChildIdx) { auto [startPos, laneIdx] = q.top(); @@ -225,15 +257,17 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t minChildIdx) } } -void WaterFlowSWLayout::ClearBack(float mainSize) +void WaterFlowSWLayout::ClearBack(float bound) { for (auto& lane : info_->lanes_) { if (lane.items_.empty()) { continue; } float lastItemStartPos = lane.endPos - lane.items_.back().mainSize; - while (GreatNotEqual(lastItemStartPos, mainSize)) { + while (GreatOrEqual(lastItemStartPos, bound)) { + info_->idxToLane_.erase(lane.items_.back().idx); lane.items_.pop_back(); + lane.endPos = lastItemStartPos - mainGap_; if (lane.items_.empty()) { break; } @@ -249,8 +283,10 @@ void WaterFlowSWLayout::ClearFront() continue; } float firstItemEndPos = lane.startPos + lane.items_.front().mainSize; - while (Negative(firstItemEndPos)) { + while (NonPositive(firstItemEndPos)) { + info_->idxToLane_.erase(lane.items_.front().idx); lane.items_.pop_front(); + lane.startPos = firstItemEndPos + mainGap_; if (lane.items_.empty()) { break; } @@ -259,13 +295,13 @@ void WaterFlowSWLayout::ClearFront() } } -ScrollAlign WaterFlowSWLayout::ParseAutoAlign(int32_t jumpIdx, float mainSize, bool inView) +ScrollAlign WaterFlowSWLayout::ParseAutoAlign(int32_t jumpIdx, bool inView) { if (inView) { if (Negative(info_->DistanceToTop(jumpIdx, mainGap_))) { return ScrollAlign::START; } - if (Negative(info_->DistanceToBottom(jumpIdx, mainSize, mainGap_))) { + if (Negative(info_->DistanceToBottom(jumpIdx, mainSize_, mainGap_))) { return ScrollAlign::END; } // item is already fully in viewport @@ -277,7 +313,7 @@ ScrollAlign WaterFlowSWLayout::ParseAutoAlign(int32_t jumpIdx, float mainSize, b return ScrollAlign::END; } -void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float mainSize) +void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) { if (jumpIdx == -1) { jumpIdx = info_->childrenCount_ - 1; @@ -285,11 +321,12 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float bool inView = jumpIdx >= info_->startIndex_ && jumpIdx <= info_->endIndex_; if (align == ScrollAlign::AUTO) { - align = ParseAutoAlign(jumpIdx, mainSize, inView); + align = ParseAutoAlign(jumpIdx, inView); } // if the item is within 1 full-viewport distance (approximately), we consider it close int32_t cntInView = info_->endIndex_ - info_->startIndex_ + 1; + std::cout << "start " << info_->startIndex_ << " end = " << info_->endIndex_ << std::endl; bool closeToView = jumpIdx > info_->endIndex_ ? jumpIdx - info_->endIndex_ < cntInView : info_->startIndex_ - jumpIdx < cntInView; if (closeToView) { @@ -298,55 +335,57 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float switch (align) { case ScrollAlign::START: { if (inView || closeToView) { - ApplyOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_)); + ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_)); } else { std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [](auto& lane) { lane.items_.clear(); lane.startPos = 0.0f; lane.endPos = 0.0f; }); + info_->totalOffset_ = 0; info_->idxToLane_.clear(); - info_->endIndex_ = jumpIdx - 1; - FillBack(mainSize, info_->childrenCount_ - 1); + FillBack(mainSize_, jumpIdx, info_->childrenCount_ - 1); } break; } case ScrollAlign::CENTER: { auto props = DynamicCast(wrapper_->GetLayoutProperty()); if (inView || closeToView) { - std::cout << "jumpIdx = " - << " map size = " << info_->idxToLane_.size() << " children size = " << info_->childrenCount_ - << std::endl; + std::cout << "jumpIdx = " << jumpIdx << " map size = " << info_->idxToLane_.size() + << " children size = " << info_->childrenCount_ << std::endl; float itemH = MeasureChild(props, jumpIdx, info_->idxToLane_.at(jumpIdx)); - ApplyOffset(mainSize, -info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize - itemH) / 2.0f); + ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize_ - itemH) / 2.0f); } else { float itemH = MeasureChild(props, jumpIdx, 0); - std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize, itemH](auto& lane) { + std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize = mainSize_, itemH](auto& lane) { lane.items_.clear(); lane.startPos = (mainSize - itemH) / 2.0f; lane.endPos = (mainSize + itemH) / 2.0f; }); + info_->idxToLane_ = { { jumpIdx, 0 } }; auto& lane = info_->lanes_[0]; lane.items_.push_back({ jumpIdx, itemH }); - info_->startIndex_ = info_->endIndex_ = jumpIdx; + info_->totalOffset_ = 0; - FillFront(0.0f, 0); - FillBack(mainSize, info_->childrenCount_ - 1); + FillFront(0.0f, jumpIdx - 1, 0); + FillBack(mainSize_, jumpIdx + 1, info_->childrenCount_ - 1); } break; } case ScrollAlign::END: { if (inView || closeToView) { - ApplyOffset(mainSize, info_->DistanceToBottom(jumpIdx, mainSize, mainGap_)); + std::cout << "jumpIdx = " << jumpIdx + << "dis to bot = " << info_->DistanceToBottom(jumpIdx, mainSize_, mainGap_) << std::endl; + ApplyDelta(info_->DistanceToBottom(jumpIdx, mainSize_, mainGap_)); } else { - std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize](auto& lane) { + std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize = mainSize_](auto& lane) { lane.items_.clear(); lane.startPos = mainSize; lane.endPos = mainSize; }); + info_->totalOffset_ = 0; info_->idxToLane_.clear(); - info_->startIndex_ = jumpIdx + 1; - FillFront(0.0f, 0); + FillFront(0.0f, jumpIdx, 0); } break; } @@ -355,20 +394,23 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float } } -void WaterFlowSWLayout::AdjustOverScroll(float mainSize) +void WaterFlowSWLayout::AdjustOverScroll() { if (info_->lanes_.empty()) { return; } - auto minStart = std::min_element(info_->lanes_.begin(), info_->lanes_.end(), - [](const auto& a, const auto& b) { return LessNotEqual(a.startPos, b.startPos); }); - auto maxEnd = std::max_element(info_->lanes_.begin(), info_->lanes_.end(), - [](const auto& a, const auto& b) { return LessNotEqual(a.endPos, b.endPos); }); - - if (Positive(minStart->startPos)) { - ApplyOffset(mainSize, -minStart->startPos); - } else if (LessNotEqual(maxEnd->endPos, mainSize)) { - ApplyOffset(mainSize, mainSize - maxEnd->endPos); + float maxEnd = info_->EndPos(); + float minStart = info_->StartPos(); + + if (Positive(minStart)) { + ApplyDelta(-minStart); + } else if (LessNotEqual(maxEnd, mainSize_)) { + bool reachedTop = info_->MinIdxInLanes() == 0 && NearZero(info_->DistanceToTop(0, mainGap_)); + if (reachedTop) { + // no room to adjust + return; + } + ApplyDelta(mainSize_ - maxEnd); } } diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index 340fb0f2ee5..9518d204735 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -39,7 +39,9 @@ public: private: void Init(const SizeF& frameSize); - void ApplyOffset(float mainSize, float offset); + void MeasureOnOffset(float delta); + + void ApplyDelta(float delta); void MeasureToTarget(int32_t targetIdx); @@ -51,30 +53,40 @@ private: * @param align ScrollAlign * @param mainSize of the viewport */ - void MeasureOnJump(int32_t jumpIdx, ScrollAlign align, float mainSize); + void MeasureOnJump(int32_t jumpIdx, ScrollAlign align); - ScrollAlign ParseAutoAlign(int32_t jumpIdx, float mainSize, bool inView); + ScrollAlign ParseAutoAlign(int32_t jumpIdx, bool inView); /** - * @brief fills the viewport backward until either condition is not satisfied. + * @brief fills the viewport backward until [viewportBound] is reached / idx < minChildIdx. * - * @param viewportBound + * @param viewportBound boundary to fill towards. + * @param idx first item index to fill with. * @param minChildIdx smallest item index to fill before stopping. */ - void FillFront(float viewportBound, int32_t minChildIdx); + void FillFront(float viewportBound, int32_t idx, int32_t minChildIdx); /** - * @brief fills the viewport forward until either condition is not satisfied. + * @brief fills the viewport forward until [viewportBound] is reached / idx > maxChildIdx. * - * @param viewportBound + * @param viewportBound boundary to fill towards. + * @param idx first item index to fill with. * @param maxChildIdx greatest item index to fill before stopping. */ - void FillBack(float viewportBound, int32_t maxChildIdx); + void FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx); + /** + * @brief Clear items above the viewport. + */ void ClearFront(); - void ClearBack(float mainSize); + /** + * @brief Clear items below the viewport. + * + * @param bound of the viewport + */ + void ClearBack(float bound); - void AdjustOverScroll(float mainSize); + void AdjustOverScroll(); float MeasureChild(const RefPtr& props, int32_t idx, size_t lane); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index d74910b68df..48601d2fe66 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -243,6 +243,8 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir } layoutInfo_->UpdateStartIndex(); prevOffset_ = layoutInfo_->offset(); + layoutInfo_->jumpIndex_ = EMPTY_JUMP_INDEX; + layoutInfo_->targetIndex_.reset(); UpdateScrollBarOffset(); CheckScrollable(); diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index e69de29bb2d..c39957d356a 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -0,0 +1,92 @@ +/* + * 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. + */ +#include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" + +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" +namespace OHOS::Ace::NG { +class WaterFlowSWTest : public WaterFlowTestNg { +protected: + void GetInstance() override + { + WaterFlowTestNg::GetInstance(); + info_ = AceType::DynamicCast(pattern_->layoutInfo_); + EXPECT_TRUE(info_); + } + + RefPtr info_; +}; + +/** + * @tc.name: Regular001 + * @tc.desc: waterFlow with fixed column + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, Regular001, TestSize.Level1) +{ + CreateWithItem([](WaterFlowModelNG model) { + ViewAbstract::SetWidth(CalcLength(400.0f)); + ViewAbstract::SetHeight(CalcLength(200.f)); + model.SetColumnsTemplate("1fr 1fr 1fr"); + }); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->lanes_[0].items_.size(), 2); + EXPECT_EQ(info_->lanes_[0].items_.back().idx, 3); + EXPECT_EQ(info_->lanes_[0].items_.back().mainSize, 200.0f); + EXPECT_EQ(info_->lanes_[0].endPos, 300.0f); + EXPECT_EQ(info_->lanes_[1].items_.back().idx, 1); + EXPECT_EQ(info_->lanes_[1].endPos, 200.0f); + EXPECT_EQ(info_->lanes_[2].endPos, 200.0f); + EXPECT_EQ(info_->lanes_[2].items_.back().idx, 4); + EXPECT_EQ(info_->startIndex_, 0); + EXPECT_EQ(info_->endIndex_, 4); +} + +/** + * @tc.name: Jump001 + * @tc.desc: waterFlow jump + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, Jump001, TestSize.Level1) +{ + CreateWithItem([](WaterFlowModelNG model) { + ViewAbstract::SetWidth(CalcLength(400.0f)); + ViewAbstract::SetHeight(CalcLength(200.f)); + model.SetColumnsTemplate("1fr 1fr 1fr"); + }); + pattern_->ScrollToIndex(8); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->startIndex_, 5); + EXPECT_EQ(info_->endIndex_, 9); + for (auto& lane : info_->lanes_) { + std::cout << "Lane = " << lane.ToString() << std::endl; + } + EXPECT_EQ(info_->idxToLane_.at(8), 2); + EXPECT_EQ(info_->lanes_[0].endPos, 200.0f); + EXPECT_EQ(info_->lanes_[1].startPos, -100.0f); + EXPECT_EQ(info_->lanes_[1].endPos, 300.0f); + EXPECT_EQ(info_->lanes_[2].endPos, 100.0f); + EXPECT_EQ(info_->lanes_[0].items_.size(), 1); + EXPECT_EQ(info_->lanes_[0].items_.front().idx, 7); + EXPECT_EQ(info_->lanes_[1].items_.size(), 2); + EXPECT_EQ(info_->lanes_[1].items_.front().idx, 5); + EXPECT_EQ(info_->lanes_[1].items_.back().idx, 9); + EXPECT_EQ(info_->lanes_[2].items_.size(), 1); + EXPECT_TRUE(info_->itemEnd_); + EXPECT_FALSE(info_->offsetEnd_); + // children in viewport are not always continuous + EXPECT_TRUE(GetChildFrameNode(frameNode_, 5)->IsActive()); + EXPECT_FALSE(GetChildFrameNode(frameNode_, 6)->IsActive()); +} +} // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 9b54a2323a6..528cec79ede 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -24,6 +24,7 @@ #include "base/utils/system_properties.h" #include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/property/property.h" #define protected public #define private public @@ -65,7 +66,6 @@ #undef protected namespace OHOS::Ace::NG { -namespace {} // namespace void WaterFlowTestNg::SetUpTestSuite() { @@ -571,6 +571,9 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest003, TestSize.Level1) model.SetColumnsTemplate("1fr 1fr 1fr"); }); FlushLayoutTask(frameNode_); + auto& info = pattern_->layoutInfo_; + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 4); EXPECT_TRUE(GetChildFrameNode(frameNode_, 3)->IsActive()); EXPECT_TRUE(GetChildFrameNode(frameNode_, 4)->IsActive()); EXPECT_FALSE(GetChildFrameNode(frameNode_, 5)->IsActive()); @@ -636,26 +639,6 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest006, TestSize.Level1) EXPECT_FALSE(GetChildFrameNode(frameNode_, 7)->IsActive()); } -/** - * @tc.name: WaterFlowTest007 - * @tc.desc: waterFlow with fixed column, scroll to index not fully showed at last line - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, WaterFlowTest007, TestSize.Level1) -{ - CreateWithItem([](WaterFlowModelNG model) { - ViewAbstract::SetWidth(CalcLength(WATERFLOW_WIDTH)); - ViewAbstract::SetHeight(CalcLength(200.f)); - model.SetColumnsTemplate("1fr 1fr 1fr"); - }); - pattern_->UpdateStartIndex(8); - FlushLayoutTask(frameNode_); - EXPECT_FALSE(GetChildFrameNode(frameNode_, 3)->IsActive()); - EXPECT_FALSE(GetChildFrameNode(frameNode_, 4)->IsActive()); - EXPECT_TRUE(GetChildFrameNode(frameNode_, 5)->IsActive()); - EXPECT_TRUE(GetChildFrameNode(frameNode_, 6)->IsActive()); -} - /** * @tc.name: WaterFlowTest008 * @tc.desc: waterFlow with fixed column, scroll to index not fully showed at first line diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.h b/test/unittest/core/pattern/waterflow/water_flow_test_ng.h index 7302e92a0f6..a9d7a62f8a6 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.h +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.h @@ -52,7 +52,7 @@ protected: static void TearDownTestSuite(); void SetUp() override; void TearDown() override; - void GetInstance(); + virtual void GetInstance(); void Create(const std::function& callback = nullptr, bool flushLayout = true); void CreateWithItem(const std::function& callback = nullptr); diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index fca8e5465e7..5267409c9ff 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -137,4 +137,24 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest005, TestSize.Level1) EXPECT_EQ(position.crossIndex, crossIndex + 1); EXPECT_EQ(position.lastItemIndex, 1); } + +/** + * @tc.name: WaterFlowTest007 + * @tc.desc: waterFlow with fixed column, scroll to index not fully showed at last line + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, WaterFlowTest007, TestSize.Level1) +{ + CreateWithItem([](WaterFlowModelNG model) { + ViewAbstract::SetWidth(CalcLength(WATERFLOW_WIDTH)); + ViewAbstract::SetHeight(CalcLength(200.f)); + model.SetColumnsTemplate("1fr 1fr 1fr"); + }); + pattern_->UpdateStartIndex(8); + FlushLayoutTask(frameNode_); + EXPECT_FALSE(GetChildFrameNode(frameNode_, 3)->IsActive()); + EXPECT_FALSE(GetChildFrameNode(frameNode_, 4)->IsActive()); + EXPECT_TRUE(GetChildFrameNode(frameNode_, 5)->IsActive()); + EXPECT_TRUE(GetChildFrameNode(frameNode_, 6)->IsActive()); +} } // namespace OHOS::Ace::NG -- Gitee From 381c854e4de7b942ee9261f91f74d8fcf979750f Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 23 Apr 2024 15:43:55 +0800 Subject: [PATCH 10/31] cache lane idx Signed-off-by: Tianer Zhou Change-Id: I889dcb6c1e692e231a88387c8cffc6a9f19914ce --- .../water_flow_layout_info_sw.cpp | 42 ++++- .../water_flow_layout_info_sw.h | 26 ++- .../sliding_window/water_flow_sw_layout.cpp | 178 +++++++++++------- .../sliding_window/water_flow_sw_layout.h | 14 +- .../waterflow/water_flow_sw_layout_test.cpp | 7 +- .../waterflow/water_flow_top_down_test.cpp | 35 ++++ 6 files changed, 208 insertions(+), 94 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 8ec5906e399..3526205410d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -22,8 +22,8 @@ namespace OHOS::Ace::NG { void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) { - startIndex_ = MinIdxInLanes(); - endIndex_ = MaxIdxInLanes(); + startIndex_ = StartIndex(); + endIndex_ = EndIndex(); for (const auto& lane : lanes_) { std::cout << "SYNC lane = " << lane.ToString() << std::endl; } @@ -35,11 +35,12 @@ void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) itemEnd_ = endIndex_ == childrenCount_ - 1; offsetEnd_ = itemEnd_ && std::all_of(lanes_.begin(), lanes_.end(), [mainSize](const Lane& lane) { return LessOrEqual(lane.endPos, mainSize); }); + synced_ = true; } float WaterFlowLayoutInfoSW::DistanceToTop(int32_t itemIdx, float mainGap) const { - if (!idxToLane_.count(itemIdx)) { + if (!ItemInView(itemIdx)) { return 0.0f; } const auto& lane = lanes_[idxToLane_.at(itemIdx)]; @@ -55,7 +56,7 @@ float WaterFlowLayoutInfoSW::DistanceToTop(int32_t itemIdx, float mainGap) const float WaterFlowLayoutInfoSW::DistanceToBottom(int32_t itemIdx, float mainSize, float mainGap) const { - if (!idxToLane_.count(itemIdx)) { + if (!ItemInView(itemIdx)) { return 0.0f; } const auto& lane = lanes_[idxToLane_.at(itemIdx)]; @@ -126,7 +127,7 @@ float WaterFlowLayoutInfoSW::CalcOverScroll(float mainSize, float delta) const if (itemStart_) { res = lanes_[0].startPos + delta; } - if (itemEnd_) { + if (offsetEnd_) { res = mainSize - (EndPos() + delta); } return res; @@ -182,7 +183,7 @@ int32_t WaterFlowLayoutInfoSW::GetMainCount() const float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx */) const { - if (!idxToLane_.count(idx)) { + if (!ItemInView(idx)) { return Infinity(); } const auto& lane = lanes_[idxToLane_.at(idx)]; @@ -233,8 +234,11 @@ void WaterFlowLayoutInfoSW::Reset() idxToLane_.clear(); } -int32_t WaterFlowLayoutInfoSW::MaxIdxInLanes() const +int32_t WaterFlowLayoutInfoSW::EndIndex() const { + if (synced_) { + return endIndex_; + } int32_t maxIdx = -1; for (const auto& lane : lanes_) { if (lane.items_.empty()) { @@ -245,8 +249,11 @@ int32_t WaterFlowLayoutInfoSW::MaxIdxInLanes() const return maxIdx; } -int32_t WaterFlowLayoutInfoSW::MinIdxInLanes() const +int32_t WaterFlowLayoutInfoSW::StartIndex() const { + if (synced_) { + return startIndex_; + } int32_t minIdx = childrenCount_; for (const auto& lane : lanes_) { if (lane.items_.empty()) { @@ -257,6 +264,25 @@ int32_t WaterFlowLayoutInfoSW::MinIdxInLanes() const return minIdx; } +int32_t WaterFlowLayoutInfoSW::GetCrossIndex(int32_t itemIndex) const +{ + if (ItemInView(itemIndex)) { + return static_cast(idxToLane_.at(itemIndex)); + } + return -1; +} + +void WaterFlowLayoutInfoSW::ResetBeforeJump(float laneBasePos) +{ + std::for_each(lanes_.begin(), lanes_.end(), [&laneBasePos](auto& lane) { + lane.items_.clear(); + lane.startPos = laneBasePos; + lane.endPos = laneBasePos; + }); + totalOffset_ = 0; + idxToLane_.clear(); +} + std::string WaterFlowLayoutInfoSW::Lane::ToString() const { std::string res = "{StartPos: " + std::to_string(startPos) + " EndPos: " + std::to_string(endPos) + " "; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index a95c288af4d..846532d436c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -51,13 +51,7 @@ public: delta_ = delta; } - int32_t GetCrossIndex(int32_t itemIndex) const override - { - if (idxToLane_.count(itemIndex)) { - return static_cast(idxToLane_.at(itemIndex)); - } - return -1; - } + int32_t GetCrossIndex(int32_t itemIndex) const override; OverScrollOffset GetOverScrolledDelta(float delta) const override; @@ -86,6 +80,13 @@ public: void Reset() override; + /** + * @brief reset layout data before performing a jump. + * + * @param laneBasePos base value for lane's start&end position. + */ + void ResetBeforeJump(float laneBasePos); + void Sync(float mainSize, float mainGap); /** @@ -107,8 +108,12 @@ public: */ float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; - int32_t MaxIdxInLanes() const; - int32_t MinIdxInLanes() const; + int32_t StartIndex() const; + int32_t EndIndex() const; + inline bool ItemInView(int32_t idx) const + { + return !lanes_.empty() && idx >= StartIndex() && idx <= EndIndex(); + } /** * @return maximum end position of items in lanes_. @@ -121,12 +126,13 @@ public: struct Lane; std::vector lanes_; - // only contains items currently in lane. + // mapping of all items previously or currently in lanes_. std::unordered_map idxToLane_; float delta_ = 0.0f; float totalOffset_ = 0.0f; // record total offset when continuously scrolling. Reset when jumped float mainGap_ = 0.0f; // update this at the end of a layout + bool synced_ = false; struct ItemInfo; }; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index 82ccad0edb8..f2220f589a5 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -30,18 +30,19 @@ namespace OHOS::Ace::NG { void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) { + info_->synced_ = false; wrapper_ = wrapper; auto props = DynamicCast(wrapper->GetLayoutProperty()); - axis_ = axis_ = props->GetAxis(); + info_->axis_ = axis_ = props->GetAxis(); auto [idealSize, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_); Init(idealSize); CheckReset(); if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { + overScroll_ = false; MeasureOnJump(info_->jumpIndex_, info_->align_); if (!NearZero(info_->delta_)) { - overScroll_ = false; MeasureOnOffset(info_->delta_); } } else if (info_->targetIndex_) { @@ -63,6 +64,8 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) return; } + auto padding = wrapper_->GetLayoutProperty()->CreatePaddingAndBorder(); + OffsetF paddingOffset { padding.top.value_or(0.0f), padding.top.value_or(0.0f) }; float crossPos = 0.0f; for (size_t i = 0; i < info_->lanes_.size(); ++i) { auto& lane = info_->lanes_[i]; @@ -73,23 +76,23 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) continue; } auto childNode = child->GetGeometryNode(); - childNode->SetMarginFrameOffset( - info_->axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }); + std::cout << "layout item " << item.idx << " with height " << childNode->GetFrameSize().Height() << " at mainPos " + << mainPos << " crossPos = " << crossPos << std::endl; + auto offset = + info_->axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }; + childNode->SetMarginFrameOffset(offset + paddingOffset); + if (child->CheckNeedForceMeasureAndLayout()) { child->Layout(); } else { child->GetHostNode()->ForceSyncGeometryNode(); } - mainPos += childNode->GetMarginFrameSize().MainSize(info_->axis_) + mainGap_; + mainPos += item.mainSize + mainGap_; } crossPos += itemCrossSize_[i] + crossGap_; } + wrapper->SetActiveChildRange(info_->startIndex_, info_->endIndex_); - for (int32_t i = info_->startIndex_; i <= info_->endIndex_; ++i) { - if (!info_->idxToLane_.count(i)) { - wrapper->RemoveChildInRenderTree(i); - } - } } void WaterFlowSWLayout::Init(const SizeF& frameSize) @@ -135,8 +138,12 @@ void WaterFlowSWLayout::CheckReset() { int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated(); if (updateIdx != -1) { - if (updateIdx <= info_->endIndex_ && updateIdx >= info_->startIndex_) { + if (info_->ItemInView(updateIdx)) { + info_->align_ = ScrollAlign::START; info_->jumpIndex_ = info_->startIndex_; + } else { + // this can disable RecoverBack / RecoverFront when the updated item is encountered + info_->idxToLane_.erase(updateIdx); } wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1); } @@ -151,6 +158,12 @@ void WaterFlowSWLayout::MeasureOnOffset(float delta) if (!overScroll_) { AdjustOverScroll(); } + // clear out items outside viewport after position change + if (Positive(delta)) { + ClearBack(mainSize_); + } else { + ClearFront(); + } } void WaterFlowSWLayout::ApplyDelta(float delta) @@ -162,14 +175,11 @@ void WaterFlowSWLayout::ApplyDelta(float delta) lane.endPos += delta; } - // clear out items outside viewport after position change if (Positive(delta)) { // positive offset is scrolling upwards - FillFront(0.0f, info_->MinIdxInLanes() - 1, 0); - ClearBack(mainSize_); + FillFront(0.0f, info_->StartIndex() - 1, 0); } else { - FillBack(mainSize_, info_->MaxIdxInLanes() + 1, info_->childrenCount_ - 1); - ClearFront(); + FillBack(mainSize_, info_->EndIndex() + 1, info_->childrenCount_ - 1); } } @@ -184,12 +194,37 @@ void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) } } +void WaterFlowSWLayout::RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx) +{ + std::unordered_set lanes; + for (size_t i = 0; i < info_->lanes_.size(); ++i) { + if (LessNotEqual(info_->lanes_[i].endPos + mainGap_, viewportBound)) { + lanes.insert(i); + } + } + + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + while (!lanes.empty() && idx <= maxChildIdx && info_->idxToLane_.count(idx)) { + size_t laneIdx = info_->idxToLane_.at(idx); + float mainLen = MeasureChild(props, idx, laneIdx); + auto& lane = info_->lanes_[laneIdx]; + lane.endPos += mainLen + mainGap_; + lane.items_.push_front({ idx++, mainLen }); + if (GreatOrEqual(lane.endPos, viewportBound)) { + lanes.erase(laneIdx); + } + } +} + // [lane start/end position, lane index] using lanePos = std::pair; void WaterFlowSWLayout::FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx) { maxChildIdx = std::min(maxChildIdx, info_->childrenCount_ - 1); + if (info_->idxToLane_.count(idx)) { + RecoverBack(viewportBound, idx, maxChildIdx); + } std::priority_queue, std::greater<>> q; for (size_t i = 0; i < info_->lanes_.size(); ++i) { std::cout << "Lane " << i << " = " << info_->lanes_[i].ToString() << std::endl; @@ -216,6 +251,28 @@ void WaterFlowSWLayout::FillBack(float viewportBound, int32_t idx, int32_t maxCh } } +void WaterFlowSWLayout::RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx) +{ + std::unordered_set lanes; + for (size_t i = 0; i < info_->lanes_.size(); ++i) { + float startPos = info_->lanes_[i].startPos; + if (GreatNotEqual(startPos - mainGap_, viewportBound)) { + lanes.insert(i); + } + } + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + while (!lanes.empty() && idx >= minChildIdx && info_->idxToLane_.count(idx)) { + size_t laneIdx = info_->idxToLane_.at(idx); + float mainLen = MeasureChild(props, idx, laneIdx); + auto& lane = info_->lanes_[laneIdx]; + lane.startPos -= mainLen + mainGap_; + lane.items_.push_front({ idx--, mainLen }); + if (LessOrEqual(lane.startPos, viewportBound)) { + lanes.erase(laneIdx); + } + } +} + namespace { // max heap but with smaller laneIdx at the top struct MaxHeapCmp { @@ -232,6 +289,9 @@ struct MaxHeapCmp { void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minChildIdx) { minChildIdx = std::max(minChildIdx, 0); + if (info_->idxToLane_.count(idx)) { + RecoverFront(viewportBound, idx, minChildIdx); + } std::priority_queue, MaxHeapCmp> q; for (size_t i = 0; i < info_->lanes_.size(); ++i) { float startPos = info_->lanes_[i].startPos; @@ -259,39 +319,35 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minC void WaterFlowSWLayout::ClearBack(float bound) { - for (auto& lane : info_->lanes_) { - if (lane.items_.empty()) { - continue; + for (int32_t i = info_->EndIndex(); i <= info_->StartIndex(); ++i) { + size_t laneIdx = info_->idxToLane_.at(i); + auto& lane = info_->lanes_[laneIdx]; + if (lane.items_.back().idx != i) { + std::abort(); } - float lastItemStartPos = lane.endPos - lane.items_.back().mainSize; - while (GreatOrEqual(lastItemStartPos, bound)) { - info_->idxToLane_.erase(lane.items_.back().idx); - lane.items_.pop_back(); - lane.endPos = lastItemStartPos - mainGap_; - if (lane.items_.empty()) { - break; - } - lastItemStartPos -= mainGap_ + lane.items_.back().mainSize; + float itemStartPos = lane.endPos - lane.items_.back().mainSize; + if (LessNotEqual(itemStartPos, bound)) { + break; } + lane.items_.pop_back(); + lane.endPos = itemStartPos - mainGap_; } } void WaterFlowSWLayout::ClearFront() { - for (auto& lane : info_->lanes_) { - if (lane.items_.empty()) { - continue; + for (int32_t i = info_->StartIndex(); i <= info_->EndIndex(); ++i) { + size_t laneIdx = info_->idxToLane_.at(i); + auto& lane = info_->lanes_[laneIdx]; + if (lane.items_.front().idx != i) { + std::abort(); } - float firstItemEndPos = lane.startPos + lane.items_.front().mainSize; - while (NonPositive(firstItemEndPos)) { - info_->idxToLane_.erase(lane.items_.front().idx); - lane.items_.pop_front(); - lane.startPos = firstItemEndPos + mainGap_; - if (lane.items_.empty()) { - break; - } - firstItemEndPos += mainGap_ + lane.items_.front().mainSize; + float itemEndPos = lane.startPos + lane.items_.front().mainSize; + if (Positive(itemEndPos)) { + break; } + lane.items_.pop_front(); + lane.startPos = itemEndPos + mainGap_; } } @@ -319,12 +375,13 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) jumpIdx = info_->childrenCount_ - 1; } - bool inView = jumpIdx >= info_->startIndex_ && jumpIdx <= info_->endIndex_; + bool inView = info_->ItemInView(jumpIdx); if (align == ScrollAlign::AUTO) { align = ParseAutoAlign(jumpIdx, inView); } - // if the item is within 1 full-viewport distance (approximately), we consider it close + // If the item is within 1 full-viewport distance (approximately), we consider it close. + // Then we simply scroll to it instead of triggering a reset/jump, which would change the layout. int32_t cntInView = info_->endIndex_ - info_->startIndex_ + 1; std::cout << "start " << info_->startIndex_ << " end = " << info_->endIndex_ << std::endl; bool closeToView = @@ -335,15 +392,9 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) switch (align) { case ScrollAlign::START: { if (inView || closeToView) { - ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_)); + MeasureOnOffset(-info_->DistanceToTop(jumpIdx, mainGap_)); } else { - std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [](auto& lane) { - lane.items_.clear(); - lane.startPos = 0.0f; - lane.endPos = 0.0f; - }); - info_->totalOffset_ = 0; - info_->idxToLane_.clear(); + info_->ResetBeforeJump(0.0f); FillBack(mainSize_, jumpIdx, info_->childrenCount_ - 1); } break; @@ -354,18 +405,15 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) std::cout << "jumpIdx = " << jumpIdx << " map size = " << info_->idxToLane_.size() << " children size = " << info_->childrenCount_ << std::endl; float itemH = MeasureChild(props, jumpIdx, info_->idxToLane_.at(jumpIdx)); - ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize_ - itemH) / 2.0f); + MeasureOnOffset(-info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize_ - itemH) / 2.0f); } else { - float itemH = MeasureChild(props, jumpIdx, 0); - std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize = mainSize_, itemH](auto& lane) { - lane.items_.clear(); - lane.startPos = (mainSize - itemH) / 2.0f; - lane.endPos = (mainSize + itemH) / 2.0f; - }); + info_->ResetBeforeJump(mainSize_ / 2.0f); info_->idxToLane_ = { { jumpIdx, 0 } }; auto& lane = info_->lanes_[0]; + float itemH = MeasureChild(props, jumpIdx, 0); + lane.startPos = (mainSize_ - itemH) / 2.0f; + lane.endPos = (mainSize_ + itemH) / 2.0f; lane.items_.push_back({ jumpIdx, itemH }); - info_->totalOffset_ = 0; FillFront(0.0f, jumpIdx - 1, 0); FillBack(mainSize_, jumpIdx + 1, info_->childrenCount_ - 1); @@ -374,17 +422,9 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) } case ScrollAlign::END: { if (inView || closeToView) { - std::cout << "jumpIdx = " << jumpIdx - << "dis to bot = " << info_->DistanceToBottom(jumpIdx, mainSize_, mainGap_) << std::endl; - ApplyDelta(info_->DistanceToBottom(jumpIdx, mainSize_, mainGap_)); + MeasureOnOffset(info_->DistanceToBottom(jumpIdx, mainSize_, mainGap_)); } else { - std::for_each(info_->lanes_.begin(), info_->lanes_.end(), [mainSize = mainSize_](auto& lane) { - lane.items_.clear(); - lane.startPos = mainSize; - lane.endPos = mainSize; - }); - info_->totalOffset_ = 0; - info_->idxToLane_.clear(); + info_->ResetBeforeJump(mainSize_); FillFront(0.0f, jumpIdx, 0); } break; @@ -405,7 +445,7 @@ void WaterFlowSWLayout::AdjustOverScroll() if (Positive(minStart)) { ApplyDelta(-minStart); } else if (LessNotEqual(maxEnd, mainSize_)) { - bool reachedTop = info_->MinIdxInLanes() == 0 && NearZero(info_->DistanceToTop(0, mainGap_)); + bool reachedTop = info_->StartIndex() == 0 && NearZero(info_->DistanceToTop(0, mainGap_)); if (reachedTop) { // no room to adjust return; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index 9518d204735..34ef80e42e6 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -65,6 +65,15 @@ private: * @param minChildIdx smallest item index to fill before stopping. */ void FillFront(float viewportBound, int32_t idx, int32_t minChildIdx); + /** + * @brief fills the viewport backward with cached idx -> lane mapping. + */ + void RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx); + /** + * @brief Clear items above the viewport. + * Iterate by index to keep item range continuous. + */ + void ClearFront(); /** * @brief fills the viewport forward until [viewportBound] is reached / idx > maxChildIdx. @@ -74,11 +83,10 @@ private: * @param maxChildIdx greatest item index to fill before stopping. */ void FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx); - /** - * @brief Clear items above the viewport. + * @brief fills the viewport backward with cached idx -> lane mapping. */ - void ClearFront(); + void RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx); /** * @brief Clear items below the viewport. * diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index c39957d356a..341c66be228 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -82,11 +82,10 @@ HWTEST_F(WaterFlowSWTest, Jump001, TestSize.Level1) EXPECT_EQ(info_->lanes_[1].items_.size(), 2); EXPECT_EQ(info_->lanes_[1].items_.front().idx, 5); EXPECT_EQ(info_->lanes_[1].items_.back().idx, 9); - EXPECT_EQ(info_->lanes_[2].items_.size(), 1); + EXPECT_EQ(info_->lanes_[2].items_.size(), 2); EXPECT_TRUE(info_->itemEnd_); EXPECT_FALSE(info_->offsetEnd_); - // children in viewport are not always continuous - EXPECT_TRUE(GetChildFrameNode(frameNode_, 5)->IsActive()); - EXPECT_FALSE(GetChildFrameNode(frameNode_, 6)->IsActive()); + EXPECT_EQ(info_->startIndex_, 5); + EXPECT_EQ(info_->endIndex_, 9); } } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index 5267409c9ff..5d88be0f642 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -157,4 +157,39 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest007, TestSize.Level1) EXPECT_TRUE(GetChildFrameNode(frameNode_, 5)->IsActive()); EXPECT_TRUE(GetChildFrameNode(frameNode_, 6)->IsActive()); } + +/** + * @tc.name: UpdateCurrentOffset003 + * @tc.desc: Test the firstIndex and endIndex after UpdateCurrentOffset + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, UpdateCurrentOffset003, TestSize.Level1) +{ + /** + * @tc.steps: step1. create waterFlow + * @tc.steps: step2. scroll up to a remote position + * @tc.expected: startIndex_ = 0 endIndex_ = 0. + */ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetEdgeEffect(EdgeEffect::SPRING, true); + CreateItem(TOTAL_LINE_NUMBER * 2); + }); + pattern_->SetAnimateCanOverScroll(true); + pattern_->UpdateCurrentOffset(10000, SCROLL_FROM_UPDATE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->firstIdx(), 0); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 0); + + /** + * @tc.steps: step1. create waterFlow + * @tc.steps: step2. scroll down to a remote position + * @tc.expected: startIndex_ = TOTAL_LINE_NUMBER * 2 - 1, endIndex_ = TOTAL_LINE_NUMBER * 2 - 1. + */ + pattern_->SetAnimateCanOverScroll(true); + pattern_->UpdateCurrentOffset(-99999, SCROLL_FROM_UPDATE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->firstIdx(), 19); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 19); +} } // namespace OHOS::Ace::NG -- Gitee From 59f122eebd04ce0dfd60824180524f2ae6ddec35 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 23 Apr 2024 16:52:14 +0800 Subject: [PATCH 11/31] resolve conflicts Signed-off-by: Tianer Zhou Change-Id: Ieacc979a79e77ca706307d1fac6c197cdfa071e6 --- .../waterflow/water_flow_layout_info.cpp | 44 +++++++++----- .../waterflow/water_flow_layout_info.h | 1 + .../pattern/waterflow/water_flow_pattern.cpp | 3 +- .../water_flow_segment_layout_test.cpp | 60 ++++++++++++++++--- 4 files changed, 83 insertions(+), 25 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 5ae5ab47d61..7d72543bd5e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -542,41 +542,53 @@ void WaterFlowLayoutInfo::PrintWaterFlowItems() const } } -void WaterFlowLayoutInfo::UpdateOffset(float delta) -{ - prevOffset_ = currentOffset_; - currentOffset_ += delta; -} - -float WaterFlowLayoutInfo::CalcTargetPosition(int32_t idx, int32_t crossIdx) const +float WaterFlowLayoutInfo::JumpToTargetAlign(const std::pair& item) const { - auto item = items_[GetSegment(idx)].at(crossIdx).at(idx); - float res = 0.0f; + float targetPosition = 0.0f; ScrollAlign align = align_; switch (align) { case ScrollAlign::START: - res = item.first; + targetPosition = -item.first; break; case ScrollAlign::END: - res = -(lastMainSize_ - (item.first + item.second)); + targetPosition = lastMainSize_ - (item.first + item.second); break; case ScrollAlign::AUTO: if (currentOffset_ + item.first < 0) { - res = item.first; + targetPosition = -item.first; } else if (currentOffset_ + item.first + item.second > lastMainSize_) { - res = -(lastMainSize_ - (item.first + item.second)); + targetPosition = lastMainSize_ - (item.first + item.second); } else { - res = -currentOffset_; + targetPosition = currentOffset_; } break; case ScrollAlign::CENTER: - res = -(-item.first + (lastMainSize_ - item.second) / 2); + targetPosition = -item.first + (lastMainSize_ - item.second) * HALF; break; default: break; } - return res; + return targetPosition; +} + +void WaterFlowLayoutInfo::JumpTo(const std::pair& item) +{ + currentOffset_ = JumpToTargetAlign(item); + align_ = ScrollAlign::START; + jumpIndex_ = EMPTY_JUMP_INDEX; } + +void WaterFlowLayoutInfo::UpdateOffset(float delta) +{ + prevOffset_ = currentOffset_; + currentOffset_ += delta; +} + +float WaterFlowLayoutInfo::CalcTargetPosition(int32_t idx, int32_t crossIdx) const +{ + return -JumpToTargetAlign(items_[GetSegment(idx)].at(crossIdx).at(idx)); +} + bool WaterFlowLayoutInfo::OutOfBounds() const { bool outOfStart = itemStart_ && Positive(currentOffset_); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index 036979326a0..b516c46ebdc 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -97,6 +97,7 @@ public: return prevPos - currentOffset_; } + float JumpToTargetAlign(const std::pair& item) const; void JumpTo(const std::pair& item); /** diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 48601d2fe66..4dc8d237451 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -265,8 +265,7 @@ bool WaterFlowPattern::ScrollToTargetIndex(int32_t index) if (crossIndex == -1) { return false; } - auto item = layoutInfo_->items_[layoutInfo_->GetSegment(index)].at(crossIndex).at(index); - float targetPosition = -layoutInfo_->JumpToTargetAlign(item); + float targetPosition = layoutInfo_->CalcTargetPosition(index, crossIndex); ScrollablePattern::AnimateTo(targetPosition, -1, nullptr, true); return true; } diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp index f8999f111df..7e1c146a6ff 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -672,7 +672,7 @@ HWTEST_F(WaterFlowSegmentTest, Reset002, TestSize.Level1) EXPECT_EQ(info->currentOffset_, -857.0f); // child requires fresh layout, should jump back to index 75 - layoutProperty_->propertyChangeFlag_ = PROPERTY_UPDATE_BY_CHILD_REQUEST; + layoutProperty_->UpdatePropertyChangeFlag(PROPERTY_UPDATE_BY_CHILD_REQUEST); frameNode_->ChildrenUpdatedFrom(0); algo->Measure(AceType::RawPtr(frameNode_)); EXPECT_EQ(info->startIndex_, 25); @@ -685,7 +685,7 @@ HWTEST_F(WaterFlowSegmentTest, Reset002, TestSize.Level1) EXPECT_EQ(info->itemInfos_.size(), 58); info->Reset(); - layoutProperty_->propertyChangeFlag_ = PROPERTY_UPDATE_BY_CHILD_REQUEST; + layoutProperty_->UpdatePropertyChangeFlag(PROPERTY_UPDATE_BY_CHILD_REQUEST); algo->Measure(AceType::RawPtr(frameNode_)); EXPECT_EQ(info->startIndex_, 25); EXPECT_EQ(info->endIndex_, 57); @@ -1832,8 +1832,8 @@ HWTEST_F(WaterFlowSegmentTest, Constraint001, TestSize.Level1) FlushLayoutTask(frameNode_); auto& info = pattern_->layoutInfo_; - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 10); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 10); EXPECT_TRUE(IsEqual(pattern_->GetItemRect(0), Rect(0, 0, 400.f / 3, 100))); layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(CalcLength(500.0f), CalcLength(Dimension(600.0f)))); @@ -1845,16 +1845,62 @@ HWTEST_F(WaterFlowSegmentTest, Constraint001, TestSize.Level1) EXPECT_EQ(GetChildWidth(frameNode_, i), (500.f - 3) / 5); } EXPECT_EQ(GetChildWidth(frameNode_, 10), 500.f); - EXPECT_EQ(info.endIndex_, 10); + EXPECT_EQ(info->endIndex_, 10); layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(CalcLength(400.0f), CalcLength(Dimension(700.0f)))); FlushLayoutTask(frameNode_); EXPECT_TRUE(IsEqual(pattern_->GetItemRect(0), Rect(0, 0, 400.f / 3, 100))); - EXPECT_EQ(info.endIndex_, 11); + EXPECT_EQ(info->endIndex_, 11); layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(CalcLength(500.0f), CalcLength(Dimension(700.0f)))); FlushLayoutTask(frameNode_); EXPECT_TRUE(IsEqual(pattern_->GetItemRect(0), Rect(0, 0, 500.f / 3, 100))); - EXPECT_EQ(info.endIndex_, 11); + EXPECT_EQ(info->endIndex_, 11); +} + +/** + * @tc.name: ResetSections001 + * @tc.desc: Layout WaterFlow and then reset to old layout + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSegmentTest, ResetSections001, TestSize.Level1) +{ + Create( + [](WaterFlowModelNG model) { + ViewAbstract::SetWidth(CalcLength(400.0f)); + ViewAbstract::SetHeight(CalcLength(600.f)); + CreateItem(60); + }, + false); + auto secObj = pattern_->GetOrCreateWaterFlowSections(); + secObj->ChangeData(0, 0, SECTION_5); + MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); + FlushLayoutTask(frameNode_); + auto info = AceType::DynamicCast(pattern_->layoutInfo_); + + UpdateCurrentOffset(-205.0f); + EXPECT_EQ(info->currentOffset_, -205.0f); + EXPECT_EQ(info->startIndex_, 3); + EXPECT_EQ(info->endIndex_, 11); + + // fallback to layout without sections + pattern_->ResetSections(); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info->currentOffset_, -205.0f); + EXPECT_EQ(info->startIndex_, 1); + EXPECT_EQ(info->endIndex_, 5); + EXPECT_EQ(info->GetCrossCount(), 1); + if (SystemProperties::WaterFlowUseSegmentedLayout()) { + EXPECT_EQ(info->segmentTails_.size(), 1); + EXPECT_EQ(info->margins_.size(), 1); + } else { + EXPECT_TRUE(info->segmentTails_.empty()); + EXPECT_TRUE(info->margins_.empty()); + } + + UpdateCurrentOffset(250.0f); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 3); } } // namespace OHOS::Ace::NG -- Gitee From 4860794be7ce75757d7ed14356c2caa1e6fea433 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 23 Apr 2024 17:35:58 +0800 Subject: [PATCH 12/31] fix test Signed-off-by: Tianer Zhou Change-Id: I9df30fcc95decfc6090268d5780b595c384fb7f8 --- test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 528cec79ede..4731622cbfd 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -723,13 +723,15 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest011, TestSize.Level1) EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT), { 0, -ITEM_HEIGHT })); - EXPECT_EQ(pattern_->layoutInfo_->offset(), -WATERFLOW_HEIGHT); + pattern_->layoutInfo_->startIndex_ = 0; - pattern_->layoutInfo_->UpdateOffset(ITEM_HEIGHT); + // total offset = ITEM_HEIGHT + pattern_->layoutInfo_->UpdateOffset(WATERFLOW_HEIGHT); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { ITEM_HEIGHT, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT * 2), { -ITEM_HEIGHT, 0 })); + // total offset = -ITEM_HEIGHT * 3 pattern_->layoutInfo_->UpdateOffset(-ITEM_HEIGHT * 4); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT * 2), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); -- Gitee From f2381108ca1bbb3c864b42b78c1b6ca0ca821e4c Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 23 Apr 2024 21:04:40 +0800 Subject: [PATCH 13/31] optimize fill Signed-off-by: Tianer Zhou Change-Id: I5db48f7f2a4f79f55c408ba4644ba5a9bd4ddbb5 --- .../water_flow_layout_info_sw.cpp | 11 +- .../sliding_window/water_flow_sw_layout.cpp | 135 +++++++++--------- .../sliding_window/water_flow_sw_layout.h | 10 +- .../waterflow/water_flow_layout_info_base.h | 2 +- .../pattern/waterflow/water_flow_test_ng.cpp | 20 +-- 5 files changed, 100 insertions(+), 78 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 3526205410d..1bd5c4cb9e6 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -27,6 +27,9 @@ void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) for (const auto& lane : lanes_) { std::cout << "SYNC lane = " << lane.ToString() << std::endl; } + if (startIndex_ <= endIndex_) { + storedOffset_ = lanes_[idxToLane_.at(startIndex_)].startPos; + } delta_ = 0.0f; lastMainSize_ = mainSize; mainGap_ = mainGap; @@ -148,18 +151,18 @@ float WaterFlowLayoutInfoSW::StartPos() const bool WaterFlowLayoutInfoSW::ReachStart(float prevPos, bool firstLayout) const { - if (firstLayout || !itemStart_ || lanes_.empty()) { + if (!itemStart_ || lanes_.empty()) { return false; } - return Negative(prevPos); + return firstLayout || Negative(prevPos); } bool WaterFlowLayoutInfoSW::ReachEnd(float prevPos) const { - if (!itemEnd_ || lanes_.empty()) { + if (!offsetEnd_ || lanes_.empty()) { return false; } - float prevEndPos = EndPos() - offset() + prevPos; + float prevEndPos = EndPos() - (totalOffset_ - prevPos); return GreatNotEqual(prevEndPos, lastMainSize_); } diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index f2220f589a5..e81d9065541 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -76,8 +76,6 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) continue; } auto childNode = child->GetGeometryNode(); - std::cout << "layout item " << item.idx << " with height " << childNode->GetFrameSize().Height() << " at mainPos " - << mainPos << " crossPos = " << crossPos << std::endl; auto offset = info_->axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }; childNode->SetMarginFrameOffset(offset + paddingOffset); @@ -194,31 +192,8 @@ void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) } } -void WaterFlowSWLayout::RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx) -{ - std::unordered_set lanes; - for (size_t i = 0; i < info_->lanes_.size(); ++i) { - if (LessNotEqual(info_->lanes_[i].endPos + mainGap_, viewportBound)) { - lanes.insert(i); - } - } - - auto props = DynamicCast(wrapper_->GetLayoutProperty()); - while (!lanes.empty() && idx <= maxChildIdx && info_->idxToLane_.count(idx)) { - size_t laneIdx = info_->idxToLane_.at(idx); - float mainLen = MeasureChild(props, idx, laneIdx); - auto& lane = info_->lanes_[laneIdx]; - lane.endPos += mainLen + mainGap_; - lane.items_.push_front({ idx++, mainLen }); - if (GreatOrEqual(lane.endPos, viewportBound)) { - lanes.erase(laneIdx); - } - } -} - // [lane start/end position, lane index] using lanePos = std::pair; - void WaterFlowSWLayout::FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx) { maxChildIdx = std::min(maxChildIdx, info_->childrenCount_ - 1); @@ -236,43 +211,16 @@ void WaterFlowSWLayout::FillBack(float viewportBound, int32_t idx, int32_t maxCh auto props = DynamicCast(wrapper_->GetLayoutProperty()); while (!q.empty() && idx <= maxChildIdx) { - auto [endPos, laneIdx] = q.top(); + auto [_, laneIdx] = q.top(); q.pop(); - float mainLen = MeasureChild(props, idx, laneIdx); - endPos += mainLen + mainGap_; - - auto& lane = info_->lanes_[laneIdx]; - lane.endPos = endPos; info_->idxToLane_[idx] = laneIdx; - lane.items_.push_back({ idx++, mainLen }); + float endPos = FillBackHelper(props, idx++, laneIdx); if (LessNotEqual(endPos, viewportBound)) { q.push({ endPos, laneIdx }); } } } -void WaterFlowSWLayout::RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx) -{ - std::unordered_set lanes; - for (size_t i = 0; i < info_->lanes_.size(); ++i) { - float startPos = info_->lanes_[i].startPos; - if (GreatNotEqual(startPos - mainGap_, viewportBound)) { - lanes.insert(i); - } - } - auto props = DynamicCast(wrapper_->GetLayoutProperty()); - while (!lanes.empty() && idx >= minChildIdx && info_->idxToLane_.count(idx)) { - size_t laneIdx = info_->idxToLane_.at(idx); - float mainLen = MeasureChild(props, idx, laneIdx); - auto& lane = info_->lanes_[laneIdx]; - lane.startPos -= mainLen + mainGap_; - lane.items_.push_front({ idx--, mainLen }); - if (LessOrEqual(lane.startPos, viewportBound)) { - lanes.erase(laneIdx); - } - } -} - namespace { // max heap but with smaller laneIdx at the top struct MaxHeapCmp { @@ -285,7 +233,6 @@ struct MaxHeapCmp { } }; } // namespace - void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minChildIdx) { minChildIdx = std::max(minChildIdx, 0); @@ -302,24 +249,83 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minC auto props = DynamicCast(wrapper_->GetLayoutProperty()); while (!q.empty() && idx >= minChildIdx) { - auto [startPos, laneIdx] = q.top(); + auto [_, laneIdx] = q.top(); q.pop(); - float mainLen = MeasureChild(props, idx, laneIdx); - startPos -= mainGap_ + mainLen; - - auto& lane = info_->lanes_[laneIdx]; info_->idxToLane_[idx] = laneIdx; - lane.startPos = startPos; - lane.items_.push_front({ idx--, mainLen }); + float startPos = FillFrontHelper(props, idx--, laneIdx); if (GreatNotEqual(startPos - mainGap_, viewportBound)) { q.push({ startPos, laneIdx }); } } } +float WaterFlowSWLayout::FillBackHelper(const RefPtr& props, int32_t idx, size_t laneIdx) +{ + float mainLen = MeasureChild(props, idx, laneIdx); + std::cout << "mainLen = " << mainLen << " idx = " << idx << std::endl; + auto& lane = info_->lanes_[laneIdx]; + lane.endPos += mainGap_ + mainLen; + if (lane.items_.empty()) { + lane.endPos -= mainGap_; + } + lane.items_.push_back({ idx, mainLen }); + return lane.endPos; +} + +float WaterFlowSWLayout::FillFrontHelper(const RefPtr& props, int32_t idx, size_t laneIdx) +{ + float mainLen = MeasureChild(props, idx, laneIdx); + auto& lane = info_->lanes_[laneIdx]; + lane.startPos -= mainGap_ + mainLen; + if (lane.items_.empty()) { + lane.startPos += mainGap_; + } + lane.items_.push_front({ idx, mainLen }); + return lane.startPos; +} + +void WaterFlowSWLayout::RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx) +{ + std::unordered_set lanes; + for (size_t i = 0; i < info_->lanes_.size(); ++i) { + if (LessNotEqual(info_->lanes_[i].endPos + mainGap_, viewportBound)) { + lanes.insert(i); + } + } + + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + while (!lanes.empty() && idx <= maxChildIdx && info_->idxToLane_.count(idx)) { + size_t laneIdx = info_->idxToLane_.at(idx); + float endPos = FillBackHelper(props, idx++, laneIdx); + if (GreatOrEqual(endPos + mainGap_, viewportBound)) { + lanes.erase(laneIdx); + } + } +} + +void WaterFlowSWLayout::RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx) +{ + std::unordered_set lanes; + for (size_t i = 0; i < info_->lanes_.size(); ++i) { + float startPos = info_->lanes_[i].startPos; + if (GreatNotEqual(startPos - mainGap_, viewportBound)) { + lanes.insert(i); + } + } + auto props = DynamicCast(wrapper_->GetLayoutProperty()); + while (!lanes.empty() && idx >= minChildIdx && info_->idxToLane_.count(idx)) { + size_t laneIdx = info_->idxToLane_.at(idx); + float startPos = FillFrontHelper(props, idx--, laneIdx); + if (LessOrEqual(startPos, viewportBound)) { + lanes.erase(laneIdx); + } + } +} + void WaterFlowSWLayout::ClearBack(float bound) { - for (int32_t i = info_->EndIndex(); i <= info_->StartIndex(); ++i) { + int32_t startIdx = info_->StartIndex(); + for (int32_t i = info_->EndIndex(); i >= startIdx; --i) { size_t laneIdx = info_->idxToLane_.at(i); auto& lane = info_->lanes_[laneIdx]; if (lane.items_.back().idx != i) { @@ -336,7 +342,8 @@ void WaterFlowSWLayout::ClearBack(float bound) void WaterFlowSWLayout::ClearFront() { - for (int32_t i = info_->StartIndex(); i <= info_->EndIndex(); ++i) { + int32_t endIdx = info_->EndIndex(); + for (int32_t i = info_->StartIndex(); i <= endIdx; ++i) { size_t laneIdx = info_->idxToLane_.at(i); auto& lane = info_->lanes_[laneIdx]; if (lane.items_.front().idx != i) { diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index 34ef80e42e6..beb3fbe1025 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -19,7 +19,7 @@ #include "core/components/scroll/scroll_controller_base.h" #include "core/components_ng/layout/layout_wrapper.h" #include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" namespace OHOS::Ace::NG { @@ -69,6 +69,10 @@ private: * @brief fills the viewport backward with cached idx -> lane mapping. */ void RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx); + /** + * @return new startPos of the filled lane. + */ + float FillFrontHelper(const RefPtr& props, int32_t idx, size_t laneIdx); /** * @brief Clear items above the viewport. * Iterate by index to keep item range continuous. @@ -87,6 +91,10 @@ private: * @brief fills the viewport backward with cached idx -> lane mapping. */ void RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx); + /** + * @return new endPos of the filled lane. + */ + float FillBackHelper(const RefPtr& props, int32_t idx, size_t laneIdx); /** * @brief Clear items below the viewport. * diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index de61958bdbe..04d2f504a77 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -68,7 +68,7 @@ public: * For triggering events. * * @param prevPos previous layout position. - * @param firstLayout check this to prevent emitting ReachStart on the initial layout. + * @param firstLayout check this to emit ReachStart on the initial layout. * @return true if current position just reached content top. */ virtual bool ReachStart(float prevPos, bool firstLayout) const = 0; diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 4731622cbfd..0866097a495 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -28,8 +28,6 @@ #define protected public #define private public -#include "test/mock/base/mock_task_executor.h" -#include "test/mock/core/common/mock_container.h" #include "test/mock/core/common/mock_theme_manager.h" #include "test/mock/core/pipeline/mock_pipeline_context.h" #include "test/mock/core/render/mock_render_context.h" @@ -37,16 +35,12 @@ #include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" #include "base/geometry/dimension.h" -#include "base/geometry/ng/size_t.h" #include "base/geometry/offset.h" #include "base/memory/ace_type.h" #include "base/utils/utils.h" #include "core/components/button/button_theme.h" #include "core/components/common/layout/constants.h" #include "core/components_ng/base/view_stack_processor.h" -#include "core/components_ng/pattern/button/button_layout_property.h" -#include "core/components_ng/pattern/button/button_model_ng.h" -#include "core/components_ng/pattern/button/button_pattern.h" #include "core/components_ng/pattern/linear_layout/row_model_ng.h" #include "core/components_ng/pattern/pattern.h" #include "core/components_ng/pattern/scrollable/scrollable.h" @@ -708,7 +702,9 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest010, TestSize.Level1) */ HWTEST_F(WaterFlowTestNg, WaterFlowTest011, TestSize.Level1) { - CreateWithItem([](WaterFlowModelNG model) { model.SetColumnsTemplate("1fr"); }); + CreateWithItem([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr"); + }); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { ITEM_HEIGHT, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); @@ -723,16 +719,23 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest011, TestSize.Level1) EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT), { 0, -ITEM_HEIGHT })); - + + // enable overScroll + pattern_->SetEdgeEffect(EdgeEffect::SPRING); + pattern_->animateOverScroll_ = true; pattern_->layoutInfo_->startIndex_ = 0; // total offset = ITEM_HEIGHT pattern_->layoutInfo_->UpdateOffset(WATERFLOW_HEIGHT); + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); + FlushLayoutTask(frameNode_); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { ITEM_HEIGHT, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT * 2), { -ITEM_HEIGHT, 0 })); // total offset = -ITEM_HEIGHT * 3 pattern_->layoutInfo_->UpdateOffset(-ITEM_HEIGHT * 4); + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); + FlushLayoutTask(frameNode_); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT * 2), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT), { 0, 0 })); @@ -1014,6 +1017,7 @@ HWTEST_F(WaterFlowTestNg, Callback001, TestSize.Level1) * @tc.expected: Trigger reachend */ UpdateCurrentOffset(-WATERFLOW_HEIGHT); + EXPECT_TRUE(pattern_->layoutInfo_->offsetEnd_); EXPECT_TRUE(isReachEndCalled); /** -- Gitee From 115fb229b56518eaf7ebd32603d73e386cfc3519 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 24 Apr 2024 10:12:38 +0800 Subject: [PATCH 14/31] fix jump to target Signed-off-by: Tianer Zhou Change-Id: I647add36d3ba4e2892322728c8e7635982b2260f --- .../water_flow_layout_info_sw.cpp | 27 ++++++++++++------- .../water_flow_layout_info_sw.h | 1 + .../sliding_window/water_flow_sw_layout.cpp | 1 - .../waterflow/water_flow_layout_info_base.h | 2 +- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 1bd5c4cb9e6..7ae48200799 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -195,7 +195,7 @@ float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx std::cout << "lane start pos: " << lane.startPos << " lane end pos: " << lane.endPos << " item count = " << lane.items_.size() << " first item " << lane.items_.front().idx << " back = " << lane.items_.back().idx << " lane indx = " << idxToLane_.at(idx) << std::endl; - if (idx <= endIndex_) { + if (idx < endIndex_) { pos = DistanceToTop(idx, mainGap_); auto it = std::find_if( lane.items_.begin(), lane.items_.end(), [idx](const ItemInfo& item) { return item.idx == idx; }); @@ -204,29 +204,36 @@ float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx } itemSize = it->mainSize; } else { - pos = -DistanceToBottom(idx, lastMainSize_, mainGap_) - lastMainSize_; if (lane.items_.back().idx != idx) { std::abort(); } itemSize = lane.items_.back().mainSize; + pos = lane.endPos - itemSize; } switch (align_) { case ScrollAlign::START: - return pos; + break; case ScrollAlign::END: - return pos - lastMainSize_ + itemSize; + pos = pos - lastMainSize_ + itemSize; + break; case ScrollAlign::AUTO: if (Negative(pos)) { - return pos; + /* */ } else if (GreatNotEqual(pos + itemSize, lastMainSize_)) { - return pos - lastMainSize_ + itemSize; + pos = pos - lastMainSize_ + itemSize; + } else { + pos = 0.0f; // already in viewport, no movement needed } - return 0.0f; // already in viewport, no movement needed + break; case ScrollAlign::CENTER: - return pos - (lastMainSize_ - itemSize) / 2; + pos = pos - (lastMainSize_ - itemSize) / 2; + break; default: - return 0.0f; + pos = 0.0f; + break; } + // convert to absolute position + return pos - totalOffset_; } void WaterFlowLayoutInfoSW::Reset() @@ -235,6 +242,7 @@ void WaterFlowLayoutInfoSW::Reset() delta_ = DistanceToTop(startIndex_, mainGap_); lanes_.clear(); idxToLane_.clear(); + synced_ = false; } int32_t WaterFlowLayoutInfoSW::EndIndex() const @@ -284,6 +292,7 @@ void WaterFlowLayoutInfoSW::ResetBeforeJump(float laneBasePos) }); totalOffset_ = 0; idxToLane_.clear(); + synced_ = false; } std::string WaterFlowLayoutInfoSW::Lane::ToString() const diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 846532d436c..9fa57dbd62c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -49,6 +49,7 @@ public: void UpdateOffset(float delta) override { delta_ = delta; + synced_ = false; } int32_t GetCrossIndex(int32_t itemIndex) const override; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index e81d9065541..42bbbc0a6d1 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -262,7 +262,6 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minC float WaterFlowSWLayout::FillBackHelper(const RefPtr& props, int32_t idx, size_t laneIdx) { float mainLen = MeasureChild(props, idx, laneIdx); - std::cout << "mainLen = " << mainLen << " idx = " << idx << std::endl; auto& lane = info_->lanes_[laneIdx]; lane.endPos += mainGap_ + mainLen; if (lane.items_.empty()) { diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index 04d2f504a77..0deecacafa8 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -93,7 +93,7 @@ public: * * @param idx item's index. * @param crossIdx item's cross-axis lane index. - * @return position + * @return absolute position to scroll to. */ virtual float CalcTargetPosition(int32_t idx, int32_t crossIdx) const = 0; -- Gitee From 6966aced28aecc721c7da6c261560f5a12f84cab Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 24 Apr 2024 16:23:39 +0800 Subject: [PATCH 15/31] layout footer, now passes all tests Signed-off-by: Tianer Zhou Change-Id: Ifad6918e0167a062a2d7fae55a4432ae9992dd43 --- .../water_flow_layout_info_sw.cpp | 6 - .../water_flow_layout_info_sw.h | 1 + .../sliding_window/water_flow_sw_layout.cpp | 159 ++++++++++++------ .../sliding_window/water_flow_sw_layout.h | 26 ++- .../waterflow/water_flow_layout_algorithm.cpp | 13 +- .../waterflow/water_flow_layout_algorithm.h | 1 - .../waterflow/water_flow_layout_utils.cpp | 11 ++ .../waterflow/water_flow_layout_utils.h | 7 + .../pattern/waterflow/water_flow_pattern.cpp | 2 +- .../waterflow/water_flow_sw_layout_test.cpp | 3 - .../pattern/waterflow/water_flow_test_ng.cpp | 12 +- 11 files changed, 157 insertions(+), 84 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 7ae48200799..6970d7bc61f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -24,9 +24,6 @@ void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) { startIndex_ = StartIndex(); endIndex_ = EndIndex(); - for (const auto& lane : lanes_) { - std::cout << "SYNC lane = " << lane.ToString() << std::endl; - } if (startIndex_ <= endIndex_) { storedOffset_ = lanes_[idxToLane_.at(startIndex_)].startPos; } @@ -192,9 +189,6 @@ float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx const auto& lane = lanes_[idxToLane_.at(idx)]; float pos = 0.0f; // main-axis position of the item's top edge relative to viewport top. Positive if below viewport float itemSize = 0.0f; - std::cout << "lane start pos: " << lane.startPos << " lane end pos: " << lane.endPos - << " item count = " << lane.items_.size() << " first item " << lane.items_.front().idx - << " back = " << lane.items_.back().idx << " lane indx = " << idxToLane_.at(idx) << std::endl; if (idx < endIndex_) { pos = DistanceToTop(idx, mainGap_); auto it = std::find_if( diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 9fa57dbd62c..cccca54fe5b 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -133,6 +133,7 @@ public: float delta_ = 0.0f; float totalOffset_ = 0.0f; // record total offset when continuously scrolling. Reset when jumped float mainGap_ = 0.0f; // update this at the end of a layout + bool synced_ = false; struct ItemInfo; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index 42bbbc0a6d1..f9c19411f7b 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -25,6 +25,7 @@ #include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_utils.h" +#include "core/components_ng/property/measure_utils.h" #include "core/components_ng/property/templates_parser.h" namespace OHOS::Ace::NG { @@ -35,26 +36,22 @@ void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) auto props = DynamicCast(wrapper->GetLayoutProperty()); info_->axis_ = axis_ = props->GetAxis(); - auto [idealSize, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_); - Init(idealSize); + auto [size, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_); + Init(size); CheckReset(); if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { - overScroll_ = false; MeasureOnJump(info_->jumpIndex_, info_->align_); - if (!NearZero(info_->delta_)) { - MeasureOnOffset(info_->delta_); - } } else if (info_->targetIndex_) { MeasureToTarget(*info_->targetIndex_); } else { MeasureOnOffset(info_->delta_); } - // if (matchChildren) { - // PostMeasureSelf(); - // } + if (matchChildren) { + PostMeasureSelf(size.CrossSize(axis_)); + } - info_->Sync(mainSize_, mainGap_); + info_->Sync(mainLen_, mainGap_); wrapper->SetCacheCount(props->GetCachedCountValue(1)); } @@ -64,20 +61,31 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) return; } - auto padding = wrapper_->GetLayoutProperty()->CreatePaddingAndBorder(); + auto props = DynamicCast(wrapper->GetLayoutProperty()); + auto padding = props->CreatePaddingAndBorder(); OffsetF paddingOffset { padding.top.value_or(0.0f), padding.top.value_or(0.0f) }; - float crossPos = 0.0f; + + bool reverse = props->IsReverse(); + bool rtl = props->GetNonAutoLayoutDirection() == TextDirection::RTL && axis_ == Axis::VERTICAL; + float selfCrossLen = wrapper->GetGeometryNode()->GetContentSize().CrossSize(axis_); + + float crossPos = rtl ? selfCrossLen + mainGap_ : 0.0f; for (size_t i = 0; i < info_->lanes_.size(); ++i) { + if (rtl) { + crossPos -= itemCrossSize_[i] + mainGap_; + } auto& lane = info_->lanes_[i]; float mainPos = lane.startPos; for (auto& item : lane.items_) { - auto child = wrapper->GetOrCreateChildByIndex(item.idx); + auto child = wrapper->GetOrCreateChildByIndex(nodeIdx(item.idx)); if (!child) { continue; } auto childNode = child->GetGeometryNode(); - auto offset = - info_->axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }; + if (reverse) { + mainPos = mainLen_ - item.mainSize - mainPos; + } + auto offset = axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }; childNode->SetMarginFrameOffset(offset + paddingOffset); if (child->CheckNeedForceMeasureAndLayout()) { @@ -87,16 +95,19 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) } mainPos += item.mainSize + mainGap_; } - crossPos += itemCrossSize_[i] + crossGap_; + if (!rtl) { + crossPos += itemCrossSize_[i] + mainGap_; + } } - wrapper->SetActiveChildRange(info_->startIndex_, info_->endIndex_); + wrapper->SetActiveChildRange(nodeIdx(info_->startIndex_), nodeIdx(info_->endIndex_)); + LayoutFooter(paddingOffset, reverse); } void WaterFlowSWLayout::Init(const SizeF& frameSize) { - info_->footerIndex_ = 0; - info_->childrenCount_ = wrapper_->GetTotalChildCount(); + // omit footer from children count + info_->childrenCount_ = wrapper_->GetTotalChildCount() - info_->footerIndex_ - 1; auto props = DynamicCast(wrapper_->GetLayoutProperty()); auto scale = props->GetLayoutConstraint()->scaleProperty; @@ -105,7 +116,7 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) mainGap_ = { axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap }; crossGap_ = { axis_ == Axis::VERTICAL ? columnsGap : rowsGap }; - mainSize_ = frameSize.MainSize(axis_); + mainLen_ = frameSize.MainSize(axis_); float crossSize = frameSize.CrossSize(axis_); std::pair, bool> cross; auto rowsTemplate = props->GetRowsTemplate().value_or("1fr"); @@ -135,6 +146,10 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) void WaterFlowSWLayout::CheckReset() { int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated(); + if (info_->footerIndex_ == 0) { + // convert children node index to item index + --updateIdx; + } if (updateIdx != -1) { if (info_->ItemInView(updateIdx)) { info_->align_ = ScrollAlign::START; @@ -150,15 +165,10 @@ void WaterFlowSWLayout::CheckReset() void WaterFlowSWLayout::MeasureOnOffset(float delta) { ApplyDelta(delta); - for (const auto& lane : info_->lanes_) { - std::cout << "after APPLY delta lane = " << lane.ToString() << std::endl; - } - if (!overScroll_) { - AdjustOverScroll(); - } + AdjustOverScroll(); // clear out items outside viewport after position change if (Positive(delta)) { - ClearBack(mainSize_); + ClearBack(mainLen_); } else { ClearFront(); } @@ -166,7 +176,6 @@ void WaterFlowSWLayout::MeasureOnOffset(float delta) void WaterFlowSWLayout::ApplyDelta(float delta) { - std::cout << "offset = " << delta << std::endl; info_->totalOffset_ += delta; for (auto& lane : info_->lanes_) { lane.startPos += delta; @@ -177,14 +186,12 @@ void WaterFlowSWLayout::ApplyDelta(float delta) // positive offset is scrolling upwards FillFront(0.0f, info_->StartIndex() - 1, 0); } else { - FillBack(mainSize_, info_->EndIndex() + 1, info_->childrenCount_ - 1); + FillBack(mainLen_, info_->EndIndex() + 1, info_->childrenCount_ - 1); } } void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) { - std::cout << "measure to target " << targetIdx << " start = " << info_->startIndex_ << " end " << info_->endIndex_ - << std::endl; if (targetIdx < info_->startIndex_) { FillFront(-FLT_MAX, info_->startIndex_ - 1, targetIdx); } else if (targetIdx > info_->endIndex_) { @@ -202,7 +209,6 @@ void WaterFlowSWLayout::FillBack(float viewportBound, int32_t idx, int32_t maxCh } std::priority_queue, std::greater<>> q; for (size_t i = 0; i < info_->lanes_.size(); ++i) { - std::cout << "Lane " << i << " = " << info_->lanes_[i].ToString() << std::endl; float endPos = info_->lanes_[i].endPos; if (LessNotEqual(endPos + mainGap_, viewportBound)) { q.push({ endPos, i }); @@ -363,7 +369,7 @@ ScrollAlign WaterFlowSWLayout::ParseAutoAlign(int32_t jumpIdx, bool inView) if (Negative(info_->DistanceToTop(jumpIdx, mainGap_))) { return ScrollAlign::START; } - if (Negative(info_->DistanceToBottom(jumpIdx, mainSize_, mainGap_))) { + if (Negative(info_->DistanceToBottom(jumpIdx, mainLen_, mainGap_))) { return ScrollAlign::END; } // item is already fully in viewport @@ -380,6 +386,7 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) if (jumpIdx == -1) { jumpIdx = info_->childrenCount_ - 1; } + overScroll_ = false; bool inView = info_->ItemInView(jumpIdx); if (align == ScrollAlign::AUTO) { @@ -389,48 +396,58 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) // If the item is within 1 full-viewport distance (approximately), we consider it close. // Then we simply scroll to it instead of triggering a reset/jump, which would change the layout. int32_t cntInView = info_->endIndex_ - info_->startIndex_ + 1; - std::cout << "start " << info_->startIndex_ << " end = " << info_->endIndex_ << std::endl; bool closeToView = jumpIdx > info_->endIndex_ ? jumpIdx - info_->endIndex_ < cntInView : info_->startIndex_ - jumpIdx < cntInView; if (closeToView) { MeasureToTarget(jumpIdx); } + Jump(jumpIdx, align, inView || closeToView); + + if (!NearZero(info_->delta_)) { + MeasureOnOffset(info_->delta_); + } else { + AdjustOverScroll(); + ClearFront(); + ClearBack(mainLen_); + } +} + +void WaterFlowSWLayout::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) +{ switch (align) { case ScrollAlign::START: { - if (inView || closeToView) { - MeasureOnOffset(-info_->DistanceToTop(jumpIdx, mainGap_)); + if (noSkip) { + ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_)); } else { info_->ResetBeforeJump(0.0f); - FillBack(mainSize_, jumpIdx, info_->childrenCount_ - 1); + FillBack(mainLen_, jumpIdx, info_->childrenCount_ - 1); } break; } case ScrollAlign::CENTER: { auto props = DynamicCast(wrapper_->GetLayoutProperty()); - if (inView || closeToView) { - std::cout << "jumpIdx = " << jumpIdx << " map size = " << info_->idxToLane_.size() - << " children size = " << info_->childrenCount_ << std::endl; + if (noSkip) { float itemH = MeasureChild(props, jumpIdx, info_->idxToLane_.at(jumpIdx)); - MeasureOnOffset(-info_->DistanceToTop(jumpIdx, mainGap_) + (mainSize_ - itemH) / 2.0f); + ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_) + (mainLen_ - itemH) / 2.0f); } else { - info_->ResetBeforeJump(mainSize_ / 2.0f); + info_->ResetBeforeJump(mainLen_ / 2.0f); info_->idxToLane_ = { { jumpIdx, 0 } }; auto& lane = info_->lanes_[0]; float itemH = MeasureChild(props, jumpIdx, 0); - lane.startPos = (mainSize_ - itemH) / 2.0f; - lane.endPos = (mainSize_ + itemH) / 2.0f; + lane.startPos = (mainLen_ - itemH) / 2.0f; + lane.endPos = (mainLen_ + itemH) / 2.0f; lane.items_.push_back({ jumpIdx, itemH }); FillFront(0.0f, jumpIdx - 1, 0); - FillBack(mainSize_, jumpIdx + 1, info_->childrenCount_ - 1); + FillBack(mainLen_, jumpIdx + 1, info_->childrenCount_ - 1); } break; } case ScrollAlign::END: { - if (inView || closeToView) { - MeasureOnOffset(info_->DistanceToBottom(jumpIdx, mainSize_, mainGap_)); + if (noSkip) { + ApplyDelta(info_->DistanceToBottom(jumpIdx, mainLen_, mainGap_)); } else { - info_->ResetBeforeJump(mainSize_); + info_->ResetBeforeJump(mainLen_); FillFront(0.0f, jumpIdx, 0); } break; @@ -448,24 +465,62 @@ void WaterFlowSWLayout::AdjustOverScroll() float maxEnd = info_->EndPos(); float minStart = info_->StartPos(); + if (LessNotEqual(maxEnd, mainLen_) && info_->footerIndex_ == 0) { + float footerMainLen = WaterFlowLayoutUtils::MeasureFooter(wrapper_, axis_); + maxEnd += mainGap_ + footerMainLen; + } + + if (overScroll_) { + return; + } if (Positive(minStart)) { ApplyDelta(-minStart); - } else if (LessNotEqual(maxEnd, mainSize_)) { + } else if (LessNotEqual(maxEnd, mainLen_)) { bool reachedTop = info_->StartIndex() == 0 && NearZero(info_->DistanceToTop(0, mainGap_)); if (reachedTop) { // no room to adjust return; } - ApplyDelta(mainSize_ - maxEnd); + ApplyDelta(mainLen_ - maxEnd); } } float WaterFlowSWLayout::MeasureChild(const RefPtr& props, int32_t idx, size_t lane) { - auto child = wrapper_->GetOrCreateChildByIndex(idx); + auto child = wrapper_->GetOrCreateChildByIndex(nodeIdx(idx)); CHECK_NULL_RETURN(child, 0.0f); child->Measure( - WaterFlowLayoutUtils::CreateChildConstraint({ itemCrossSize_[lane], mainSize_, axis_ }, props, child)); + WaterFlowLayoutUtils::CreateChildConstraint({ itemCrossSize_[lane], mainLen_, axis_ }, props, child)); return child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); } + +void WaterFlowSWLayout::LayoutFooter(const OffsetF& paddingOffset, bool reverse) +{ + float endPos = info_->EndPos(); + if (info_->footerIndex_ != 0 || GreatOrEqual(endPos, mainLen_)) { + return; + } + auto footer = wrapper_->GetOrCreateChildByIndex(0); + float mainPos = endPos + mainGap_; + if (reverse) { + mainPos = mainLen_ - footer->GetGeometryNode()->GetMarginFrameSize().MainSize(axis_) - mainPos; + } + footer->GetGeometryNode()->SetMarginFrameOffset( + (axis_ == Axis::VERTICAL) ? OffsetF(0.0f, mainPos) + paddingOffset : OffsetF(mainPos, 0.0f) + paddingOffset); + footer->Layout(); +} + +void WaterFlowSWLayout::PostMeasureSelf(float selfCrossLen) +{ + mainLen_ = info_->GetContentHeight(); + SizeF selfSize = (axis_ == Axis::VERTICAL) ? SizeF(selfCrossLen, mainLen_) : SizeF(mainLen_, selfCrossLen); + auto props = wrapper_->GetLayoutProperty(); + AddPaddingToSize(props->CreatePaddingAndBorder(), selfSize); + wrapper_->GetGeometryNode()->SetFrameSize(selfSize); +} + +inline int32_t WaterFlowSWLayout::nodeIdx(int32_t idx) const +{ + return idx + info_->footerIndex_ + 1; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index beb3fbe1025..e9687234388 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -38,6 +38,7 @@ public: private: void Init(const SizeF& frameSize); + void CheckReset(); void MeasureOnOffset(float delta); @@ -55,6 +56,19 @@ private: */ void MeasureOnJump(int32_t jumpIdx, ScrollAlign align); + /** + * @brief Helper to perform jumping to an item. + * + * @param noSkip true if we can directly apply offset to reach the target. + */ + void Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip); + + /** + * @brief convert Auto align to other Align types. + * + * @param inView true if item is between startIndex and endIndex. + * @return converted ScrollAlign type. + */ ScrollAlign ParseAutoAlign(int32_t jumpIdx, bool inView); /** @@ -104,16 +118,24 @@ private: void AdjustOverScroll(); + /** + * @brief If need to match children size, adjust self size after measuring children. + */ + void PostMeasureSelf(float selfCrossLen); + float MeasureChild(const RefPtr& props, int32_t idx, size_t lane); - void CheckReset(); + void LayoutFooter(const OffsetF& paddingOffset, bool reverse); + + // convert FlowItem's index to children node index. + inline int32_t nodeIdx(int32_t idx) const; LayoutWrapper* wrapper_ {}; RefPtr info_; Axis axis_ {}; std::vector itemCrossSize_; - float mainSize_ = 0.0f; + float mainLen_ = 0.0f; float mainGap_ = 0.0f; float crossGap_ = 0.0f; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp index 11cee3dfd2e..0ad8d92a8e0 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp @@ -354,7 +354,7 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L auto maxItemHeight = layoutInfo_->GetMaxMainHeight(); if (layoutInfo_->footerIndex_ >= 0) { footerMainStartPos_ = maxItemHeight; - footerMainSize_ = MeasureFooter(layoutWrapper); + footerMainSize_ = WaterFlowLayoutUtils::MeasureFooter(layoutWrapper, axis_); maxItemHeight += footerMainSize_; } layoutInfo_->maxHeight_ = maxItemHeight; @@ -389,15 +389,4 @@ void WaterFlowLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, L layoutInfo_->offsetEnd_ = false; } } - -float WaterFlowLayoutAlgorithm::MeasureFooter(LayoutWrapper* layoutWrapper) -{ - auto footer = layoutWrapper->GetOrCreateChildByIndex(layoutInfo_->footerIndex_); - auto layoutProperty = layoutWrapper->GetLayoutProperty(); - auto footerConstraint = layoutProperty->CreateChildConstraint(); - footer->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_CONTENT); - footer->Measure(footerConstraint); - auto itemSize = footer->GetGeometryNode()->GetMarginFrameSize(); - return GetMainAxisSize(itemSize, axis_); -} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h index 198b1c23363..426e6148f65 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h @@ -49,7 +49,6 @@ private: { return index + layoutInfo_->footerIndex_ + 1; } - float MeasureFooter(LayoutWrapper* layoutWrapper); void LayoutFooter(LayoutWrapper* layoutWrapper, const OffsetF& childFrameOffset, bool reverse); std::map itemsCrossSize_; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp index 50587c901c9..53f322f2beb 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp @@ -131,4 +131,15 @@ std::pair WaterFlowLayoutUtils::PreMeasureSelf(LayoutWrapper* wrapp wrapper->GetGeometryNode()->SetContentSize(size); return { size, matchChildren }; } + +float WaterFlowLayoutUtils::MeasureFooter(LayoutWrapper* wrapper, Axis axis) +{ + auto footer = wrapper->GetOrCreateChildByIndex(0); + auto layoutProperty = wrapper->GetLayoutProperty(); + auto footerConstraint = layoutProperty->CreateChildConstraint(); + footer->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_CONTENT); + footer->Measure(footerConstraint); + auto itemSize = footer->GetGeometryNode()->GetMarginFrameSize(); + return GetMainAxisSize(itemSize, axis); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h index 77423663f90..74bf9b5f83c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.h @@ -40,6 +40,13 @@ public: * @return [idealSize given by parent, whether measure is successful (need to adapt to children size if not)]. */ static std::pair PreMeasureSelf(LayoutWrapper* wrapper, Axis axis); + + /** + * @brief Helper to measure the footer node. + * REQUIRES: footer resides at index 0. + * @return main length of the footer node. + */ + static float MeasureFooter(LayoutWrapper* layoutWrapper, Axis axis); }; } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_UTILS_H diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 4dc8d237451..b5499495181 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -565,11 +565,11 @@ void WaterFlowPattern::AddFooter(const RefPtr& footer) CHECK_NULL_VOID(host); auto prevFooter = footer_.Upgrade(); if (!prevFooter) { - layoutInfo_->footerIndex_ = 0; host->AddChild(footer); } else { host->ReplaceChild(prevFooter, footer); } + layoutInfo_->footerIndex_ = 0; footer_ = footer; footer->SetActive(false); } diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index 341c66be228..6dcb5e80164 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -69,9 +69,6 @@ HWTEST_F(WaterFlowSWTest, Jump001, TestSize.Level1) FlushLayoutTask(frameNode_); EXPECT_EQ(info_->startIndex_, 5); EXPECT_EQ(info_->endIndex_, 9); - for (auto& lane : info_->lanes_) { - std::cout << "Lane = " << lane.ToString() << std::endl; - } EXPECT_EQ(info_->idxToLane_.at(8), 2); EXPECT_EQ(info_->lanes_[0].endPos, 200.0f); EXPECT_EQ(info_->lanes_[1].startPos, -100.0f); diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 0866097a495..8a5dc7bc524 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -108,15 +108,15 @@ void WaterFlowTestNg::Create(const std::function& callba RefPtr positionController = model.CreateScrollController(); RefPtr scrollBarProxy = model.CreateScrollBarProxy(); model.Create(); +#ifdef TEST_WATER_FLOW_SW + model.SetLayoutMode(WaterFlowLayoutMode::SLIDING_WINDOW); +#endif ViewAbstract::SetWidth(CalcLength(WATERFLOW_WIDTH)); ViewAbstract::SetHeight(CalcLength(WATERFLOW_HEIGHT)); model.SetScroller(positionController, scrollBarProxy); if (callback) { callback(model); } -#ifdef TEST_WATER_FLOW_SW - model.SetLayoutMode(WaterFlowLayoutMode::SLIDING_WINDOW); -#endif GetInstance(); if (flushLayout) { FlushLayoutTask(frameNode_); @@ -702,9 +702,7 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest010, TestSize.Level1) */ HWTEST_F(WaterFlowTestNg, WaterFlowTest011, TestSize.Level1) { - CreateWithItem([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr"); - }); + CreateWithItem([](WaterFlowModelNG model) { model.SetColumnsTemplate("1fr"); }); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { ITEM_HEIGHT, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); @@ -719,7 +717,7 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest011, TestSize.Level1) EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(0.f), { 0, 0 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(-ITEM_HEIGHT), { 0, -ITEM_HEIGHT })); - + // enable overScroll pattern_->SetEdgeEffect(EdgeEffect::SPRING); pattern_->animateOverScroll_ = true; -- Gitee From 310d9c99bbbc351266bc9fabfe660ba36754e44b Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 24 Apr 2024 16:36:53 +0800 Subject: [PATCH 16/31] fix segmented footer test Signed-off-by: Tianer Zhou Change-Id: Ia67ac1569cce213d6e4809b8f7fdb76aa1e4b4a2 --- .../components_ng/pattern/waterflow/water_flow_pattern.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index b5499495181..3d5fa149c55 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -566,10 +566,10 @@ void WaterFlowPattern::AddFooter(const RefPtr& footer) auto prevFooter = footer_.Upgrade(); if (!prevFooter) { host->AddChild(footer); + layoutInfo_->footerIndex_ = 0; } else { host->ReplaceChild(prevFooter, footer); } - layoutInfo_->footerIndex_ = 0; footer_ = footer; footer->SetActive(false); } @@ -581,5 +581,9 @@ void WaterFlowPattern::SetLayoutMode(LayoutMode mode) if (!layoutInfo_ || mode != layoutInfo_->mode()) { layoutInfo_ = WaterFlowLayoutInfoBase::Create(mode); } + // footer index only set during first AddFooter call + if (footer_.Upgrade()) { + layoutInfo_->footerIndex_ = 0; + } } } // namespace OHOS::Ace::NG -- Gitee From b03d5938af80cf390ebcce2aa91a6aa54a5ab0c9 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 24 Apr 2024 16:48:27 +0800 Subject: [PATCH 17/31] fix code check Signed-off-by: Tianer Zhou Change-Id: Ie94b16857b5994fa191a469193e5d2bd6e7fa449 --- .../sliding_window/water_flow_layout_info_sw.cpp | 2 +- .../sliding_window/water_flow_layout_info_sw.h | 12 +++++------- .../layout/sliding_window/water_flow_sw_layout.h | 2 +- .../waterflow/water_flow_layout_algorithm.cpp | 3 ++- .../pattern/waterflow/water_flow_layout_info_base.h | 4 ++-- .../pattern/waterflow/water_flow_layout_utils.cpp | 3 ++- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 6970d7bc61f..a064068dfcb 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -220,7 +220,7 @@ float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx } break; case ScrollAlign::CENTER: - pos = pos - (lastMainSize_ - itemSize) / 2; + pos = pos - (lastMainSize_ - itemSize) / 2.0f; break; default: pos = 0.0f; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index cccca54fe5b..dba81874f1d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -83,7 +83,7 @@ public: /** * @brief reset layout data before performing a jump. - * + * * @param laneBasePos base value for lane's start&end position. */ void ResetBeforeJump(float laneBasePos); @@ -92,19 +92,17 @@ public: /** * @brief Calculates distance from the item's top edge to the top of the viewport. - * - * @param item idx - * @param mainGap + * + * @param item index * @return positive result when item's top edge is below viewport. */ float DistanceToTop(int32_t item, float mainGap) const; /** * @brief Calculates distance from the item's bottom edge to the bottom of the viewport. - * - * @param item idx + * + * @param item index * @param mainSize of the viewport - * @param mainGap * @return positive result when item's bottom edge is above viewport. */ float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index e9687234388..b095c581b97 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -48,7 +48,7 @@ private: /** * @brief When the item is within or close to viewport, layout is preserved and we merely apply an offset. - * But when jumping to an item further away, the current layout would be reset, and a new layout happens from that index. + * When jumping to an item further away, the current layout would be reset for better layout performance. * * @param jumpIdx * @param align ScrollAlign diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp index 0ad8d92a8e0..3b068f0ac10 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp @@ -303,7 +303,8 @@ void WaterFlowLayoutAlgorithm::FillViewport(float mainSize, LayoutWrapper* layou auto currentIndex = layoutInfo_->startIndex_; auto position = GetItemPosition(currentIndex); bool fill = false; - while (LessNotEqual(position.startMainPos + layoutInfo_->currentOffset_, mainSize) || layoutInfo_->jumpIndex_ >= 0) { + while ( + LessNotEqual(position.startMainPos + layoutInfo_->currentOffset_, mainSize) || layoutInfo_->jumpIndex_ >= 0) { auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(GetChildIndexWithFooter(currentIndex)); if (!itemWrapper) { break; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index 0deecacafa8..39dd98ba4dd 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -64,7 +64,7 @@ public: virtual float CalcOverScroll(float mainSize, float delta) const = 0; /** - * @brief Check if WaterFlow just reached content top from the recent layout by comparing current position to [prevPos]. + * @brief Check if WaterFlow just reached content top from the recent layout. * For triggering events. * * @param prevPos previous layout position. @@ -73,7 +73,7 @@ public: */ virtual bool ReachStart(float prevPos, bool firstLayout) const = 0; /** - * @brief Check if WaterFlow just reached content bottom from the recent layout by comparing current position to [prevPos]. + * @brief Check if WaterFlow just reached content bottom from the recent layout. * For triggering events. * * @param prevPos previous layout position. diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp index 53f322f2beb..f12806cc562 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_utils.cpp @@ -42,7 +42,8 @@ std::string WaterFlowLayoutUtils::PreParseArgs(const std::string& args) return rowsArgs; } -FlowItemPosition WaterFlowLayoutUtils::GetItemPosition(const RefPtr& info, int32_t index, float mainGap) +FlowItemPosition WaterFlowLayoutUtils::GetItemPosition( + const RefPtr& info, int32_t index, float mainGap) { auto crossIndex = info->GetCrossIndex(index); // already in layoutInfo -- Gitee From bde9b878e4bf23107cb7acd667aee82552b2f0e0 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Thu, 25 Apr 2024 16:26:24 +0800 Subject: [PATCH 18/31] remove childrenCount_ from info Signed-off-by: Tianer Zhou Change-Id: Icd10a218d2d2de018e5bd70bdd7ca08881d65a88 --- .../water_flow_layout_info_sw.cpp | 6 ++-- .../water_flow_layout_info_sw.h | 2 +- .../sliding_window/water_flow_sw_layout.cpp | 29 ++++++++++--------- .../sliding_window/water_flow_sw_layout.h | 1 + .../waterflow/water_flow_layout_info.h | 2 ++ .../waterflow/water_flow_layout_info_base.h | 1 - .../pattern/waterflow/water_flow_pattern.cpp | 9 ++++++ .../pattern/waterflow/water_flow_pattern.h | 5 +--- 8 files changed, 33 insertions(+), 22 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index a064068dfcb..adf76ddd38a 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -20,7 +20,7 @@ #include "base/utils/utils.h" namespace OHOS::Ace::NG { -void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) +void WaterFlowLayoutInfoSW::Sync(int32_t itemCnt, float mainSize, float mainGap) { startIndex_ = StartIndex(); endIndex_ = EndIndex(); @@ -32,7 +32,7 @@ void WaterFlowLayoutInfoSW::Sync(float mainSize, float mainGap) mainGap_ = mainGap; itemStart_ = startIndex_ == 0 && NonNegative(DistanceToTop(0, mainGap_)); - itemEnd_ = endIndex_ == childrenCount_ - 1; + itemEnd_ = endIndex_ == itemCnt - 1; offsetEnd_ = itemEnd_ && std::all_of(lanes_.begin(), lanes_.end(), [mainSize](const Lane& lane) { return LessOrEqual(lane.endPos, mainSize); }); synced_ = true; @@ -259,7 +259,7 @@ int32_t WaterFlowLayoutInfoSW::StartIndex() const if (synced_) { return startIndex_; } - int32_t minIdx = childrenCount_; + auto minIdx = Infinity(); for (const auto& lane : lanes_) { if (lane.items_.empty()) { continue; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index dba81874f1d..d04e6ce374c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -88,7 +88,7 @@ public: */ void ResetBeforeJump(float laneBasePos); - void Sync(float mainSize, float mainGap); + void Sync(int32_t itemCnt, float mainSize, float mainGap); /** * @brief Calculates distance from the item's top edge to the top of the viewport. diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index f9c19411f7b..fbd4bce1ba5 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -51,7 +51,7 @@ void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) PostMeasureSelf(size.CrossSize(axis_)); } - info_->Sync(mainLen_, mainGap_); + info_->Sync(itemCnt_, mainLen_, mainGap_); wrapper->SetCacheCount(props->GetCachedCountValue(1)); } @@ -107,7 +107,7 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) void WaterFlowSWLayout::Init(const SizeF& frameSize) { // omit footer from children count - info_->childrenCount_ = wrapper_->GetTotalChildCount() - info_->footerIndex_ - 1; + itemCnt_ = wrapper_->GetTotalChildCount() - info_->footerIndex_ - 1; auto props = DynamicCast(wrapper_->GetLayoutProperty()); auto scale = props->GetLayoutConstraint()->scaleProperty; @@ -122,11 +122,9 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) auto rowsTemplate = props->GetRowsTemplate().value_or("1fr"); auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr"); if (axis_ == Axis::VERTICAL) { - cross = ParseTemplateArgs( - WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, info_->childrenCount_); + cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, itemCnt_); } else { - cross = ParseTemplateArgs( - WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, info_->childrenCount_); + cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, itemCnt_); } if (cross.second) { crossGap_ = 0.0f; @@ -153,7 +151,7 @@ void WaterFlowSWLayout::CheckReset() if (updateIdx != -1) { if (info_->ItemInView(updateIdx)) { info_->align_ = ScrollAlign::START; - info_->jumpIndex_ = info_->startIndex_; + info_->jumpIndex_ = std::min(info_->startIndex_, itemCnt_ - 1); } else { // this can disable RecoverBack / RecoverFront when the updated item is encountered info_->idxToLane_.erase(updateIdx); @@ -186,12 +184,15 @@ void WaterFlowSWLayout::ApplyDelta(float delta) // positive offset is scrolling upwards FillFront(0.0f, info_->StartIndex() - 1, 0); } else { - FillBack(mainLen_, info_->EndIndex() + 1, info_->childrenCount_ - 1); + FillBack(mainLen_, info_->EndIndex() + 1, itemCnt_ - 1); } } void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) { + if (itemCnt_ == 0) { + return; + } if (targetIdx < info_->startIndex_) { FillFront(-FLT_MAX, info_->startIndex_ - 1, targetIdx); } else if (targetIdx > info_->endIndex_) { @@ -203,7 +204,8 @@ void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) using lanePos = std::pair; void WaterFlowSWLayout::FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx) { - maxChildIdx = std::min(maxChildIdx, info_->childrenCount_ - 1); + idx = std::max(idx, 0); + maxChildIdx = std::min(maxChildIdx, itemCnt_ - 1); if (info_->idxToLane_.count(idx)) { RecoverBack(viewportBound, idx, maxChildIdx); } @@ -241,6 +243,7 @@ struct MaxHeapCmp { } // namespace void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minChildIdx) { + idx = std::min(itemCnt_ - 1, idx); minChildIdx = std::max(minChildIdx, 0); if (info_->idxToLane_.count(idx)) { RecoverFront(viewportBound, idx, minChildIdx); @@ -383,8 +386,8 @@ ScrollAlign WaterFlowSWLayout::ParseAutoAlign(int32_t jumpIdx, bool inView) void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) { - if (jumpIdx == -1) { - jumpIdx = info_->childrenCount_ - 1; + if (jumpIdx == LAST_ITEM) { + jumpIdx = itemCnt_ - 1; } overScroll_ = false; @@ -420,7 +423,7 @@ void WaterFlowSWLayout::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_)); } else { info_->ResetBeforeJump(0.0f); - FillBack(mainLen_, jumpIdx, info_->childrenCount_ - 1); + FillBack(mainLen_, jumpIdx, itemCnt_ - 1); } break; } @@ -439,7 +442,7 @@ void WaterFlowSWLayout::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) lane.items_.push_back({ jumpIdx, itemH }); FillFront(0.0f, jumpIdx - 1, 0); - FillBack(mainLen_, jumpIdx + 1, info_->childrenCount_ - 1); + FillBack(mainLen_, jumpIdx + 1, itemCnt_ - 1); } break; } diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index b095c581b97..4b0a5ff724c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -133,6 +133,7 @@ private: LayoutWrapper* wrapper_ {}; RefPtr info_; + int32_t itemCnt_ = 0; Axis axis_ {}; std::vector itemCrossSize_; float mainLen_ = 0.0f; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index b516c46ebdc..d3431075c18 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -167,6 +167,8 @@ public: */ void Sync(float mainSize, bool overScroll); + int32_t childrenCount_ = 0; + float currentOffset_ = 0.0f; float prevOffset_ = 0.0f; // 0.0f until itemEnd_ is true diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index 39dd98ba4dd..ce7a55cbc7e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -125,7 +125,6 @@ public: int32_t startIndex_ = 0; int32_t endIndex_ = -1; int32_t footerIndex_ = -1; - int32_t childrenCount_ = 0; float lastMainSize_ = 0.0f; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 3d5fa149c55..81664c3f128 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -586,4 +586,13 @@ void WaterFlowPattern::SetLayoutMode(LayoutMode mode) layoutInfo_->footerIndex_ = 0; } } + +int32_t WaterFlowPattern::GetChildrenCount() const +{ + auto host = GetHost(); + if (host) { + return host->GetTotalChildCount(); + } + return 0; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index 5d42f6bf5a9..af6ad73b1d0 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -86,10 +86,7 @@ public: return layoutInfo_->endIndex_; } - int32_t GetChildrenCount() const - { - return layoutInfo_->childrenCount_; - } + int32_t GetChildrenCount() const; float GetTotalOffset() const override { -- Gitee From a7c301d04021d05de38ac9c0d64955afd1ec6ab1 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Fri, 26 Apr 2024 14:30:26 +0800 Subject: [PATCH 19/31] resolve conflicts Signed-off-by: Tianer Zhou Change-Id: I6d880d085bc1c7b4528111394db1c92c0c474ad3 --- .../engine/jsEnumStyle.js | 6 ++ .../jsview/js_water_flow.cpp | 36 ++++++---- .../sliding_window/water_flow_sw_layout.h | 4 +- .../waterflow/water_flow_layout_info_base.h | 2 +- .../pattern/waterflow/water_flow_pattern.cpp | 18 ++++- .../pattern/waterflow/water_flow_pattern.h | 5 +- .../water_flow_segment_layout_test.cpp | 72 +++++++++---------- 7 files changed, 83 insertions(+), 60 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js b/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js index 8199e2b2883..5034c937c63 100644 --- a/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js +++ b/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js @@ -2487,6 +2487,12 @@ class WaterFlowSections { } } +var WaterFlowLayoutMode; +(function (WaterFlowLayoutMode) { + WaterFlowLayoutMode[WaterFlowLayoutMode["TOP_DOWN"] = 0] = "TOP_DOWN"; + WaterFlowLayoutMode[WaterFlowLayoutMode["SLIDING_WINDOW"] = 1] = "SLIDING_WINDOW"; +})(WaterFlowLayoutMode || (WaterFlowLayoutMode = {})); + class ChildrenMainSizeParamError extends Error { constructor(message, code) { super(message); diff --git a/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp b/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp index 10b286c5d63..221de96ce90 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp @@ -100,6 +100,26 @@ void ParseSections( } waterFlowSections->ChangeData(0, waterFlowSections->GetSectionInfo().size(), newSections); } + +void ParseScroller(const JSRef& obj) +{ + auto scroller = obj->GetProperty("scroller"); + if (scroller->IsObject()) { + auto* jsScroller = JSRef::Cast(scroller)->Unwrap(); + CHECK_NULL_VOID(jsScroller); + jsScroller->SetInstanceId(Container::CurrentId()); + auto positionController = WaterFlowModel::GetInstance()->CreateScrollController(); + jsScroller->SetController(positionController); + + // Init scroll bar proxy. + auto proxy = jsScroller->GetScrollBarProxy(); + if (!proxy) { + proxy = WaterFlowModel::GetInstance()->CreateScrollBarProxy(); + jsScroller->SetScrollBarProxy(proxy); + } + WaterFlowModel::GetInstance()->SetScroller(positionController, proxy); + } +} } // namespace void UpdateWaterFlowSections(const JSCallbackInfo& args, const JSRef& sections) @@ -159,22 +179,8 @@ void JSWaterFlow::Create(const JSCallbackInfo& args) } WaterFlowModel::GetInstance()->SetLayoutMode(mode); - auto scroller = obj->GetProperty("scroller"); - if (scroller->IsObject()) { - auto* jsScroller = JSRef::Cast(scroller)->Unwrap(); - CHECK_NULL_VOID(jsScroller); - jsScroller->SetInstanceId(Container::CurrentId()); - auto positionController = WaterFlowModel::GetInstance()->CreateScrollController(); - jsScroller->SetController(positionController); + ParseScroller(obj); - // Init scroll bar proxy. - auto proxy = jsScroller->GetScrollBarProxy(); - if (!proxy) { - proxy = WaterFlowModel::GetInstance()->CreateScrollBarProxy(); - jsScroller->SetScrollBarProxy(proxy); - } - WaterFlowModel::GetInstance()->SetScroller(positionController, proxy); - } auto sections = obj->GetProperty("sections"); auto footerObject = obj->GetProperty("footer"); if (sections->IsObject()) { diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h index 4b0a5ff724c..d3012f1ca4e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h @@ -88,7 +88,7 @@ private: */ float FillFrontHelper(const RefPtr& props, int32_t idx, size_t laneIdx); /** - * @brief Clear items above the viewport. + * @brief Clear items above the viewport. * Iterate by index to keep item range continuous. */ void ClearFront(); @@ -111,7 +111,7 @@ private: float FillBackHelper(const RefPtr& props, int32_t idx, size_t laneIdx); /** * @brief Clear items below the viewport. - * + * * @param bound of the viewport */ void ClearBack(float bound); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index ce7a55cbc7e..66b872b626f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -47,7 +47,7 @@ public: /** * @brief Get which cross-axis lane the item is in. * - * @param itemIndex + * @param itemIndex * @return lane index */ virtual int32_t GetCrossIndex(int32_t itemIndex) const = 0; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 81664c3f128..da627ddca50 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -391,6 +391,14 @@ Rect WaterFlowPattern::GetItemRect(int32_t index) const itemGeometry->GetFrameRect().Width(), itemGeometry->GetFrameRect().Height()); } +RefPtr WaterFlowPattern::GetSections() const +{ + if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + return nullptr; + } + return sections_; +} + RefPtr WaterFlowPattern::GetOrCreateWaterFlowSections() { if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { @@ -423,8 +431,11 @@ RefPtr WaterFlowPattern::GetOrCreateWaterFlowSections() } void WaterFlowPattern::OnSectionChanged(int32_t start) -{ +{ // SlidingWindow mode should never reach this callback + if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + return; + } auto info = DynamicCast(layoutInfo_); CHECK_NULL_VOID(info); auto host = GetHost(); @@ -455,8 +466,11 @@ void WaterFlowPattern::OnSectionChangedNow(int32_t start) void WaterFlowPattern::ResetSections() { - layoutInfo_->Reset(); sections_.Reset(); + if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + return; + } + layoutInfo_->Reset(); MarkDirtyNodeSelf(); } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index af6ad73b1d0..fa9c05378bf 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -124,10 +124,7 @@ public: void OnRestoreInfo(const std::string& restoreInfo) override; Rect GetItemRect(int32_t index) const override; - RefPtr GetSections() const - { - return sections_; - } + RefPtr GetSections() const; RefPtr GetOrCreateWaterFlowSections(); void ResetSections(); diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp index 7e1c146a6ff..7dcc76c9130 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -16,7 +16,7 @@ #include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" #include "core/components_ng/pattern/waterflow/water_flow_item_pattern.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info->h" #include "core/components_ng/property/calc_length.h" #include "core/components_ng/property/measure_property.h" @@ -1666,14 +1666,14 @@ HWTEST_F(WaterFlowSegmentTest, Replace003, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-2000.0f); - EXPECT_EQ(info.segmentStartPos_[2], 613.0f); - EXPECT_EQ(info.currentOffset_, -2000.0f); - EXPECT_EQ(info.startIndex_, 20); - EXPECT_EQ(info.endIndex_, 26); - EXPECT_EQ(info.itemInfos_.size(), 27); - EXPECT_EQ(info.endPosArray_.size(), 26); + EXPECT_EQ(info->segmentStartPos_[2], 613.0f); + EXPECT_EQ(info->currentOffset_, -2000.0f); + EXPECT_EQ(info->startIndex_, 20); + EXPECT_EQ(info->endIndex_, 26); + EXPECT_EQ(info->itemInfos_.size(), 27); + EXPECT_EQ(info->endPosArray_.size(), 26); AddItems(10); frameNode_->ChildrenUpdatedFrom(37); @@ -1681,27 +1681,27 @@ HWTEST_F(WaterFlowSegmentTest, Replace003, TestSize.Level1) newSection[0].itemsCount = 40; secObj->ChangeData(2, 1, newSection); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); - EXPECT_EQ(info.itemInfos_.size(), 27); - EXPECT_EQ(info.items_[2].at(0).size(), 20); - EXPECT_EQ(info.endPosArray_.size(), 26); - EXPECT_EQ(info.segmentStartPos_[2], 613.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 3); - EXPECT_EQ(info.segmentTails_.size(), 3); - EXPECT_EQ(info.segmentTails_[2], 46); + EXPECT_EQ(info->itemInfos_.size(), 27); + EXPECT_EQ(info->items_[2].at(0).size(), 20); + EXPECT_EQ(info->endPosArray_.size(), 26); + EXPECT_EQ(info->segmentStartPos_[2], 613.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 3); + EXPECT_EQ(info->segmentTails_.size(), 3); + EXPECT_EQ(info->segmentTails_[2], 46); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.currentOffset_, -2000.0f); - EXPECT_EQ(info.startIndex_, 20); + EXPECT_EQ(info->currentOffset_, -2000.0f); + EXPECT_EQ(info->startIndex_, 20); EXPECT_EQ(GetChildY(frameNode_, 20), -61.0f); AddItems(7); frameNode_->ChildrenUpdatedFrom(7); secObj->ChangeData(1, 1, ADD_SECTION_7); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); - EXPECT_EQ(info.itemInfos_.size(), 14); - EXPECT_EQ(info.segmentTails_[1], 13); + EXPECT_EQ(info->itemInfos_.size(), 14); + EXPECT_EQ(info->segmentTails_[1], 13); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.startIndex_, 20); + EXPECT_EQ(info->startIndex_, 20); EXPECT_EQ(GetChildY(frameNode_, 20), -61.0f); } @@ -1723,7 +1723,7 @@ HWTEST_F(WaterFlowSegmentTest, Replace004, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_9); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); AddItems(100); frameNode_->ChildrenUpdatedFrom(6); @@ -1731,20 +1731,20 @@ HWTEST_F(WaterFlowSegmentTest, Replace004, TestSize.Level1) newSection[0].itemsCount = 106; secObj->ChangeData(0, 1, newSection); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); - EXPECT_EQ(info.itemInfos_.size(), 6); - EXPECT_EQ(info.items_[0].at(0).size(), 2); - EXPECT_EQ(info.endPosArray_.size(), 2); - EXPECT_EQ(info.segmentStartPos_[0], 0.0f); - EXPECT_EQ(info.segmentStartPos_.size(), 1); - EXPECT_EQ(info.segmentTails_.size(), 1); - EXPECT_EQ(info.segmentTails_[0], 105); + EXPECT_EQ(info->itemInfos_.size(), 6); + EXPECT_EQ(info->items_[0].at(0).size(), 2); + EXPECT_EQ(info->endPosArray_.size(), 2); + EXPECT_EQ(info->segmentStartPos_[0], 0.0f); + EXPECT_EQ(info->segmentStartPos_.size(), 1); + EXPECT_EQ(info->segmentTails_.size(), 1); + EXPECT_EQ(info->segmentTails_[0], 105); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.endIndex_, 17); - EXPECT_EQ(info.items_[0].at(0).size(), 6); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->endIndex_, 17); + EXPECT_EQ(info->items_[0].at(0).size(), 6); UpdateCurrentOffset(-10000.0f); - EXPECT_EQ(info.currentOffset_, -3000.0f); + EXPECT_EQ(info->currentOffset_, -3000.0f); for (int i = 0; i < 100; ++i) { frameNode_->RemoveChildAtIndex(10); } @@ -1752,11 +1752,11 @@ HWTEST_F(WaterFlowSegmentTest, Replace004, TestSize.Level1) newSection[0].itemsCount = 10; secObj->ChangeData(0, 1, newSection); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); - EXPECT_EQ(info.segmentTails_[0], 9); + EXPECT_EQ(info->segmentTails_[0], 9); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.endIndex_, 9); - EXPECT_EQ(info.items_[0].at(0).size(), 4); + EXPECT_EQ(info->currentOffset_, 0.0f); + EXPECT_EQ(info->endIndex_, 9); + EXPECT_EQ(info->items_[0].at(0).size(), 4); } /** -- Gitee From 1abdc1ce8a7213dd6e7fe0e60d3913177f481520 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 29 Apr 2024 19:55:22 +0800 Subject: [PATCH 20/31] fix change template jump Signed-off-by: Tianer Zhou Change-Id: I83eb88a13e1dddb285a8b21e40b5a18ac99c838c --- .../water_flow_layout_info_sw.cpp | 12 +++++++ .../water_flow_layout_info_sw.h | 5 +++ .../sliding_window/water_flow_sw_layout.cpp | 8 ++--- .../waterflow/water_flow_sw_layout_test.cpp | 31 +++++++++++++++++++ .../waterflow/water_flow_top_down_test.cpp | 2 +- 5 files changed, 51 insertions(+), 7 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index adf76ddd38a..53949ff0757 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -304,4 +304,16 @@ std::string WaterFlowLayoutInfoSW::Lane::ToString() const res += "}"; return res; } + +bool WaterFlowLayoutInfoSW::ItemCloseToView(int32_t idx) const +{ + if (lanes_.empty() || + std::all_of(lanes_.begin(), lanes_.end(), [](const Lane& lane) { return lane.items_.empty(); })) { + return false; + } + int32_t startIdx = StartIndex(); + int32_t endIdx = EndIndex(); + using std::abs, std::min; + return min(abs(idx - endIdx), abs(idx - startIdx)) < endIdx - startIdx + 1; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index d04e6ce374c..61a2cdd9c4a 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -113,6 +113,11 @@ public: { return !lanes_.empty() && idx >= StartIndex() && idx <= EndIndex(); } + /** + * @param idx of the item. + * @return true the item is approximately within 1 full-viewport distance. + */ + bool ItemCloseToView(int32_t idx) const; /** * @return maximum end position of items in lanes_. diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index fbd4bce1ba5..99cc6185d12 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -396,16 +396,12 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) align = ParseAutoAlign(jumpIdx, inView); } - // If the item is within 1 full-viewport distance (approximately), we consider it close. - // Then we simply scroll to it instead of triggering a reset/jump, which would change the layout. - int32_t cntInView = info_->endIndex_ - info_->startIndex_ + 1; - bool closeToView = - jumpIdx > info_->endIndex_ ? jumpIdx - info_->endIndex_ < cntInView : info_->startIndex_ - jumpIdx < cntInView; + // If item is close, we simply scroll to it instead of triggering a reset/jump, which would change the layout. + bool closeToView = info_->ItemCloseToView(jumpIdx); if (closeToView) { MeasureToTarget(jumpIdx); } Jump(jumpIdx, align, inView || closeToView); - if (!NearZero(info_->delta_)) { MeasureOnOffset(info_->delta_); } else { diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index 6dcb5e80164..5c62daa8b4c 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -85,4 +85,35 @@ HWTEST_F(WaterFlowSWTest, Jump001, TestSize.Level1) EXPECT_EQ(info_->startIndex_, 5); EXPECT_EQ(info_->endIndex_, 9); } + +/** + * @tc.name: ChangeTemplate001 + * @tc.desc: waterFlow change lane count + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, ChangeTemplate001, TestSize.Level1) +{ + CreateWithItem([](WaterFlowModelNG model) { + ViewAbstract::SetWidth(CalcLength(600.0f)); + ViewAbstract::SetHeight(CalcLength(200.f)); + model.SetColumnsTemplate("1fr 1fr 1fr"); + }); + UpdateCurrentOffset(-300.0f); + auto info = pattern_->layoutInfo_; + EXPECT_EQ(info->startIndex_, 5); + EXPECT_EQ(info->endIndex_, 9); + EXPECT_EQ(GetChildOffset(frameNode_, 5), OffsetF(200.0f, -100.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 6), OffsetF(400.0f, -100.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 7), OffsetF(0.0f, 0.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 8), OffsetF(400.0f, 0.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 9), OffsetF(200.0f, 100.0f)); + layoutProperty_->UpdateColumnsTemplate("1fr 1fr"); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info->startIndex_, 5); + EXPECT_EQ(info->endIndex_, 8); + EXPECT_EQ(GetChildOffset(frameNode_, 5), OffsetF(0.0f, -100.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 6), OffsetF(300.0f, -100.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 7), OffsetF(300.0f, 0.0f)); + EXPECT_EQ(GetChildOffset(frameNode_, 8), OffsetF(0.0f, 100.0f)); +} } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index 5d88be0f642..85201c92d12 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -100,7 +100,7 @@ HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest004, TestSize.Level1) EXPECT_EQ(pattern_->layoutInfo_->endIndex_, resetFrom); info->Reset(resetFrom - 1); - EXPECT_EQ(pattern_->layoutInfo_->endIndex_, -1); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, resetFrom); } /** -- Gitee From 61922266d783e24065b39982a515990731c4fea0 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 30 Apr 2024 16:47:50 +0800 Subject: [PATCH 21/31] disable scrollTo and fix scrollToEdge Signed-off-by: Tianer Zhou Change-Id: Id9f9276f6346b55a8177712c6c2da58737a1e010 --- .../sliding_window/water_flow_sw_layout.cpp | 3 + .../pattern/waterflow/water_flow_pattern.cpp | 19 ++- .../pattern/waterflow/water_flow_pattern.h | 9 ++ .../waterflow/water_flow_top_down_test.cpp | 113 ++++++++++++++++++ 4 files changed, 143 insertions(+), 1 deletion(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index 99cc6185d12..f48ad6bcf19 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -388,6 +388,9 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) { if (jumpIdx == LAST_ITEM) { jumpIdx = itemCnt_ - 1; + } else if (jumpIdx == itemCnt_ && info_->footerIndex_ == 0) { + // offset to footer + info_->delta_ = -Infinity(); } overScroll_ = false; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index da627ddca50..efbfc6a652a 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -347,7 +347,7 @@ void WaterFlowPattern::ScrollPage(bool reverse, bool smooth) if (smooth) { float distance = reverse ? mainContentSize : -mainContentSize; float position = layoutInfo_->offset() + distance; - AnimateTo(-position, -1, nullptr, true); + ScrollablePattern::AnimateTo(-position, -1, nullptr, true); } else { UpdateCurrentOffset(reverse ? mainContentSize : -mainContentSize, SCROLL_FROM_JUMP); } @@ -547,6 +547,23 @@ void WaterFlowPattern::OnAnimateStop() MarkDirtyNodeSelf(); } +void WaterFlowPattern::AnimateTo( + float position, float duration, const RefPtr& curve, bool smooth, bool canOverScroll) +{ + if (layoutMode_ == WaterFlowLayoutMode::SLIDING_WINDOW) { + return; + } + ScrollablePattern::AnimateTo(position, duration, curve, smooth, canOverScroll); +} + +void WaterFlowPattern::ScrollTo(float position) +{ + if (layoutMode_ == WaterFlowLayoutMode::SLIDING_WINDOW) { + return; + } + ScrollablePattern::ScrollTo(position); +} + bool WaterFlowPattern::NeedRender() { auto host = GetHost(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index fa9c05378bf..bed95993abb 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -100,6 +100,15 @@ public: void SetAccessibilityAction(); void OnAnimateStop() override; + /** + * @brief LayoutMode::SLIDING_WINDOW doesn't support scrollTo and animateTo + */ + void ScrollTo(float position) override; + /** + * @brief LayoutMode::SLIDING_WINDOW doesn't support animateTo + */ + void AnimateTo( + float position, float duration, const RefPtr& curve, bool smooth, bool canOverScroll) override; void ScrollPage(bool reverse, bool smooth = false) override; diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index 85201c92d12..aa2527027e2 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -192,4 +192,117 @@ HWTEST_F(WaterFlowTestNg, UpdateCurrentOffset003, TestSize.Level1) EXPECT_EQ(pattern_->layoutInfo_->firstIdx(), 19); EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 19); } + +/** + * @tc.name: onWillScrollAndOnDidScroll001 + * @tc.desc: Test onScroll event + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, OnWillScrollAndOnDidScroll001, TestSize.Level1) +{ + bool isOnScrollCallBack = false; + bool isOnWillScrollCallBack = false; + bool isOnDidScrollCallBack = false; + + CalcDimension offsetY; + ScrollState scrollState = ScrollState::IDLE; + auto onScroll = [&offsetY, &scrollState, &isOnScrollCallBack](CalcDimension offset, ScrollState state) { + offsetY = offset; + scrollState = state; + isOnScrollCallBack = true; + }; + Dimension willScrollOffset; + ScrollState willScrollState; + auto onWillScroll = [&willScrollOffset, &willScrollState, &isOnWillScrollCallBack]( + Dimension offset, ScrollState state) { + willScrollOffset = offset; + willScrollState = state; + isOnWillScrollCallBack = true; + }; + Dimension didScrollOffset; + ScrollState didScrollState = ScrollState::IDLE; + auto onDidScroll = [&didScrollOffset, &didScrollState, &isOnDidScrollCallBack]( + Dimension offset, ScrollState state) { + didScrollOffset = offset; + didScrollState = state; + isOnDidScrollCallBack = true; + }; + + CreateWithItem([onScroll](WaterFlowModelNG model) { model.SetOnScroll(onScroll); }); + eventHub_->SetOnWillScroll(std::move(onWillScroll)); + eventHub_->SetOnDidScroll(std::move(onDidScroll)); + + /** + * @tc.steps: step1. finger moves down at top + * @tc.expected: Trigger onWillScroll and onDidScroll with SCROLL state + */ + pattern_->ScrollTo(ITEM_HEIGHT * 5); + FlushLayoutTask(frameNode_); + EXPECT_TRUE(isOnScrollCallBack); + EXPECT_TRUE(isOnWillScrollCallBack); + EXPECT_TRUE(isOnDidScrollCallBack); + EXPECT_EQ(offsetY.Value(), ITEM_HEIGHT * 5); + EXPECT_EQ(willScrollOffset.Value(), ITEM_HEIGHT * 5); + EXPECT_EQ(didScrollOffset.Value(), ITEM_HEIGHT * 5); + EXPECT_EQ(scrollState, willScrollState); + EXPECT_EQ(scrollState, didScrollState); +} + +/** + * @tc.name: onScroll + * @tc.desc: Test onScroll event + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, OnWillScrollAndOnDidScroll002, TestSize.Level1) +{ + bool isOnScrollCallBack = false; + bool isOnWillScrollCallBack = false; + bool isOnDidScrollCallBack = false; + + CalcDimension offsetY; + ScrollState scrollState = ScrollState::IDLE; + auto onScroll = [&offsetY, &scrollState, &isOnScrollCallBack](CalcDimension offset, ScrollState state) { + offsetY = offset; + scrollState = state; + isOnScrollCallBack = true; + }; + Dimension willScrollOffset; + ScrollState willScrollState; + auto onWillScroll = [&willScrollOffset, &willScrollState, &isOnWillScrollCallBack]( + Dimension offset, ScrollState state) { + willScrollOffset = offset; + willScrollState = state; + isOnWillScrollCallBack = true; + }; + Dimension didScrollOffset; + ScrollState didScrollState = ScrollState::IDLE; + auto onDidScroll = [&didScrollOffset, &didScrollState, &isOnDidScrollCallBack]( + Dimension offset, ScrollState state) { + didScrollOffset = offset; + didScrollState = state; + isOnDidScrollCallBack = true; + }; + + CreateWithItem([onScroll](WaterFlowModelNG model) { + model.SetOnScroll(onScroll); + model.SetLayoutDirection(FlexDirection::ROW); + }); + eventHub_->SetOnWillScroll(std::move(onWillScroll)); + eventHub_->SetOnDidScroll(std::move(onDidScroll)); + + /** + * @tc.steps: step1. finger moves down at top + * @tc.expected: Trigger onScroll with SCROLL state + */ + pattern_->ScrollTo(ITEM_HEIGHT * 5); + FlushLayoutTask(frameNode_); + EXPECT_TRUE(isOnScrollCallBack); + EXPECT_TRUE(isOnWillScrollCallBack); + EXPECT_TRUE(isOnDidScrollCallBack); + EXPECT_EQ(offsetY.Value(), ITEM_HEIGHT * 5); + EXPECT_EQ(willScrollOffset.Value(), ITEM_HEIGHT * 5); + EXPECT_EQ(didScrollOffset.Value(), ITEM_HEIGHT * 5); + EXPECT_EQ(scrollState, willScrollState); + EXPECT_EQ(scrollState, didScrollState); +} } // namespace OHOS::Ace::NG -- Gitee From 0abaeaef1eb1da3ae555d271c6879fe4cd9591cd Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Tue, 30 Apr 2024 19:54:08 +0800 Subject: [PATCH 22/31] fix children update reset Signed-off-by: Tianer Zhou Change-Id: I30ce40dd8f27e4357d8e93d3e69f36a802505358 --- .../water_flow_layout_info_sw.cpp | 22 +++++++ .../water_flow_layout_info_sw.h | 4 +- .../sliding_window/water_flow_sw_layout.cpp | 43 +++++++------- .../waterflow/water_flow_sw_layout_test.cpp | 58 +++++++++++++++++++ 4 files changed, 105 insertions(+), 22 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 53949ff0757..39494041bfb 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -316,4 +316,26 @@ bool WaterFlowLayoutInfoSW::ItemCloseToView(int32_t idx) const using std::abs, std::min; return min(abs(idx - endIdx), abs(idx - startIdx)) < endIdx - startIdx + 1; } + +void WaterFlowLayoutInfoSW::ClearDataFrom(int32_t idx, float mainGap) +{ + for (auto it = idxToLane_.begin(); it != idxToLane_.end();) { + if (it->first >= idx) { + it = idxToLane_.erase(it); // Erase and get the iterator to the next element + } else { + ++it; // Move to the next element + } + } + for (auto& lane : lanes_) { + while (!lane.items_.empty()) { + if (lane.items_.back().idx >= idx) { + lane.endPos -= lane.items_.back().mainSize + mainGap; + lane.items_.pop_back(); + } else { + lane.endPos = std::max(lane.endPos, lane.startPos); + break; + } + } + } +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 61a2cdd9c4a..1538f955219 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -128,10 +128,12 @@ public: */ float StartPos() const; + void ClearDataFrom(int32_t idx, float mainGap); + struct Lane; std::vector lanes_; // mapping of all items previously or currently in lanes_. - std::unordered_map idxToLane_; + std::unordered_map idxToLane_; float delta_ = 0.0f; float totalOffset_ = 0.0f; // record total offset when continuously scrolling. Reset when jumped diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index f48ad6bcf19..af964ec310f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -144,20 +144,21 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) void WaterFlowSWLayout::CheckReset() { int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated(); - if (info_->footerIndex_ == 0) { - // convert children node index to item index - --updateIdx; - } - if (updateIdx != -1) { - if (info_->ItemInView(updateIdx)) { - info_->align_ = ScrollAlign::START; - info_->jumpIndex_ = std::min(info_->startIndex_, itemCnt_ - 1); - } else { - // this can disable RecoverBack / RecoverFront when the updated item is encountered - info_->idxToLane_.erase(updateIdx); - } - wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1); + if (updateIdx == -1) { + return; + } + if (info_->footerIndex_ == 0 && updateIdx == 0) { + // footer updated, no need to reset or clear cache + return; + } + // convert children node index to item index + updateIdx -= (info_->footerIndex_ + 1); + info_->ClearDataFrom(updateIdx, mainGap_); + if (updateIdx <= info_->startIndex_) { + info_->jumpIndex_ = std::min(info_->startIndex_, itemCnt_ - 1); + info_->align_ = ScrollAlign::START; } + wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1); } void WaterFlowSWLayout::MeasureOnOffset(float delta) @@ -389,7 +390,7 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) if (jumpIdx == LAST_ITEM) { jumpIdx = itemCnt_ - 1; } else if (jumpIdx == itemCnt_ && info_->footerIndex_ == 0) { - // offset to footer + // jump to footer info_->delta_ = -Infinity(); } overScroll_ = false; @@ -475,15 +476,15 @@ void WaterFlowSWLayout::AdjustOverScroll() if (overScroll_) { return; } - if (Positive(minStart)) { + int32_t startIdx = info_->StartIndex(); + if (startIdx == 0 && Positive(minStart)) { ApplyDelta(-minStart); - } else if (LessNotEqual(maxEnd, mainLen_)) { - bool reachedTop = info_->StartIndex() == 0 && NearZero(info_->DistanceToTop(0, mainGap_)); - if (reachedTop) { - // no room to adjust - return; + } else if (info_->EndIndex() == itemCnt_ - 1 && LessNotEqual(maxEnd, mainLen_)) { + float delta = mainLen_ - maxEnd; + if (startIdx == 0) { + delta = std::min(-minStart, delta); } - ApplyDelta(mainLen_ - maxEnd); + ApplyDelta(delta); } } diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index 5c62daa8b4c..8385eadf3e7 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -53,6 +53,64 @@ HWTEST_F(WaterFlowSWTest, Regular001, TestSize.Level1) EXPECT_EQ(info_->endIndex_, 4); } +/** + * @tc.name: Reset001 + * @tc.desc: waterFlow children update + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, Reset001, TestSize.Level1) +{ + CreateWithItem([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetFooter(GetDefaultHeaderBuilder()); + }); + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->startIndex_, 0); + EXPECT_EQ(info_->endIndex_, 9); + for (int i = 0; i < 5; i++) { + frameNode_->RemoveChildAtIndex(6); + } + frameNode_->ChildrenUpdatedFrom(6); + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->startIndex_, 0); + EXPECT_EQ(info_->endIndex_, 4); + EXPECT_FALSE(info_->idxToLane_.count(5)); + EXPECT_EQ(GetChildY(frameNode_, 1), 0.0f); + EXPECT_EQ(GetChildY(frameNode_, 5), 200.0f); + EXPECT_EQ(GetChildY(frameNode_, 0), 400.0f); +} + +/** + * @tc.name: Reset002 + * @tc.desc: waterFlow children update before startIdx + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, Reset002, TestSize.Level1) +{ + CreateWithItem([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetFooter(GetDefaultHeaderBuilder()); + CreateItem(100); + }); + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->startIndex_, 99); + EXPECT_EQ(info_->endIndex_, 109); + for (int i = 0; i < 5; i++) { + frameNode_->RemoveChildAtIndex(6); + } + frameNode_->ChildrenUpdatedFrom(6); + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->startIndex_, 95); + EXPECT_EQ(info_->endIndex_, 104); + EXPECT_TRUE(info_->offsetEnd_); + EXPECT_EQ(GetChildY(frameNode_, 95), -150.0f); + EXPECT_EQ(GetChildY(frameNode_, 0), 750.0f); +} + /** * @tc.name: Jump001 * @tc.desc: waterFlow jump -- Gitee From 6d6096721a18224f3524c38e514ded1db3bdbbba Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 1 May 2024 11:21:09 +0800 Subject: [PATCH 23/31] fix offsetEnd with footer Signed-off-by: Tianer Zhou Change-Id: I206a470b8fed5677044f9cbb9af5f277fcb3fa73 --- .../layout/sliding_window/water_flow_layout_info_sw.cpp | 9 ++++++--- .../layout/sliding_window/water_flow_layout_info_sw.h | 1 + .../layout/sliding_window/water_flow_sw_layout.cpp | 8 ++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 39494041bfb..8873d70c07c 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -33,8 +33,11 @@ void WaterFlowLayoutInfoSW::Sync(int32_t itemCnt, float mainSize, float mainGap) itemStart_ = startIndex_ == 0 && NonNegative(DistanceToTop(0, mainGap_)); itemEnd_ = endIndex_ == itemCnt - 1; - offsetEnd_ = itemEnd_ && std::all_of(lanes_.begin(), lanes_.end(), - [mainSize](const Lane& lane) { return LessOrEqual(lane.endPos, mainSize); }); + if (!itemEnd_) { + footerHeight_ = 0.0f; + } + offsetEnd_ = itemEnd_ && LessOrEqual(EndPos() + footerHeight_, mainSize); + synced_ = true; } @@ -160,7 +163,7 @@ bool WaterFlowLayoutInfoSW::ReachEnd(float prevPos) const return false; } float prevEndPos = EndPos() - (totalOffset_ - prevPos); - return GreatNotEqual(prevEndPos, lastMainSize_); + return GreatNotEqual(prevEndPos + footerHeight_, lastMainSize_); } float WaterFlowLayoutInfoSW::GetContentHeight() const diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 1538f955219..2a67c045ac5 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -138,6 +138,7 @@ public: float delta_ = 0.0f; float totalOffset_ = 0.0f; // record total offset when continuously scrolling. Reset when jumped float mainGap_ = 0.0f; // update this at the end of a layout + float footerHeight_ = 0.0f; bool synced_ = false; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp index af964ec310f..e3e9740ce78 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp @@ -468,9 +468,9 @@ void WaterFlowSWLayout::AdjustOverScroll() float maxEnd = info_->EndPos(); float minStart = info_->StartPos(); - if (LessNotEqual(maxEnd, mainLen_) && info_->footerIndex_ == 0) { - float footerMainLen = WaterFlowLayoutUtils::MeasureFooter(wrapper_, axis_); - maxEnd += mainGap_ + footerMainLen; + if (LessOrEqual(maxEnd, mainLen_) && info_->footerIndex_ == 0) { + info_->footerHeight_ = WaterFlowLayoutUtils::MeasureFooter(wrapper_, axis_); + maxEnd += info_->footerHeight_; } if (overScroll_) { @@ -506,7 +506,7 @@ void WaterFlowSWLayout::LayoutFooter(const OffsetF& paddingOffset, bool reverse) auto footer = wrapper_->GetOrCreateChildByIndex(0); float mainPos = endPos + mainGap_; if (reverse) { - mainPos = mainLen_ - footer->GetGeometryNode()->GetMarginFrameSize().MainSize(axis_) - mainPos; + mainPos = mainLen_ - info_->footerHeight_ - mainPos; } footer->GetGeometryNode()->SetMarginFrameOffset( (axis_ == Axis::VERTICAL) ? OffsetF(0.0f, mainPos) + paddingOffset : OffsetF(mainPos, 0.0f) + paddingOffset); -- Gitee From dd899d5e02a477e92044cdf6b244e7c4f8004d35 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Wed, 1 May 2024 11:48:53 +0800 Subject: [PATCH 24/31] handle child update Signed-off-by: Tianer Zhou Change-Id: Id7e146cabd7fe15847ae08d5a9f46d61fbe24f17 --- .../waterflow/water_flow_item_node.cpp | 5 +- .../pattern/waterflow/water_flow_pattern.cpp | 2 - .../pattern/waterflow/water_flow_pattern.h | 11 ++- test/unittest/core/pattern/waterflow/BUILD.gn | 2 + .../waterflow/water_flow_regular_test.cpp | 76 +++++++++++++++++++ .../waterflow/water_flow_sw_layout_test.cpp | 63 +++++++++++++-- .../pattern/waterflow/water_flow_test_ng.cpp | 15 +--- .../waterflow/water_flow_top_down_test.cpp | 30 ++++++++ 8 files changed, 179 insertions(+), 25 deletions(-) create mode 100644 test/unittest/core/pattern/waterflow/water_flow_regular_test.cpp diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_item_node.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_item_node.cpp index 4f774b02f41..5b1167003d7 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_item_node.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_item_node.cpp @@ -40,8 +40,9 @@ bool WaterFlowItemNode::RequestParentDirty() parent->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST); auto pattern = parent->GetPattern(); CHECK_NULL_RETURN(pattern, true); - // record index of dirty child, but only when using new Sectioned layout - if (pattern->GetSections() || SystemProperties::WaterFlowUseSegmentedLayout()) { + // record index of dirty child, but only when using new layout + if (pattern->GetLayoutMode() == WaterFlowLayoutMode::SLIDING_WINDOW || pattern->GetSections() || + SystemProperties::WaterFlowUseSegmentedLayout()) { auto idx = parent->GetChildTrueIndex(Claim(this)); if (idx > -1) { parent->ChildrenUpdatedFrom(idx); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index efbfc6a652a..8815cb6ba9e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -26,8 +26,6 @@ #include "core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" namespace OHOS::Ace::NG { -using LayoutMode = WaterFlowLayoutMode; - SizeF WaterFlowPattern::GetContentSize() const { auto host = GetHost(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index bed95993abb..ed1ed03a63f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -20,7 +20,7 @@ #include "core/components_ng/pattern/waterflow/water_flow_accessibility_property.h" #include "core/components_ng/pattern/waterflow/water_flow_content_modifier.h" #include "core/components_ng/pattern/waterflow/water_flow_event_hub.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/pattern/waterflow/water_flow_sections.h" @@ -42,7 +42,12 @@ public: OverScrollOffset GetOverScrollOffset(double delta) const override; void UpdateScrollBarOffset() override; - void SetLayoutMode(WaterFlowLayoutMode mode); + using LayoutMode = WaterFlowLayoutMode; + void SetLayoutMode(LayoutMode mode); + LayoutMode GetLayoutMode() const + { + return layoutMode_; + } RefPtr CreateLayoutAlgorithm() override; @@ -162,7 +167,7 @@ private: bool ScrollToTargetIndex(int32_t index); bool NeedRender(); std::optional targetIndex_; - WaterFlowLayoutMode layoutMode_ = WaterFlowLayoutMode::TOP_DOWN; + LayoutMode layoutMode_ = LayoutMode::TOP_DOWN; RefPtr layoutInfo_ = WaterFlowLayoutInfoBase::Create(layoutMode_); RefPtr sections_; diff --git a/test/unittest/core/pattern/waterflow/BUILD.gn b/test/unittest/core/pattern/waterflow/BUILD.gn index a9a66e4d84b..a1a27c9f6c2 100644 --- a/test/unittest/core/pattern/waterflow/BUILD.gn +++ b/test/unittest/core/pattern/waterflow/BUILD.gn @@ -16,6 +16,7 @@ import("//foundation/arkui/ace_engine/test/unittest/ace_unittest.gni") ace_unittest("water_flow_test_old") { type = "new" sources = [ + "water_flow_regular_test.cpp", "water_flow_test_ng.cpp", "water_flow_top_down_test.cpp", ] @@ -37,6 +38,7 @@ ace_unittest("water_flow_test_sw") { type = "new" defines = [ "TEST_WATER_FLOW_SW" ] sources = [ + "water_flow_regular_test.cpp", "water_flow_sw_layout_test.cpp", "water_flow_test_ng.cpp", ] diff --git a/test/unittest/core/pattern/waterflow/water_flow_regular_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_regular_test.cpp new file mode 100644 index 00000000000..12f61778ffd --- /dev/null +++ b/test/unittest/core/pattern/waterflow/water_flow_regular_test.cpp @@ -0,0 +1,76 @@ +/* + * 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. + */ +#include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" + +namespace OHOS::Ace::NG { +// TEST non-segmented layout + +/** + * @tc.name: OffsetEnd001 + * @tc.desc: Check OffsetEnd value with footer + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, OffsetEnd001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetFooter(GetDefaultHeaderBuilder()); + model.SetRowsGap(Dimension(5.0f)); + CreateItem(30); + }); + pattern_->ScrollToIndex(29, false, ScrollAlign::END); + auto info = pattern_->layoutInfo_; + FlushLayoutTask(frameNode_); + EXPECT_EQ(info->endIndex_, 29); + EXPECT_EQ(GetChildY(frameNode_, 30), 600.0f); + EXPECT_FALSE(info->offsetEnd_); + + UpdateCurrentOffset(-45.0f); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info->endIndex_, 29); + EXPECT_FALSE(info->offsetEnd_); + + UpdateCurrentOffset(-5.0f); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info->endIndex_, 29); + EXPECT_TRUE(info->offsetEnd_); + EXPECT_TRUE(info->ReachEnd(50.0f)); + + UpdateCurrentOffset(1.0f); + FlushLayoutTask(frameNode_); + EXPECT_FALSE(info->offsetEnd_); +} + +/** + * @tc.name: ScrollToEdge001 + * @tc.desc: Test ScrollToEdge func + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, ScrollToEdge001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetFooter(GetDefaultHeaderBuilder()); + model.SetColumnsTemplate("1fr 1fr"); + CreateItem(100); + }); + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + auto info = pattern_->layoutInfo_; + EXPECT_EQ(info->endIndex_, 99); + EXPECT_EQ(GetChildY(frameNode_, 100), 550.0f); + EXPECT_EQ(GetChildOffset(frameNode_, info->footerIndex_), OffsetF(0.0f, 750.0f)); + // scrolled to footer +} +} // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index 8385eadf3e7..19d4793cc0d 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -149,7 +149,7 @@ HWTEST_F(WaterFlowSWTest, Jump001, TestSize.Level1) * @tc.desc: waterFlow change lane count * @tc.type: FUNC */ -HWTEST_F(WaterFlowTestNg, ChangeTemplate001, TestSize.Level1) +HWTEST_F(WaterFlowSWTest, ChangeTemplate001, TestSize.Level1) { CreateWithItem([](WaterFlowModelNG model) { ViewAbstract::SetWidth(CalcLength(600.0f)); @@ -157,9 +157,8 @@ HWTEST_F(WaterFlowTestNg, ChangeTemplate001, TestSize.Level1) model.SetColumnsTemplate("1fr 1fr 1fr"); }); UpdateCurrentOffset(-300.0f); - auto info = pattern_->layoutInfo_; - EXPECT_EQ(info->startIndex_, 5); - EXPECT_EQ(info->endIndex_, 9); + EXPECT_EQ(info_->startIndex_, 5); + EXPECT_EQ(info_->endIndex_, 9); EXPECT_EQ(GetChildOffset(frameNode_, 5), OffsetF(200.0f, -100.0f)); EXPECT_EQ(GetChildOffset(frameNode_, 6), OffsetF(400.0f, -100.0f)); EXPECT_EQ(GetChildOffset(frameNode_, 7), OffsetF(0.0f, 0.0f)); @@ -167,11 +166,63 @@ HWTEST_F(WaterFlowTestNg, ChangeTemplate001, TestSize.Level1) EXPECT_EQ(GetChildOffset(frameNode_, 9), OffsetF(200.0f, 100.0f)); layoutProperty_->UpdateColumnsTemplate("1fr 1fr"); FlushLayoutTask(frameNode_); - EXPECT_EQ(info->startIndex_, 5); - EXPECT_EQ(info->endIndex_, 8); + EXPECT_EQ(info_->startIndex_, 5); + EXPECT_EQ(info_->endIndex_, 8); EXPECT_EQ(GetChildOffset(frameNode_, 5), OffsetF(0.0f, -100.0f)); EXPECT_EQ(GetChildOffset(frameNode_, 6), OffsetF(300.0f, -100.0f)); EXPECT_EQ(GetChildOffset(frameNode_, 7), OffsetF(300.0f, 0.0f)); EXPECT_EQ(GetChildOffset(frameNode_, 8), OffsetF(0.0f, 100.0f)); } + +/** + * @tc.name: ModifyItem002 + * @tc.desc: Test WaterFlow reacting to child height change. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, ModifyItem002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Calling the ScrollToIndex interface to set values to 20 and true. + * @tc.expected: pattern_->targetIndex_ is 20 + */ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetFooter(GetDefaultHeaderBuilder()); + CreateItem(80); + }); + + pattern_->ScrollToIndex(50, false, ScrollAlign::CENTER); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->startIndex_, 44); + EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); + EXPECT_EQ(GetChildY(frameNode_, 51), 350.0f); + auto child = GetChildFrameNode(frameNode_, 49); + child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(300.0))); + child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->startIndex_, 44); + EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); + EXPECT_EQ(GetChildHeight(frameNode_, 49), 300.0f); + + child = GetChildFrameNode(frameNode_, 40); + child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(10.0))); + child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->startIndex_, 44); + EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); + EXPECT_FALSE(child->IsActive()); + EXPECT_FALSE(info_->idxToLane_.count(40)); + + // update footer + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + EXPECT_EQ(GetChildY(frameNode_, 80), 550.0f); + + child = GetChildFrameNode(frameNode_, 0); + child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(1.0))); + child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(GetChildY(frameNode_, 80), 599.0f); + EXPECT_EQ(GetChildHeight(frameNode_, 0), 1.0f); +} } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 8a5dc7bc524..7d068f7a21f 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -17,45 +17,36 @@ #include #include -#include "gtest/gtest.h" +#ifndef TEST_SEGMENTED_WATER_FLOW #include "test/mock/base/mock_system_properties.h" +#endif #include "test/mock/core/rosen/mock_canvas.h" -#include "water_flow_item_maps.h" -#include "base/utils/system_properties.h" #include "core/components/scroll/scroll_controller_base.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" #include "core/components_ng/property/property.h" #define protected public #define private public #include "test/mock/core/common/mock_theme_manager.h" #include "test/mock/core/pipeline/mock_pipeline_context.h" -#include "test/mock/core/render/mock_render_context.h" -#include "test/unittest/core/pattern/test_ng.h" #include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" -#include "base/geometry/dimension.h" -#include "base/geometry/offset.h" -#include "base/memory/ace_type.h" -#include "base/utils/utils.h" #include "core/components/button/button_theme.h" #include "core/components/common/layout/constants.h" #include "core/components_ng/base/view_stack_processor.h" #include "core/components_ng/pattern/linear_layout/row_model_ng.h" -#include "core/components_ng/pattern/pattern.h" #include "core/components_ng/pattern/scrollable/scrollable.h" #include "core/components_ng/pattern/waterflow/water_flow_accessibility_property.h" #include "core/components_ng/pattern/waterflow/water_flow_event_hub.h" #include "core/components_ng/pattern/waterflow/water_flow_item_model_ng.h" #include "core/components_ng/pattern/waterflow/water_flow_item_node.h" #include "core/components_ng/pattern/waterflow/water_flow_item_pattern.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/pattern/waterflow/water_flow_model_ng.h" #include "core/components_ng/pattern/waterflow/water_flow_pattern.h" #include "core/components_ng/property/measure_property.h" #include "core/components_v2/inspector/inspector_constants.h" -#include "core/pipeline/base/constants.h" #undef private #undef protected diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index aa2527027e2..ca37c65c55a 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -305,4 +305,34 @@ HWTEST_F(WaterFlowTestNg, OnWillScrollAndOnDidScroll002, TestSize.Level1) EXPECT_EQ(scrollState, willScrollState); EXPECT_EQ(scrollState, didScrollState); } + +/** + * @tc.name: ModifyItem001 + * @tc.desc: Test WaterFlow reacting to child height change. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, ModifyItem002, TestSize.Level1) +{ + /** + * @tc.steps: step1. Calling the ScrollToIndex interface to set values to 20 and true. + * @tc.expected: pattern_->targetIndex_ is 20 + */ + CreateWithItem([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + CreateItem(80); + }); + auto info = pattern_->layoutInfo_; + + pattern_->ScrollToIndex(50, false, ScrollAlign::CENTER); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info->startIndex_, 43); + EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); + auto child = GetChildFrameNode(frameNode_, 49); + child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(300.0))); + child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info->startIndex_, 43); + EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); + EXPECT_EQ(GetChildHeight(frameNode_, 49), 300.0f); +} } // namespace OHOS::Ace::NG -- Gitee From 8b78c7ece8f984d8974082a7819cbd06676a3815 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Fri, 3 May 2024 12:01:52 +0800 Subject: [PATCH 25/31] fix overScroll and spring effect Signed-off-by: Tianer Zhou Change-Id: I7ce6eb3e1cc6127b766afa001e957880fad0b51c --- .../water_flow_layout_info_sw.cpp | 38 ++++++++++++++----- .../water_flow_layout_info_sw.h | 29 ++++++++++++++ ...sw_layout.cpp => water_flow_layout_sw.cpp} | 2 +- .../waterflow/water_flow_layout_info.h | 14 +++++++ .../waterflow/water_flow_layout_info_base.h | 15 +++++++- .../pattern/waterflow/water_flow_pattern.cpp | 26 ++++++------- 6 files changed, 99 insertions(+), 25 deletions(-) rename frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/{water_flow_sw_layout.cpp => water_flow_layout_sw.cpp} (99%) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 8873d70c07c..b38880ccba5 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -30,13 +30,16 @@ void WaterFlowLayoutInfoSW::Sync(int32_t itemCnt, float mainSize, float mainGap) delta_ = 0.0f; lastMainSize_ = mainSize; mainGap_ = mainGap; + startPos_ = StartPos(); + endPos_ = EndPos(); - itemStart_ = startIndex_ == 0 && NonNegative(DistanceToTop(0, mainGap_)); - itemEnd_ = endIndex_ == itemCnt - 1; + itemStart_ = (startIndex_ == 0 && NonNegative(startPos_)) || GreatOrEqual(startPos_, mainSize); + itemEnd_ = endIndex_ == itemCnt - 1 || (itemCnt > 0 && NonPositive(endPos_)); if (!itemEnd_) { footerHeight_ = 0.0f; } - offsetEnd_ = itemEnd_ && LessOrEqual(EndPos() + footerHeight_, mainSize); + offsetEnd_ = itemEnd_ && LessOrEqual(endPos_ + footerHeight_, mainSize); + maxHeight_ = std::max(endPos_ - startPos_ + footerHeight_, maxHeight_); synced_ = true; } @@ -110,7 +113,7 @@ OverScrollOffset WaterFlowLayoutInfoSW::GetOverScrolledDelta(float delta) const if (!itemEnd_) { return res; } - float disToBot = EndPos() - lastMainSize_; + float disToBot = EndPos() + footerHeight_ - lastMainSize_; if (!itemEnd_) { res.end = std::min(0.0f, disToBot + delta); } else if (Negative(delta)) { @@ -128,22 +131,28 @@ float WaterFlowLayoutInfoSW::CalcOverScroll(float mainSize, float delta) const } float res = 0.0f; if (itemStart_) { - res = lanes_[0].startPos + delta; + res = StartPos() + delta; } if (offsetEnd_) { - res = mainSize - (EndPos() + delta); + res = mainSize - (EndPos() + footerHeight_ + delta); } return res; } float WaterFlowLayoutInfoSW::EndPos() const { + if (synced_) { + return endPos_; + } return std::max_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { return LessNotEqual(left.endPos, right.endPos); })->endPos; } float WaterFlowLayoutInfoSW::StartPos() const { + if (synced_) { + return startPos_; + } return std::min_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { return LessNotEqual(left.startPos, right.startPos); })->startPos; @@ -168,10 +177,8 @@ bool WaterFlowLayoutInfoSW::ReachEnd(float prevPos) const float WaterFlowLayoutInfoSW::GetContentHeight() const { - if (lanes_.empty()) { - return 0.0f; - } - return EndPos() - StartPos(); + // only height in view are remembered + return maxHeight_; } int32_t WaterFlowLayoutInfoSW::GetMainCount() const @@ -239,6 +246,7 @@ void WaterFlowLayoutInfoSW::Reset() delta_ = DistanceToTop(startIndex_, mainGap_); lanes_.clear(); idxToLane_.clear(); + maxHeight_ = 0.0f; synced_ = false; } @@ -288,6 +296,7 @@ void WaterFlowLayoutInfoSW::ResetBeforeJump(float laneBasePos) lane.endPos = laneBasePos; }); totalOffset_ = 0; + maxHeight_ = 0.0f; idxToLane_.clear(); synced_ = false; } @@ -341,4 +350,13 @@ void WaterFlowLayoutInfoSW::ClearDataFrom(int32_t idx, float mainGap) } } } + +float WaterFlowLayoutInfoSW::BottomFinalPos(float viewHeight) const +{ + if (LessNotEqual(maxHeight_, viewHeight)) { + // content < view + return -(EndPos() + footerHeight_) + maxHeight_; + } + return -(EndPos() + footerHeight_) + viewHeight; +}; } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 2a67c045ac5..add96a0404d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -79,6 +79,16 @@ public: return lanes_.size(); } + float CurrentPos() const override + { + return 0.0f; + } + float TopFinalPos() const override + { + return -StartPos(); + }; + float BottomFinalPos(float viewHeight) const override; + void Reset() override; /** @@ -88,6 +98,17 @@ public: */ void ResetBeforeJump(float laneBasePos); + void BeginUpdate() + { + synced_ = false; + } + /** + * @brief synchronize data after update is completed. + * + * @param itemCnt number of FlowItems. + * @param mainSize main-axis length of the viewport. + * @param mainGap main-axis gap between items. + */ void Sync(int32_t itemCnt, float mainSize, float mainGap); /** @@ -138,8 +159,16 @@ public: float delta_ = 0.0f; float totalOffset_ = 0.0f; // record total offset when continuously scrolling. Reset when jumped float mainGap_ = 0.0f; // update this at the end of a layout + + // maximum content height encountered so far, mainly for comparing content and viewport height + float maxHeight_ = 0.0f; float footerHeight_ = 0.0f; +private: + /* cache */ + float startPos_ = 0.0f; + float endPos_ = 0.0f; + bool synced_ = false; struct ItemInfo; diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp similarity index 99% rename from frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp rename to frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp index e3e9740ce78..e4588360f7f 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp @@ -31,7 +31,7 @@ namespace OHOS::Ace::NG { void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) { - info_->synced_ = false; + info_->BeginUpdate(); wrapper_ = wrapper; auto props = DynamicCast(wrapper->GetLayoutProperty()); info_->axis_ = axis_ = props->GetAxis(); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index d3431075c18..9ba53eaed01 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -97,6 +97,20 @@ public: return prevPos - currentOffset_; } + float CurrentPos() const override + { + return currentOffset_; + } + float TopFinalPos() const override + { + return 0.0f; + }; + float BottomFinalPos(float viewHeight) const override + { + float endOffset = viewHeight - GetContentHeight(); + return Negative(endOffset) ? endOffset : 0.0f; + }; + float JumpToTargetAlign(const std::pair& item) const; void JumpTo(const std::pair& item); diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index 66b872b626f..ab7b0c73c2d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -39,7 +39,7 @@ public: /* PURE GETTERs */ virtual WaterFlowLayoutMode mode() const = 0; - virtual float offset() const = 0; + virtual float offset() const = 0; // total offset of content virtual int32_t firstIdx() const = 0; // for compatibility virtual void UpdateOffset(float delta) = 0; @@ -105,6 +105,19 @@ public: virtual int32_t GetMainCount() const = 0; virtual int32_t GetCrossCount() const = 0; + /* ======== provide position info for spring effect animation ========= */ + virtual float CurrentPos() const = 0; + /** + * @return final position to bounce back to after over-scrolling from top. + */ + virtual float TopFinalPos() const = 0; + /** + * @param viewHeight height of the viewport. + * @return final position to bounce back to after over-scrolling from bottom. + */ + virtual float BottomFinalPos(float viewHeight) const = 0; + /* ========================================== */ + virtual void Reset() = 0; // for compatibility diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 8815cb6ba9e..c51c5ae72d3 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -502,28 +502,28 @@ void WaterFlowPattern::SetEdgeEffectCallback(const RefPtr& scr scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double { auto pattern = weak.Upgrade(); CHECK_NULL_RETURN(pattern, 0.0); - return pattern->layoutInfo_->offset(); + return pattern->layoutInfo_->CurrentPos(); }); scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double { auto pattern = weak.Upgrade(); CHECK_NULL_RETURN(pattern, 0.0); - auto leadOffset = pattern->GetMainContentSize() - pattern->layoutInfo_->GetContentHeight(); - if (pattern->GetAlwaysEnabled() && Positive(leadOffset)) { - return 0.0; - } - return Negative(leadOffset) ? leadOffset : 0.0; + return pattern->layoutInfo_->BottomFinalPos(pattern->GetMainContentSize()); + }); + scrollEffect->SetTrailingCallback([weak = AceType::WeakClaim(this)]() -> double { + auto pattern = weak.Upgrade(); + CHECK_NULL_RETURN(pattern, 0.0); + return pattern->layoutInfo_->TopFinalPos(); }); - scrollEffect->SetTrailingCallback([]() -> double { return 0.0; }); scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double { auto pattern = weak.Upgrade(); CHECK_NULL_RETURN(pattern, 0.0); - auto leadOffset = pattern->GetMainContentSize() - pattern->layoutInfo_->GetContentHeight(); - if (pattern->GetAlwaysEnabled() && Positive(leadOffset)) { - return 0.0; - } - return Negative(leadOffset) ? leadOffset : 0.0; + return pattern->layoutInfo_->BottomFinalPos(pattern->GetMainContentSize()); + }); + scrollEffect->SetInitTrailingCallback([weak = AceType::WeakClaim(this)]() -> double { + auto pattern = weak.Upgrade(); + CHECK_NULL_RETURN(pattern, 0.0); + return pattern->layoutInfo_->TopFinalPos(); }); - scrollEffect->SetInitTrailingCallback([]() -> double { return 0.0; }); } void WaterFlowPattern::MarkDirtyNodeSelf() -- Gitee From 1d6ea2cc67faa09f84177f57e255e6f7ef9da21c Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Fri, 3 May 2024 12:01:56 +0800 Subject: [PATCH 26/31] add test Signed-off-by: Tianer Zhou Change-Id: Iea746bd3c25576bc8bcc8620409b245a458e6de7 --- .../core/components_ng/pattern/BUILD.gn | 2 +- test/unittest/BUILD.gn | 2 +- .../waterflow/water_flow_sw_layout_test.cpp | 77 ++++++++++++++++++- .../pattern/waterflow/water_flow_test_ng.cpp | 2 + .../waterflow/water_flow_top_down_test.cpp | 38 +++++++++ 5 files changed, 118 insertions(+), 3 deletions(-) diff --git a/frameworks/core/components_ng/pattern/BUILD.gn b/frameworks/core/components_ng/pattern/BUILD.gn index e167f73d362..e6e98582d0a 100644 --- a/frameworks/core/components_ng/pattern/BUILD.gn +++ b/frameworks/core/components_ng/pattern/BUILD.gn @@ -525,7 +525,7 @@ build_component_ng("pattern_ng") { "video/video_pattern.cpp", "view_context/view_context_model_ng.cpp", "waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp", - "waterflow/layout/sliding_window/water_flow_sw_layout.cpp", + "waterflow/layout/sliding_window/water_flow_layout_sw.cpp", "waterflow/water_flow_accessibility_property.cpp", "waterflow/water_flow_content_modifier.cpp", "waterflow/water_flow_item_model_ng.cpp", diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index 5b7602990e6..afb2d9dec88 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -1047,7 +1047,7 @@ ohos_source_set("ace_components_pattern") { "$ace_root/frameworks/core/components_ng/pattern/video/video_pattern.cpp", "$ace_root/frameworks/core/components_ng/pattern/view_context/view_context_model_ng.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp", - "$ace_root/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.cpp", + "$ace_root/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_accessibility_property.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_content_modifier.cpp", "$ace_root/frameworks/core/components_ng/pattern/waterflow/water_flow_item_model_ng.cpp", diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index 19d4793cc0d..44a9b5485a6 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -190,7 +190,7 @@ HWTEST_F(WaterFlowSWTest, ModifyItem002, TestSize.Level1) model.SetFooter(GetDefaultHeaderBuilder()); CreateItem(80); }); - + pattern_->ScrollToIndex(50, false, ScrollAlign::CENTER); FlushLayoutTask(frameNode_); EXPECT_EQ(info_->startIndex_, 44); @@ -225,4 +225,79 @@ HWTEST_F(WaterFlowSWTest, ModifyItem002, TestSize.Level1) EXPECT_EQ(GetChildY(frameNode_, 80), 599.0f); EXPECT_EQ(GetChildHeight(frameNode_, 0), 1.0f); } + +/** + * @tc.name: OverScroll001 + * @tc.desc: Test overScroll past limits + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, OverScroll001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetFooter(GetDefaultHeaderBuilder()); + model.SetEdgeEffect(EdgeEffect::SPRING, true); + CreateItem(50); + }); + pattern_->SetAnimateCanOverScroll(true); + UpdateCurrentOffset(30000.0f); + const float startPos = info_->StartPos(); + EXPECT_TRUE(info_->startIndex_ > info_->endIndex_); + EXPECT_GT(info_->StartPos(), 2000.0f); + EXPECT_TRUE(info_->lanes_[0].items_.empty()); + EXPECT_TRUE(info_->lanes_[1].items_.empty()); + + info_->delta_ = -50.0f; + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->StartPos(), startPos - 50.0f); + EXPECT_TRUE(info_->startIndex_ > info_->endIndex_); + EXPECT_TRUE(info_->lanes_[0].items_.empty()); + EXPECT_TRUE(info_->lanes_[1].items_.empty()); + + UpdateCurrentOffset(-35000.0f); + EXPECT_EQ(info_->startIndex_, 11); + EXPECT_EQ(info_->endIndex_, 22); + EXPECT_LT(info_->StartPos(), 0.0f); + EXPECT_GT(info_->EndPos(), 800.0f); +} + +/** + * @tc.name: OverScroll002 + * @tc.desc: Test overScroll past limits + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, OverScroll002, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetFooter(GetDefaultHeaderBuilder()); + model.SetEdgeEffect(EdgeEffect::SPRING, true); + CreateItem(50); + }); + pattern_->SetAnimateCanOverScroll(true); + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + + UpdateCurrentOffset(-30000.0f); + EXPECT_TRUE(info_->startIndex_ > info_->endIndex_); + EXPECT_TRUE(info_->lanes_[0].items_.empty()); + EXPECT_TRUE(info_->lanes_[1].items_.empty()); + const float endPos = info_->EndPos(); + EXPECT_LT(info_->EndPos(), -2000.0f); + + info_->delta_ = 30.0f; + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->EndPos(), endPos + 30.0f); + EXPECT_TRUE(info_->startIndex_ > info_->endIndex_); + EXPECT_TRUE(info_->lanes_[0].items_.empty()); + EXPECT_TRUE(info_->lanes_[1].items_.empty()); + + UpdateCurrentOffset(35000.0f); + EXPECT_EQ(info_->startIndex_, 28); + EXPECT_EQ(info_->endIndex_, 41); + EXPECT_LT(info_->StartPos(), 0.0f); + EXPECT_GT(info_->EndPos(), 800.0f); +} } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index 7d068f7a21f..bcc97ac4085 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -1041,12 +1041,14 @@ HWTEST_F(WaterFlowTestNg, Callback002, TestSize.Level1) EXPECT_EQ(effect->currentPositionCallback_(), 0); pattern_->SetAlwaysEnabled(true); + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); FlushLayoutTask(frameNode_); EXPECT_EQ(effect->leadingCallback_(), 0); EXPECT_EQ(effect->initLeadingCallback_(), 0); EXPECT_EQ(effect->currentPositionCallback_(), 0); pattern_->layoutInfo_->Reset(); + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); FlushLayoutTask(frameNode_); EXPECT_EQ(effect->leadingCallback_(), 0); EXPECT_EQ(effect->initLeadingCallback_(), 0); diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index ca37c65c55a..ab4013d1343 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -335,4 +335,42 @@ HWTEST_F(WaterFlowTestNg, ModifyItem002, TestSize.Level1) EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); EXPECT_EQ(GetChildHeight(frameNode_, 49), 300.0f); } + +/** + * @tc.name: OverScroll001 + * @tc.desc: Test overScroll past limits + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, OverScroll001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create waterFlow + * @tc.expected: startIndex_ = 0 endIndex_ = 10. + */ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetFooter(GetDefaultHeaderBuilder()); + model.SetEdgeEffect(EdgeEffect::SPRING, true); + CreateItem(50); + }); + pattern_->SetAnimateCanOverScroll(true); + auto info = pattern_->layoutInfo_; + for (int i = 0; i < 50; ++i) { + UpdateCurrentOffset(500.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_GT(info->offset(), 0.0f); + } + EXPECT_GT(info->offset(), 2500.0f); + UpdateCurrentOffset(-25500.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 10); + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + for (int i = 0; i < 50; ++i) { + UpdateCurrentOffset(-200.0f); + EXPECT_EQ(info->endIndex_, std::max(49, info->footerIndex_)); + EXPECT_EQ(info->BottomFinalPos(800.0f), -3050.0f); + } + EXPECT_LT(info->offset(), -4000.0f); +} } // namespace OHOS::Ace::NG -- Gitee From 02d18d437dcd04a6bbdff7bddc97ac2ee8412630 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Fri, 3 May 2024 12:02:44 +0800 Subject: [PATCH 27/31] change file name Signed-off-by: Tianer Zhou Change-Id: If18136ba77f861b57e8afe8729cc785c72b170ce --- .../waterflow/layout/sliding_window/water_flow_layout_sw.cpp | 2 +- .../{water_flow_sw_layout.h => water_flow_layout_sw.h} | 0 .../core/components_ng/pattern/waterflow/water_flow_pattern.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/{water_flow_sw_layout.h => water_flow_layout_sw.h} (100%) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp index e4588360f7f..85d92cb2382 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h" +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h" #include #include diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h similarity index 100% rename from frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h rename to frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index c51c5ae72d3..b2911e35d44 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -17,7 +17,7 @@ #include "base/utils/utils.h" #include "core/components/scroll/scroll_controller_base.h" -#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_sw_layout.h" +#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" -- Gitee From 46737739cdf0ab34591ad6f1bdfea1e88d346353 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Fri, 3 May 2024 14:44:31 +0800 Subject: [PATCH 28/31] review fix Signed-off-by: Tianer Zhou Change-Id: I355dba597ba776c44f66cb9a41e8eec62eb4aad4 --- frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp b/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp index 221de96ce90..e570a2026c0 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_water_flow.cpp @@ -169,7 +169,7 @@ void JSWaterFlow::Create(const JSCallbackInfo& args) // set layout mode first. SetFooter is dependent to it using LayoutMode = NG::WaterFlowLayoutMode; - auto mode = LayoutMode::SLIDING_WINDOW; + auto mode = LayoutMode::TOP_DOWN; auto jsMode = obj->GetProperty("layoutMode"); if (jsMode->IsNumber()) { mode = static_cast(jsMode->ToNumber()); -- Gitee From 9c0a1161b59e768c0f5ed0ec77285c56b45c6498 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Fri, 3 May 2024 15:35:32 +0800 Subject: [PATCH 29/31] implement adjusting misalignment and test Signed-off-by: Tianer Zhou Change-Id: I8ac938e414dde4f871614fc5f869d430d8b40155 --- .../water_flow_layout_info_sw.cpp | 13 ++++- .../water_flow_layout_info_sw.h | 13 +++-- .../pattern/waterflow/water_flow_pattern.cpp | 54 ++++++++++++------- .../pattern/waterflow/water_flow_pattern.h | 7 ++- .../waterflow/water_flow_sw_layout_test.cpp | 27 ++++++++++ 5 files changed, 87 insertions(+), 27 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index b38880ccba5..1221e103ee2 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -295,7 +295,7 @@ void WaterFlowLayoutInfoSW::ResetBeforeJump(float laneBasePos) lane.startPos = laneBasePos; lane.endPos = laneBasePos; }); - totalOffset_ = 0; + totalOffset_ = 0.0f; maxHeight_ = 0.0f; idxToLane_.clear(); synced_ = false; @@ -359,4 +359,15 @@ float WaterFlowLayoutInfoSW::BottomFinalPos(float viewHeight) const } return -(EndPos() + footerHeight_) + viewHeight; }; + +bool WaterFlowLayoutInfoSW::IsMisaligned() const +{ + if (lanes_.empty()) { + return false; + } + bool laneNotAligned = std::any_of(lanes_.begin(), lanes_.end(), [](const auto& lane) { + return !NearZero(lane.startPos); + }); + return itemStart_ && (laneNotAligned || lanes_[0].items_.front().idx != 0); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index add96a0404d..c2b128083dd 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -32,16 +32,16 @@ class WaterFlowLayoutInfoSW : public WaterFlowLayoutInfoBase { DECLARE_ACE_TYPE(WaterFlowLayoutInfoSW, WaterFlowLayoutInfoBase); public: - WaterFlowLayoutMode mode() const override + WaterFlowLayoutMode Mode() const override { return WaterFlowLayoutMode::SLIDING_WINDOW; } - float offset() const override + float Offset() const override { return totalOffset_; } - int32_t firstIdx() const override + int32_t FirstIdx() const override { return startIndex_; } @@ -128,6 +128,13 @@ public: */ float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; + /** + * @brief If we jump to a position and scroll back to top, the staring items might not be aligned with the top boundary. + * @return true if 1. any lane misaligned with top boundary. + * 2. the first item is not in the first lane. + */ + bool IsMisaligned() const; + int32_t StartIndex() const; int32_t EndIndex() const; inline bool ItemInView(int32_t idx) const diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index b2911e35d44..a9a84f0a398 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -63,9 +63,9 @@ bool WaterFlowPattern::UpdateCurrentOffset(float delta, int32_t source) if (layoutInfo_->offsetEnd_ && delta < 0) { return false; } - if (layoutMode_ == LayoutMode::TOP_DOWN && GreatNotEqual(delta, 0.0f)) { + if (layoutInfo_->Mode() == LayoutMode::TOP_DOWN && GreatNotEqual(delta, 0.0f)) { // adjust top overScroll - delta = std::min(delta, -layoutInfo_->offset()); + delta = std::min(delta, -layoutInfo_->Offset()); } } float userOffset = FireOnWillScroll(-delta); @@ -101,7 +101,7 @@ OverScrollOffset WaterFlowPattern::GetOverScrollOffset(double delta) const void WaterFlowPattern::UpdateScrollBarOffset() { - if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { return; } if (!GetScrollBar() && !GetScrollBarProxy()) { @@ -132,7 +132,7 @@ RefPtr WaterFlowPattern::CreateLayoutAlgorithm() RefPtr algorithm; if (sections_ || SystemProperties::WaterFlowUseSegmentedLayout()) { algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); - } else if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + } else if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); } else { int32_t footerIndex = -1; @@ -182,7 +182,7 @@ void WaterFlowPattern::OnModifyDone() auto paintProperty = GetPaintProperty(); CHECK_NULL_VOID(paintProperty); - if (layoutMode_ != LayoutMode::SLIDING_WINDOW && paintProperty->GetScrollBarProperty()) { + if (layoutInfo_->Mode() != LayoutMode::SLIDING_WINDOW && paintProperty->GetScrollBarProperty()) { SetScrollBar(paintProperty->GetScrollBarProperty()); } SetAccessibilityAction(); @@ -217,12 +217,12 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir if (onDidScroll) { FireOnScroll(delta, onDidScroll); } - bool indexChanged = itemRange_.first != layoutInfo_->firstIdx() || itemRange_.second != layoutInfo_->endIndex_; + bool indexChanged = itemRange_.first != layoutInfo_->FirstIdx() || itemRange_.second != layoutInfo_->endIndex_; if (indexChanged) { auto onScrollIndex = eventHub->GetOnScrollIndex(); - itemRange_ = { layoutInfo_->firstIdx(), layoutInfo_->endIndex_ }; + itemRange_ = { layoutInfo_->FirstIdx(), layoutInfo_->endIndex_ }; if (onScrollIndex) { - onScrollIndex(layoutInfo_->firstIdx(), layoutInfo_->endIndex_); + onScrollIndex(layoutInfo_->FirstIdx(), layoutInfo_->endIndex_); } } auto onReachStart = eventHub->GetOnReachStart(); @@ -240,7 +240,7 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir targetIndex_.reset(); } layoutInfo_->UpdateStartIndex(); - prevOffset_ = layoutInfo_->offset(); + prevOffset_ = layoutInfo_->Offset(); layoutInfo_->jumpIndex_ = EMPTY_JUMP_INDEX; layoutInfo_->targetIndex_.reset(); UpdateScrollBarOffset(); @@ -344,7 +344,7 @@ void WaterFlowPattern::ScrollPage(bool reverse, bool smooth) auto mainContentSize = geometryNode->GetPaddingSize().MainSize(axis); if (smooth) { float distance = reverse ? mainContentSize : -mainContentSize; - float position = layoutInfo_->offset() + distance; + float position = layoutInfo_->Offset() + distance; ScrollablePattern::AnimateTo(-position, -1, nullptr, true); } else { UpdateCurrentOffset(reverse ? mainContentSize : -mainContentSize, SCROLL_FROM_JUMP); @@ -391,7 +391,7 @@ Rect WaterFlowPattern::GetItemRect(int32_t index) const RefPtr WaterFlowPattern::GetSections() const { - if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { return nullptr; } return sections_; @@ -399,7 +399,7 @@ RefPtr WaterFlowPattern::GetSections() const RefPtr WaterFlowPattern::GetOrCreateWaterFlowSections() { - if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { return nullptr; } if (sections_) { @@ -431,7 +431,7 @@ RefPtr WaterFlowPattern::GetOrCreateWaterFlowSections() void WaterFlowPattern::OnSectionChanged(int32_t start) { // SlidingWindow mode should never reach this callback - if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { return; } auto info = DynamicCast(layoutInfo_); @@ -465,7 +465,7 @@ void WaterFlowPattern::OnSectionChangedNow(int32_t start) void WaterFlowPattern::ResetSections() { sections_.Reset(); - if (layoutMode_ == LayoutMode::SLIDING_WINDOW) { + if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { return; } layoutInfo_->Reset(); @@ -533,22 +533,40 @@ void WaterFlowPattern::MarkDirtyNodeSelf() host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); } +namespace { +// check if layout is misaligned after a scroll event +void CheckMisalignment(const RefPtr& info) +{ + if (info->Mode() != WaterFlowLayoutMode::SLIDING_WINDOW) { + return; + } + auto infoSW = AceType::DynamicCast(info); + if (infoSW->IsMisaligned()) { + infoSW->ResetBeforeJump(0.0f); + info->jumpIndex_ = 0; + info->align_ = ScrollAlign::START; + } +} +} // namespace + void WaterFlowPattern::OnScrollEndCallback() { scrollStop_ = true; + CheckMisalignment(layoutInfo_); MarkDirtyNodeSelf(); } void WaterFlowPattern::OnAnimateStop() { scrollStop_ = true; + CheckMisalignment(layoutInfo_); MarkDirtyNodeSelf(); } void WaterFlowPattern::AnimateTo( float position, float duration, const RefPtr& curve, bool smooth, bool canOverScroll) { - if (layoutMode_ == WaterFlowLayoutMode::SLIDING_WINDOW) { + if (layoutInfo_->Mode() == WaterFlowLayoutMode::SLIDING_WINDOW) { return; } ScrollablePattern::AnimateTo(position, duration, curve, smooth, canOverScroll); @@ -556,7 +574,7 @@ void WaterFlowPattern::AnimateTo( void WaterFlowPattern::ScrollTo(float position) { - if (layoutMode_ == WaterFlowLayoutMode::SLIDING_WINDOW) { + if (layoutInfo_->Mode() == WaterFlowLayoutMode::SLIDING_WINDOW) { return; } ScrollablePattern::ScrollTo(position); @@ -605,9 +623,7 @@ void WaterFlowPattern::AddFooter(const RefPtr& footer) void WaterFlowPattern::SetLayoutMode(LayoutMode mode) { - layoutMode_ = mode; - - if (!layoutInfo_ || mode != layoutInfo_->mode()) { + if (!layoutInfo_ || mode != layoutInfo_->Mode()) { layoutInfo_ = WaterFlowLayoutInfoBase::Create(mode); } // footer index only set during first AddFooter call diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h index ed1ed03a63f..228cda014ac 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -46,7 +46,7 @@ public: void SetLayoutMode(LayoutMode mode); LayoutMode GetLayoutMode() const { - return layoutMode_; + return layoutInfo_->Mode(); } RefPtr CreateLayoutAlgorithm() override; @@ -95,7 +95,7 @@ public: float GetTotalOffset() const override { - return -layoutInfo_->offset(); + return -layoutInfo_->Offset(); } int32_t GetRows() const; @@ -167,8 +167,7 @@ private: bool ScrollToTargetIndex(int32_t index); bool NeedRender(); std::optional targetIndex_; - LayoutMode layoutMode_ = LayoutMode::TOP_DOWN; - RefPtr layoutInfo_ = WaterFlowLayoutInfoBase::Create(layoutMode_); + RefPtr layoutInfo_ = WaterFlowLayoutInfoBase::Create(LayoutMode::TOP_DOWN); RefPtr sections_; float prevOffset_ = 0.0f; diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index 44a9b5485a6..3d006cf3b94 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -300,4 +300,31 @@ HWTEST_F(WaterFlowSWTest, OverScroll002, TestSize.Level1) EXPECT_LT(info_->StartPos(), 0.0f); EXPECT_GT(info_->EndPos(), 800.0f); } + +/** + * @tc.name: Misaligned001 + * @tc.desc: Test misalignment and adjustment + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, Misaligned001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetFooter(GetDefaultHeaderBuilder()); + CreateItem(50); + }); + EXPECT_FALSE(info_->IsMisaligned()); + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + + UpdateCurrentOffset(Infinity()); + EXPECT_TRUE(info_->IsMisaligned()); + EXPECT_EQ(GetChildY(frameNode_, 1), 100.0f); + EXPECT_EQ(GetChildX(frameNode_, 1), 240.0f); + pattern_->OnScrollEndCallback(); + FlushLayoutTask(frameNode_); + EXPECT_FALSE(info_->IsMisaligned()); + EXPECT_EQ(info_->lanes_[0].startPos, 0.0f); + EXPECT_EQ(info_->lanes_[0].items_.front().idx, 0); +} } // namespace OHOS::Ace::NG -- Gitee From 3a03d824877b35230f4c03e87cfaa49a832577d6 Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Fri, 3 May 2024 15:35:40 +0800 Subject: [PATCH 30/31] review fixes Signed-off-by: Tianer Zhou Change-Id: I5f7f9a89903e1debcff0afbb1d0289db505cb94c --- .../engine/jsEnumStyle.js | 2 +- .../water_flow_layout_info_sw.cpp | 22 ++- .../water_flow_layout_info_sw.h | 9 +- .../sliding_window/water_flow_layout_sw.cpp | 52 +++---- .../sliding_window/water_flow_layout_sw.h | 6 +- .../waterflow/water_flow_layout_algorithm.cpp | 2 +- .../waterflow/water_flow_layout_info.cpp | 1 - .../waterflow/water_flow_layout_info.h | 6 +- .../waterflow/water_flow_layout_info_base.h | 6 +- .../pattern/waterflow/water_flow_pattern.cpp | 12 +- test/unittest/core/BUILD.gn | 2 + .../water_flow_segment_layout_test.cpp | 2 +- .../pattern/waterflow/water_flow_test_ng.cpp | 137 +++++++++++++++++- .../waterflow/water_flow_top_down_test.cpp | 20 ++- 14 files changed, 206 insertions(+), 73 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js b/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js index 5034c937c63..ab6d17b60a5 100644 --- a/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js +++ b/frameworks/bridge/declarative_frontend/engine/jsEnumStyle.js @@ -2489,7 +2489,7 @@ class WaterFlowSections { var WaterFlowLayoutMode; (function (WaterFlowLayoutMode) { - WaterFlowLayoutMode[WaterFlowLayoutMode["TOP_DOWN"] = 0] = "TOP_DOWN"; + WaterFlowLayoutMode[WaterFlowLayoutMode["ALWAYS_TOP_DOWN"] = 0] = "ALWAYS_TOP_DOWN"; WaterFlowLayoutMode[WaterFlowLayoutMode["SLIDING_WINDOW"] = 1] = "SLIDING_WINDOW"; })(WaterFlowLayoutMode || (WaterFlowLayoutMode = {})); diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 1221e103ee2..3aae0192960 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -203,14 +203,8 @@ float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx pos = DistanceToTop(idx, mainGap_); auto it = std::find_if( lane.items_.begin(), lane.items_.end(), [idx](const ItemInfo& item) { return item.idx == idx; }); - if (it == lane.items_.end()) { - std::abort(); - } itemSize = it->mainSize; } else { - if (lane.items_.back().idx != idx) { - std::abort(); - } itemSize = lane.items_.back().mainSize; pos = lane.endPos - itemSize; } @@ -351,13 +345,14 @@ void WaterFlowLayoutInfoSW::ClearDataFrom(int32_t idx, float mainGap) } } +float WaterFlowLayoutInfoSW::TopFinalPos() const +{ + return -(StartPos() + delta_); +}; + float WaterFlowLayoutInfoSW::BottomFinalPos(float viewHeight) const { - if (LessNotEqual(maxHeight_, viewHeight)) { - // content < view - return -(EndPos() + footerHeight_) + maxHeight_; - } - return -(EndPos() + footerHeight_) + viewHeight; + return -(EndPos() + delta_ + footerHeight_) + std::min(maxHeight_, viewHeight); }; bool WaterFlowLayoutInfoSW::IsMisaligned() const @@ -365,9 +360,12 @@ bool WaterFlowLayoutInfoSW::IsMisaligned() const if (lanes_.empty()) { return false; } + if (!itemStart_ || !NearZero(StartPos())) { + return false; + } bool laneNotAligned = std::any_of(lanes_.begin(), lanes_.end(), [](const auto& lane) { return !NearZero(lane.startPos); }); - return itemStart_ && (laneNotAligned || lanes_[0].items_.front().idx != 0); + return laneNotAligned || lanes_[0].items_.front().idx != 0; } } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index c2b128083dd..56a4caef1cf 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -83,10 +83,7 @@ public: { return 0.0f; } - float TopFinalPos() const override - { - return -StartPos(); - }; + float TopFinalPos() const override; float BottomFinalPos(float viewHeight) const override; void Reset() override; @@ -129,7 +126,9 @@ public: float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; /** - * @brief If we jump to a position and scroll back to top, the staring items might not be aligned with the top boundary. + * @brief Check if the layout is misaligned. + * + * If we jump and scroll back to top, the staring items might not be aligned with the top boundary. * @return true if 1. any lane misaligned with top boundary. * 2. the first item is not in the first lane. */ diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp index 85d92cb2382..023670a95be 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp @@ -29,7 +29,7 @@ #include "core/components_ng/property/templates_parser.h" namespace OHOS::Ace::NG { -void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) +void WaterFlowLayoutSW::Measure(LayoutWrapper* wrapper) { info_->BeginUpdate(); wrapper_ = wrapper; @@ -55,7 +55,7 @@ void WaterFlowSWLayout::Measure(LayoutWrapper* wrapper) wrapper->SetCacheCount(props->GetCachedCountValue(1)); } -void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) +void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) { if (info_->lanes_.empty()) { return; @@ -104,7 +104,7 @@ void WaterFlowSWLayout::Layout(LayoutWrapper* wrapper) LayoutFooter(paddingOffset, reverse); } -void WaterFlowSWLayout::Init(const SizeF& frameSize) +void WaterFlowLayoutSW::Init(const SizeF& frameSize) { // omit footer from children count itemCnt_ = wrapper_->GetTotalChildCount() - info_->footerIndex_ - 1; @@ -141,7 +141,7 @@ void WaterFlowSWLayout::Init(const SizeF& frameSize) } } -void WaterFlowSWLayout::CheckReset() +void WaterFlowLayoutSW::CheckReset() { int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated(); if (updateIdx == -1) { @@ -161,7 +161,7 @@ void WaterFlowSWLayout::CheckReset() wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1); } -void WaterFlowSWLayout::MeasureOnOffset(float delta) +void WaterFlowLayoutSW::MeasureOnOffset(float delta) { ApplyDelta(delta); AdjustOverScroll(); @@ -173,7 +173,7 @@ void WaterFlowSWLayout::MeasureOnOffset(float delta) } } -void WaterFlowSWLayout::ApplyDelta(float delta) +void WaterFlowLayoutSW::ApplyDelta(float delta) { info_->totalOffset_ += delta; for (auto& lane : info_->lanes_) { @@ -189,7 +189,7 @@ void WaterFlowSWLayout::ApplyDelta(float delta) } } -void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) +void WaterFlowLayoutSW::MeasureToTarget(int32_t targetIdx) { if (itemCnt_ == 0) { return; @@ -203,7 +203,7 @@ void WaterFlowSWLayout::MeasureToTarget(int32_t targetIdx) // [lane start/end position, lane index] using lanePos = std::pair; -void WaterFlowSWLayout::FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx) +void WaterFlowLayoutSW::FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx) { idx = std::max(idx, 0); maxChildIdx = std::min(maxChildIdx, itemCnt_ - 1); @@ -242,7 +242,7 @@ struct MaxHeapCmp { } }; } // namespace -void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minChildIdx) +void WaterFlowLayoutSW::FillFront(float viewportBound, int32_t idx, int32_t minChildIdx) { idx = std::min(itemCnt_ - 1, idx); minChildIdx = std::max(minChildIdx, 0); @@ -269,7 +269,7 @@ void WaterFlowSWLayout::FillFront(float viewportBound, int32_t idx, int32_t minC } } -float WaterFlowSWLayout::FillBackHelper(const RefPtr& props, int32_t idx, size_t laneIdx) +float WaterFlowLayoutSW::FillBackHelper(const RefPtr& props, int32_t idx, size_t laneIdx) { float mainLen = MeasureChild(props, idx, laneIdx); auto& lane = info_->lanes_[laneIdx]; @@ -281,7 +281,7 @@ float WaterFlowSWLayout::FillBackHelper(const RefPtr& p return lane.endPos; } -float WaterFlowSWLayout::FillFrontHelper(const RefPtr& props, int32_t idx, size_t laneIdx) +float WaterFlowLayoutSW::FillFrontHelper(const RefPtr& props, int32_t idx, size_t laneIdx) { float mainLen = MeasureChild(props, idx, laneIdx); auto& lane = info_->lanes_[laneIdx]; @@ -293,7 +293,7 @@ float WaterFlowSWLayout::FillFrontHelper(const RefPtr& return lane.startPos; } -void WaterFlowSWLayout::RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx) +void WaterFlowLayoutSW::RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx) { std::unordered_set lanes; for (size_t i = 0; i < info_->lanes_.size(); ++i) { @@ -312,7 +312,7 @@ void WaterFlowSWLayout::RecoverBack(float viewportBound, int32_t& idx, int32_t m } } -void WaterFlowSWLayout::RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx) +void WaterFlowLayoutSW::RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx) { std::unordered_set lanes; for (size_t i = 0; i < info_->lanes_.size(); ++i) { @@ -331,15 +331,12 @@ void WaterFlowSWLayout::RecoverFront(float viewportBound, int32_t& idx, int32_t } } -void WaterFlowSWLayout::ClearBack(float bound) +void WaterFlowLayoutSW::ClearBack(float bound) { int32_t startIdx = info_->StartIndex(); for (int32_t i = info_->EndIndex(); i >= startIdx; --i) { size_t laneIdx = info_->idxToLane_.at(i); auto& lane = info_->lanes_[laneIdx]; - if (lane.items_.back().idx != i) { - std::abort(); - } float itemStartPos = lane.endPos - lane.items_.back().mainSize; if (LessNotEqual(itemStartPos, bound)) { break; @@ -349,15 +346,12 @@ void WaterFlowSWLayout::ClearBack(float bound) } } -void WaterFlowSWLayout::ClearFront() +void WaterFlowLayoutSW::ClearFront() { int32_t endIdx = info_->EndIndex(); for (int32_t i = info_->StartIndex(); i <= endIdx; ++i) { size_t laneIdx = info_->idxToLane_.at(i); auto& lane = info_->lanes_[laneIdx]; - if (lane.items_.front().idx != i) { - std::abort(); - } float itemEndPos = lane.startPos + lane.items_.front().mainSize; if (Positive(itemEndPos)) { break; @@ -367,7 +361,7 @@ void WaterFlowSWLayout::ClearFront() } } -ScrollAlign WaterFlowSWLayout::ParseAutoAlign(int32_t jumpIdx, bool inView) +ScrollAlign WaterFlowLayoutSW::ParseAutoAlign(int32_t jumpIdx, bool inView) { if (inView) { if (Negative(info_->DistanceToTop(jumpIdx, mainGap_))) { @@ -385,7 +379,7 @@ ScrollAlign WaterFlowSWLayout::ParseAutoAlign(int32_t jumpIdx, bool inView) return ScrollAlign::END; } -void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) +void WaterFlowLayoutSW::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) { if (jumpIdx == LAST_ITEM) { jumpIdx = itemCnt_ - 1; @@ -415,7 +409,7 @@ void WaterFlowSWLayout::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) } } -void WaterFlowSWLayout::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) +void WaterFlowLayoutSW::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) { switch (align) { case ScrollAlign::START: { @@ -460,7 +454,7 @@ void WaterFlowSWLayout::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) } } -void WaterFlowSWLayout::AdjustOverScroll() +void WaterFlowLayoutSW::AdjustOverScroll() { if (info_->lanes_.empty()) { return; @@ -488,7 +482,7 @@ void WaterFlowSWLayout::AdjustOverScroll() } } -float WaterFlowSWLayout::MeasureChild(const RefPtr& props, int32_t idx, size_t lane) +float WaterFlowLayoutSW::MeasureChild(const RefPtr& props, int32_t idx, size_t lane) { auto child = wrapper_->GetOrCreateChildByIndex(nodeIdx(idx)); CHECK_NULL_RETURN(child, 0.0f); @@ -497,7 +491,7 @@ float WaterFlowSWLayout::MeasureChild(const RefPtr& pro return child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); } -void WaterFlowSWLayout::LayoutFooter(const OffsetF& paddingOffset, bool reverse) +void WaterFlowLayoutSW::LayoutFooter(const OffsetF& paddingOffset, bool reverse) { float endPos = info_->EndPos(); if (info_->footerIndex_ != 0 || GreatOrEqual(endPos, mainLen_)) { @@ -513,7 +507,7 @@ void WaterFlowSWLayout::LayoutFooter(const OffsetF& paddingOffset, bool reverse) footer->Layout(); } -void WaterFlowSWLayout::PostMeasureSelf(float selfCrossLen) +void WaterFlowLayoutSW::PostMeasureSelf(float selfCrossLen) { mainLen_ = info_->GetContentHeight(); SizeF selfSize = (axis_ == Axis::VERTICAL) ? SizeF(selfCrossLen, mainLen_) : SizeF(mainLen_, selfCrossLen); @@ -522,7 +516,7 @@ void WaterFlowSWLayout::PostMeasureSelf(float selfCrossLen) wrapper_->GetGeometryNode()->SetFrameSize(selfSize); } -inline int32_t WaterFlowSWLayout::nodeIdx(int32_t idx) const +inline int32_t WaterFlowLayoutSW::nodeIdx(int32_t idx) const { return idx + info_->footerIndex_ + 1; } diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h index d3012f1ca4e..a5b86344ed5 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h @@ -23,11 +23,11 @@ namespace OHOS::Ace::NG { -class ACE_EXPORT WaterFlowSWLayout : public WaterFlowLayoutBase { - DECLARE_ACE_TYPE(WaterFlowSWLayout, WaterFlowLayoutBase); +class ACE_EXPORT WaterFlowLayoutSW : public WaterFlowLayoutBase { + DECLARE_ACE_TYPE(WaterFlowLayoutSW, WaterFlowLayoutBase); public: - explicit WaterFlowSWLayout(const RefPtr& info) : info_(info) {} + explicit WaterFlowLayoutSW(const RefPtr& info) : info_(info) {} void Measure(LayoutWrapper* wrapper) override; void Layout(LayoutWrapper* wrapper) override; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp index 3b068f0ac10..03955a9da46 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_algorithm.cpp @@ -221,7 +221,7 @@ void WaterFlowLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper) } auto currentOffset = childFrameOffset; auto crossOffset = itemCrossPosition->second; - auto mainOffset = item.second.first + layoutInfo_.currentOffset_; + auto mainOffset = item.second.first + layoutInfo_->currentOffset_; if (isRtl) { crossOffset = crossSize - crossOffset - itemsCrossSize_.at(mainPositions.first); } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 7d72543bd5e..33e500b56ae 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -18,7 +18,6 @@ #include #include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/property/calc_length.h" #include "core/components_ng/property/measure_property.h" #include "core/components_ng/property/measure_utils.h" diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h index 9ba53eaed01..b505a803aee 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.h @@ -45,15 +45,15 @@ public: WaterFlowLayoutInfo() = default; ~WaterFlowLayoutInfo() override = default; - WaterFlowLayoutMode mode() const override + WaterFlowLayoutMode Mode() const override { return WaterFlowLayoutMode::TOP_DOWN; } - float offset() const override + float Offset() const override { return currentOffset_; } - int32_t firstIdx() const override + int32_t FirstIdx() const override { return firstIndex_; } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h index ab7b0c73c2d..0b43f02669b 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info_base.h @@ -38,9 +38,9 @@ public: static RefPtr Create(WaterFlowLayoutMode mode); /* PURE GETTERs */ - virtual WaterFlowLayoutMode mode() const = 0; - virtual float offset() const = 0; // total offset of content - virtual int32_t firstIdx() const = 0; // for compatibility + virtual WaterFlowLayoutMode Mode() const = 0; + virtual float Offset() const = 0; // total offset of content + virtual int32_t FirstIdx() const = 0; // for compatibility virtual void UpdateOffset(float delta) = 0; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index a9a84f0a398..88c030467ab 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -19,9 +19,7 @@ #include "core/components/scroll/scroll_controller_base.h" #include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" #include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" #include "core/components_ng/pattern/waterflow/water_flow_paint_method.h" #include "core/components_ng/pattern/waterflow/water_flow_segmented_layout.h" @@ -68,8 +66,8 @@ bool WaterFlowPattern::UpdateCurrentOffset(float delta, int32_t source) delta = std::min(delta, -layoutInfo_->Offset()); } } - float userOffset = FireOnWillScroll(-delta); - layoutInfo_->UpdateOffset(-userOffset); + delta = -FireOnWillScroll(-delta); + layoutInfo_->UpdateOffset(delta); host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); return true; }; @@ -133,7 +131,7 @@ RefPtr WaterFlowPattern::CreateLayoutAlgorithm() if (sections_ || SystemProperties::WaterFlowUseSegmentedLayout()) { algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); } else if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { - algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); + algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); } else { int32_t footerIndex = -1; auto footer = footer_.Upgrade(); @@ -182,7 +180,9 @@ void WaterFlowPattern::OnModifyDone() auto paintProperty = GetPaintProperty(); CHECK_NULL_VOID(paintProperty); - if (layoutInfo_->Mode() != LayoutMode::SLIDING_WINDOW && paintProperty->GetScrollBarProperty()) { + if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { + SetScrollBar(DisplayMode::OFF); + } else if (paintProperty->GetScrollBarProperty()) { SetScrollBar(paintProperty->GetScrollBarProperty()); } SetAccessibilityAction(); diff --git a/test/unittest/core/BUILD.gn b/test/unittest/core/BUILD.gn index 4f288be2312..2dfccf6f569 100644 --- a/test/unittest/core/BUILD.gn +++ b/test/unittest/core/BUILD.gn @@ -168,6 +168,8 @@ group("linux_core_unittest") { # "pattern/waterflow:water_flow_test_ng", "pattern/waterflow:water_flow_test_old", "pattern/waterflow:water_flow_test_sw", + + # "pattern/xcomponent:xcomponent_test_ng", "property:core_property_unittest", "render:core_render_unittest", "syntax:core_syntax_unittest", diff --git a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp index 7dcc76c9130..b5aa3854669 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_segment_layout_test.cpp @@ -16,7 +16,7 @@ #include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" #include "core/components_ng/pattern/waterflow/water_flow_item_pattern.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_info->h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" #include "core/components_ng/property/calc_length.h" #include "core/components_ng/property/measure_property.h" diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index bcc97ac4085..e88e486c624 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -836,7 +836,7 @@ HWTEST_F(WaterFlowTestNg, WaterFlowPatternTest002, TestSize.Level1) */ HandleDrag(-100.f); pattern_->UpdateScrollBarOffset(); - EXPECT_EQ(pattern_->layoutInfo_->offset(), 0.f); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0.f); EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 21); @@ -1416,7 +1416,122 @@ HWTEST_F(WaterFlowTestNg, MeasureForAnimation001, TestSize.Level1) * @tc.desc: Layout WaterFlow and then reset to old layout * @tc.type: FUNC */ +<<<<<<< HEAD HWTEST_F(WaterFlowTestNg, ResetSections001, TestSize.Level1) +======= +HWTEST_F(WaterFlowTestNg, ModifyItem001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Calling the ScrollToIndex interface to set values to 20 and true. + * @tc.expected: pattern_->targetIndex_ is 20 + */ + CreateWithItem([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + CreateItem(80); + }); + auto info = pattern_->layoutInfo_; + EXPECT_EQ(info->endIndex_, 10); + auto child = GetChildFrameNode(frameNode_, 8); + child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(500.0))); + child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(GetChildHeight(frameNode_, 8), 500.0f); +} + +/** + * @tc.name: ScrollToIndex001 + * @tc.desc: Test WaterFlow ScrollToIndex function. + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, ScrollToIndex001, TestSize.Level1) +{ + /** + * @tc.steps: step1. Calling the ScrollToIndex interface to set values to 20 and true. + * @tc.expected: pattern_->targetIndex_ is 20 + */ + CreateWithItem([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + CreateItem(80); + }); + pattern_->ScrollToIndex(20, true); + EXPECT_EQ(pattern_->targetIndex_, 20); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->finalPosition_, 1400.0f); + + pattern_->ScrollToIndex(80, true); + EXPECT_EQ(pattern_->targetIndex_, 80); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->finalPosition_, 5900.0f); +} + +/** + * @tc.name: ScrollToIndex002 + * @tc.desc: Test ScrollToIndex func + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, ScrollToIndex002, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + CreateItem(30); + }); + + pattern_->ScrollToIndex(3, false, ScrollAlign::AUTO); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0); + + pattern_->ScrollToIndex(15, false, ScrollAlign::START); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 15); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -1100); + + pattern_->ScrollToIndex(LAST_ITEM); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 19); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -1500); + + pattern_->ScrollToIndex(0, false, ScrollAlign::START); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0); + + pattern_->ScrollToIndex(15, false, ScrollAlign::AUTO); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 7); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -500); + + pattern_->ScrollToIndex(7, false, ScrollAlign::CENTER); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 3); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -200); + + pattern_->ScrollToIndex(14, false, ScrollAlign::END); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 3); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -300); + + pattern_->ScrollToIndex(2, false, ScrollAlign::AUTO); + FlushLayoutTask(frameNode_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 1); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -100); +} + +/** + * @tc.name: ScrollToIndex003 + * @tc.desc: Test ScrollToIndex func + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, ScrollToIndex003, TestSize.Level1) +>>>>>>> review fixes { Create([](WaterFlowModelNG model) { ViewAbstract::SetWidth(CalcLength(400.0f)); @@ -1427,6 +1542,7 @@ HWTEST_F(WaterFlowTestNg, ResetSections001, TestSize.Level1) secObj->ChangeData(0, 0, SECTION_5); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); +<<<<<<< HEAD auto& info = pattern_->layoutInfo_; UpdateCurrentOffset(-205.0f); @@ -1453,5 +1569,24 @@ HWTEST_F(WaterFlowTestNg, ResetSections001, TestSize.Level1) EXPECT_EQ(info.currentOffset_, 0.0f); EXPECT_EQ(info.startIndex_, 0); EXPECT_EQ(info.endIndex_, 3); +======= + EXPECT_FLOAT_EQ(pattern_->finalPosition_, 800.f); + + pattern_->ScrollToIndex(3, true, ScrollAlign::AUTO); + FlushLayoutTask(frameNode_); + EXPECT_FLOAT_EQ(pattern_->finalPosition_, 800.f); + + pattern_->ScrollPage(false); + FlushLayoutTask(frameNode_); + EXPECT_LT(pattern_->layoutInfo_->Offset(), 0.f); + + pattern_->ScrollToIndex(3, true, ScrollAlign::AUTO); + FlushLayoutTask(frameNode_); + EXPECT_FLOAT_EQ(pattern_->finalPosition_, 200.f); + + pattern_->ScrollToIndex(29, true); + FlushLayoutTask(frameNode_); + EXPECT_FLOAT_EQ(pattern_->finalPosition_, 2100.f); +>>>>>>> review fixes } } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index ab4013d1343..c50814aac1d 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -178,7 +178,7 @@ HWTEST_F(WaterFlowTestNg, UpdateCurrentOffset003, TestSize.Level1) pattern_->SetAnimateCanOverScroll(true); pattern_->UpdateCurrentOffset(10000, SCROLL_FROM_UPDATE); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->firstIdx(), 0); + EXPECT_EQ(pattern_->layoutInfo_->FirstIdx(), 0); EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 0); /** @@ -189,7 +189,7 @@ HWTEST_F(WaterFlowTestNg, UpdateCurrentOffset003, TestSize.Level1) pattern_->SetAnimateCanOverScroll(true); pattern_->UpdateCurrentOffset(-99999, SCROLL_FROM_UPDATE); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->firstIdx(), 19); + EXPECT_EQ(pattern_->layoutInfo_->FirstIdx(), 19); EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 19); } @@ -214,10 +214,13 @@ HWTEST_F(WaterFlowTestNg, OnWillScrollAndOnDidScroll001, TestSize.Level1) Dimension willScrollOffset; ScrollState willScrollState; auto onWillScroll = [&willScrollOffset, &willScrollState, &isOnWillScrollCallBack]( - Dimension offset, ScrollState state) { + Dimension offset, ScrollState state, ScrollSource source) { willScrollOffset = offset; willScrollState = state; isOnWillScrollCallBack = true; + ScrollFrameResult result; + result.offset = offset; + return result; }; Dimension didScrollOffset; ScrollState didScrollState = ScrollState::IDLE; @@ -269,10 +272,13 @@ HWTEST_F(WaterFlowTestNg, OnWillScrollAndOnDidScroll002, TestSize.Level1) Dimension willScrollOffset; ScrollState willScrollState; auto onWillScroll = [&willScrollOffset, &willScrollState, &isOnWillScrollCallBack]( - Dimension offset, ScrollState state) { + Dimension offset, ScrollState state, ScrollSource source) { willScrollOffset = offset; willScrollState = state; isOnWillScrollCallBack = true; + ScrollFrameResult result; + result.offset = offset; + return result; }; Dimension didScrollOffset; ScrollState didScrollState = ScrollState::IDLE; @@ -358,9 +364,9 @@ HWTEST_F(WaterFlowTestNg, OverScroll001, TestSize.Level1) for (int i = 0; i < 50; ++i) { UpdateCurrentOffset(500.0f); EXPECT_EQ(info->startIndex_, 0); - EXPECT_GT(info->offset(), 0.0f); + EXPECT_GT(info->Offset(), 0.0f); } - EXPECT_GT(info->offset(), 2500.0f); + EXPECT_GT(info->Offset(), 2500.0f); UpdateCurrentOffset(-25500.0f); EXPECT_EQ(info->startIndex_, 0); EXPECT_EQ(info->endIndex_, 10); @@ -371,6 +377,6 @@ HWTEST_F(WaterFlowTestNg, OverScroll001, TestSize.Level1) EXPECT_EQ(info->endIndex_, std::max(49, info->footerIndex_)); EXPECT_EQ(info->BottomFinalPos(800.0f), -3050.0f); } - EXPECT_LT(info->offset(), -4000.0f); + EXPECT_LT(info->Offset(), -4000.0f); } } // namespace OHOS::Ace::NG -- Gitee From 1070b65cb35b6370741d2f108ec5eca2281f7a3b Mon Sep 17 00:00:00 2001 From: Tianer Zhou Date: Mon, 20 May 2024 11:58:01 +0800 Subject: [PATCH 31/31] break up commit Signed-off-by: Tianer Zhou Change-Id: I8ac8d8ba9298111ab1fafdade522fa58dcca957f --- .../water_flow_layout_info_sw.cpp | 359 +----------- .../water_flow_layout_info_sw.h | 178 ------ .../sliding_window/water_flow_layout_sw.cpp | 511 +----------------- .../sliding_window/water_flow_layout_sw.h | 123 +---- .../waterflow/water_flow_layout_info.cpp | 2 +- .../pattern/waterflow/water_flow_pattern.cpp | 29 +- .../waterflow/water_flow_segmented_layout.cpp | 4 +- .../waterflow/water_flow_scroller_test_ng.cpp | 102 ++-- .../waterflow/water_flow_sw_layout_test.cpp | 318 +---------- .../pattern/waterflow/water_flow_test_ng.cpp | 180 +----- .../waterflow/water_flow_top_down_test.cpp | 370 +------------ 11 files changed, 95 insertions(+), 2081 deletions(-) diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp index 3aae0192960..8259313ca46 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.cpp @@ -11,361 +11,4 @@ * 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. - */ -#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" - -#include -#include - -#include "base/utils/utils.h" - -namespace OHOS::Ace::NG { -void WaterFlowLayoutInfoSW::Sync(int32_t itemCnt, float mainSize, float mainGap) -{ - startIndex_ = StartIndex(); - endIndex_ = EndIndex(); - if (startIndex_ <= endIndex_) { - storedOffset_ = lanes_[idxToLane_.at(startIndex_)].startPos; - } - delta_ = 0.0f; - lastMainSize_ = mainSize; - mainGap_ = mainGap; - startPos_ = StartPos(); - endPos_ = EndPos(); - - itemStart_ = (startIndex_ == 0 && NonNegative(startPos_)) || GreatOrEqual(startPos_, mainSize); - itemEnd_ = endIndex_ == itemCnt - 1 || (itemCnt > 0 && NonPositive(endPos_)); - if (!itemEnd_) { - footerHeight_ = 0.0f; - } - offsetEnd_ = itemEnd_ && LessOrEqual(endPos_ + footerHeight_, mainSize); - maxHeight_ = std::max(endPos_ - startPos_ + footerHeight_, maxHeight_); - - synced_ = true; -} - -float WaterFlowLayoutInfoSW::DistanceToTop(int32_t itemIdx, float mainGap) const -{ - if (!ItemInView(itemIdx)) { - return 0.0f; - } - const auto& lane = lanes_[idxToLane_.at(itemIdx)]; - float dist = lane.startPos; - for (const auto& item : lane.items_) { - if (item.idx == itemIdx) { - break; - } - dist += item.mainSize + mainGap; - } - return dist; -} - -float WaterFlowLayoutInfoSW::DistanceToBottom(int32_t itemIdx, float mainSize, float mainGap) const -{ - if (!ItemInView(itemIdx)) { - return 0.0f; - } - const auto& lane = lanes_[idxToLane_.at(itemIdx)]; - float dist = mainSize - lane.endPos; - for (auto item = lane.items_.rbegin(); item != lane.items_.rend(); ++item) { - if (item->idx == itemIdx) { - break; - } - dist += item->mainSize + mainGap; - } - return dist; -} - -bool WaterFlowLayoutInfoSW::OutOfBounds() const -{ - if (lanes_.empty()) { - return false; - } - // checking first lane is enough because re-align automatically happens when reaching start - if (itemStart_ && Positive(lanes_[0].startPos)) { - return true; - } - if (itemEnd_) { - return std::all_of(lanes_.begin(), lanes_.end(), - [mainSize = lastMainSize_](const Lane& lane) { return LessNotEqual(lane.endPos, mainSize); }); - } - return false; -} - -OverScrollOffset WaterFlowLayoutInfoSW::GetOverScrolledDelta(float delta) const -{ - OverScrollOffset res {}; - if (lanes_.empty()) { - return res; - } - - if (startIndex_ == 0) { - float disToTop = -StartPos(); - if (!itemStart_) { - res.start = std::max(0.0f, delta - disToTop); - } else if (Positive(delta)) { - res.start = delta; - } else { - res.start = std::max(delta, disToTop); - } - } - - if (!itemEnd_) { - return res; - } - float disToBot = EndPos() + footerHeight_ - lastMainSize_; - if (!itemEnd_) { - res.end = std::min(0.0f, disToBot + delta); - } else if (Negative(delta)) { - res.end = delta; - } else { - res.end = std::min(delta, -disToBot); - } - return res; -} - -float WaterFlowLayoutInfoSW::CalcOverScroll(float mainSize, float delta) const -{ - if (lanes_.empty()) { - return 0.0f; - } - float res = 0.0f; - if (itemStart_) { - res = StartPos() + delta; - } - if (offsetEnd_) { - res = mainSize - (EndPos() + footerHeight_ + delta); - } - return res; -} - -float WaterFlowLayoutInfoSW::EndPos() const -{ - if (synced_) { - return endPos_; - } - return std::max_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { - return LessNotEqual(left.endPos, right.endPos); - })->endPos; -} -float WaterFlowLayoutInfoSW::StartPos() const -{ - if (synced_) { - return startPos_; - } - return std::min_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { - return LessNotEqual(left.startPos, right.startPos); - })->startPos; -} - -bool WaterFlowLayoutInfoSW::ReachStart(float prevPos, bool firstLayout) const -{ - if (!itemStart_ || lanes_.empty()) { - return false; - } - return firstLayout || Negative(prevPos); -} - -bool WaterFlowLayoutInfoSW::ReachEnd(float prevPos) const -{ - if (!offsetEnd_ || lanes_.empty()) { - return false; - } - float prevEndPos = EndPos() - (totalOffset_ - prevPos); - return GreatNotEqual(prevEndPos + footerHeight_, lastMainSize_); -} - -float WaterFlowLayoutInfoSW::GetContentHeight() const -{ - // only height in view are remembered - return maxHeight_; -} - -int32_t WaterFlowLayoutInfoSW::GetMainCount() const -{ - if (lanes_.empty()) { - return 0; - } - return static_cast(std::max_element(lanes_.begin(), lanes_.end(), [](const Lane& left, const Lane& right) { - return left.items_.size() < right.items_.size(); - })->items_.size()); -} - -float WaterFlowLayoutInfoSW::CalcTargetPosition(int32_t idx, int32_t /* crossIdx */) const -{ - if (!ItemInView(idx)) { - return Infinity(); - } - const auto& lane = lanes_[idxToLane_.at(idx)]; - float pos = 0.0f; // main-axis position of the item's top edge relative to viewport top. Positive if below viewport - float itemSize = 0.0f; - if (idx < endIndex_) { - pos = DistanceToTop(idx, mainGap_); - auto it = std::find_if( - lane.items_.begin(), lane.items_.end(), [idx](const ItemInfo& item) { return item.idx == idx; }); - itemSize = it->mainSize; - } else { - itemSize = lane.items_.back().mainSize; - pos = lane.endPos - itemSize; - } - switch (align_) { - case ScrollAlign::START: - break; - case ScrollAlign::END: - pos = pos - lastMainSize_ + itemSize; - break; - case ScrollAlign::AUTO: - if (Negative(pos)) { - /* */ - } else if (GreatNotEqual(pos + itemSize, lastMainSize_)) { - pos = pos - lastMainSize_ + itemSize; - } else { - pos = 0.0f; // already in viewport, no movement needed - } - break; - case ScrollAlign::CENTER: - pos = pos - (lastMainSize_ - itemSize) / 2.0f; - break; - default: - pos = 0.0f; - break; - } - // convert to absolute position - return pos - totalOffset_; -} - -void WaterFlowLayoutInfoSW::Reset() -{ - jumpIndex_ = startIndex_; - delta_ = DistanceToTop(startIndex_, mainGap_); - lanes_.clear(); - idxToLane_.clear(); - maxHeight_ = 0.0f; - synced_ = false; -} - -int32_t WaterFlowLayoutInfoSW::EndIndex() const -{ - if (synced_) { - return endIndex_; - } - int32_t maxIdx = -1; - for (const auto& lane : lanes_) { - if (lane.items_.empty()) { - continue; - } - maxIdx = std::max(maxIdx, lane.items_.back().idx); - } - return maxIdx; -} - -int32_t WaterFlowLayoutInfoSW::StartIndex() const -{ - if (synced_) { - return startIndex_; - } - auto minIdx = Infinity(); - for (const auto& lane : lanes_) { - if (lane.items_.empty()) { - continue; - } - minIdx = std::min(minIdx, lane.items_.front().idx); - } - return minIdx; -} - -int32_t WaterFlowLayoutInfoSW::GetCrossIndex(int32_t itemIndex) const -{ - if (ItemInView(itemIndex)) { - return static_cast(idxToLane_.at(itemIndex)); - } - return -1; -} - -void WaterFlowLayoutInfoSW::ResetBeforeJump(float laneBasePos) -{ - std::for_each(lanes_.begin(), lanes_.end(), [&laneBasePos](auto& lane) { - lane.items_.clear(); - lane.startPos = laneBasePos; - lane.endPos = laneBasePos; - }); - totalOffset_ = 0.0f; - maxHeight_ = 0.0f; - idxToLane_.clear(); - synced_ = false; -} - -std::string WaterFlowLayoutInfoSW::Lane::ToString() const -{ - std::string res = "{StartPos: " + std::to_string(startPos) + " EndPos: " + std::to_string(endPos) + " "; - if (items_.empty()) { - res += "empty"; - } else { - res += "Items ["; - for (const auto& item : items_) { - res += std::to_string(item.idx) + " "; - } - res += "] "; - } - res += "}"; - return res; -} - -bool WaterFlowLayoutInfoSW::ItemCloseToView(int32_t idx) const -{ - if (lanes_.empty() || - std::all_of(lanes_.begin(), lanes_.end(), [](const Lane& lane) { return lane.items_.empty(); })) { - return false; - } - int32_t startIdx = StartIndex(); - int32_t endIdx = EndIndex(); - using std::abs, std::min; - return min(abs(idx - endIdx), abs(idx - startIdx)) < endIdx - startIdx + 1; -} - -void WaterFlowLayoutInfoSW::ClearDataFrom(int32_t idx, float mainGap) -{ - for (auto it = idxToLane_.begin(); it != idxToLane_.end();) { - if (it->first >= idx) { - it = idxToLane_.erase(it); // Erase and get the iterator to the next element - } else { - ++it; // Move to the next element - } - } - for (auto& lane : lanes_) { - while (!lane.items_.empty()) { - if (lane.items_.back().idx >= idx) { - lane.endPos -= lane.items_.back().mainSize + mainGap; - lane.items_.pop_back(); - } else { - lane.endPos = std::max(lane.endPos, lane.startPos); - break; - } - } - } -} - -float WaterFlowLayoutInfoSW::TopFinalPos() const -{ - return -(StartPos() + delta_); -}; - -float WaterFlowLayoutInfoSW::BottomFinalPos(float viewHeight) const -{ - return -(EndPos() + delta_ + footerHeight_) + std::min(maxHeight_, viewHeight); -}; - -bool WaterFlowLayoutInfoSW::IsMisaligned() const -{ - if (lanes_.empty()) { - return false; - } - if (!itemStart_ || !NearZero(StartPos())) { - return false; - } - bool laneNotAligned = std::any_of(lanes_.begin(), lanes_.end(), [](const auto& lane) { - return !NearZero(lane.startPos); - }); - return laneNotAligned || lanes_[0].items_.front().idx != 0; -} -} // namespace OHOS::Ace::NG + */ \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h index 56a4caef1cf..5f57dd12fb7 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h @@ -15,182 +15,4 @@ #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_SW_H #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_SW_H - -#include -#include -#include - -#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" - -namespace OHOS::Ace::NG { - -/** - * @brief Layout Data structure for Sliding Window version of WaterFlowLayout - */ -class WaterFlowLayoutInfoSW : public WaterFlowLayoutInfoBase { - DECLARE_ACE_TYPE(WaterFlowLayoutInfoSW, WaterFlowLayoutInfoBase); - -public: - WaterFlowLayoutMode Mode() const override - { - return WaterFlowLayoutMode::SLIDING_WINDOW; - } - - float Offset() const override - { - return totalOffset_; - } - int32_t FirstIdx() const override - { - return startIndex_; - } - - void UpdateOffset(float delta) override - { - delta_ = delta; - synced_ = false; - } - - int32_t GetCrossIndex(int32_t itemIndex) const override; - - OverScrollOffset GetOverScrolledDelta(float delta) const override; - - float CalcOverScroll(float mainSize, float delta) const override; - - bool ReachStart(float prevPos, bool firstLayout) const override; - - bool ReachEnd(float prevPos) const override; - - bool OutOfBounds() const override; - - float GetContentHeight() const override; - - float CalcTargetPosition(int32_t idx, int32_t crossIdx) const override; - - float GetDelta(float prevPos) const override - { - return prevPos - totalOffset_; - } - - int32_t GetMainCount() const override; - int32_t GetCrossCount() const override - { - return lanes_.size(); - } - - float CurrentPos() const override - { - return 0.0f; - } - float TopFinalPos() const override; - float BottomFinalPos(float viewHeight) const override; - - void Reset() override; - - /** - * @brief reset layout data before performing a jump. - * - * @param laneBasePos base value for lane's start&end position. - */ - void ResetBeforeJump(float laneBasePos); - - void BeginUpdate() - { - synced_ = false; - } - /** - * @brief synchronize data after update is completed. - * - * @param itemCnt number of FlowItems. - * @param mainSize main-axis length of the viewport. - * @param mainGap main-axis gap between items. - */ - void Sync(int32_t itemCnt, float mainSize, float mainGap); - - /** - * @brief Calculates distance from the item's top edge to the top of the viewport. - * - * @param item index - * @return positive result when item's top edge is below viewport. - */ - float DistanceToTop(int32_t item, float mainGap) const; - - /** - * @brief Calculates distance from the item's bottom edge to the bottom of the viewport. - * - * @param item index - * @param mainSize of the viewport - * @return positive result when item's bottom edge is above viewport. - */ - float DistanceToBottom(int32_t item, float mainSize, float mainGap) const; - - /** - * @brief Check if the layout is misaligned. - * - * If we jump and scroll back to top, the staring items might not be aligned with the top boundary. - * @return true if 1. any lane misaligned with top boundary. - * 2. the first item is not in the first lane. - */ - bool IsMisaligned() const; - - int32_t StartIndex() const; - int32_t EndIndex() const; - inline bool ItemInView(int32_t idx) const - { - return !lanes_.empty() && idx >= StartIndex() && idx <= EndIndex(); - } - /** - * @param idx of the item. - * @return true the item is approximately within 1 full-viewport distance. - */ - bool ItemCloseToView(int32_t idx) const; - - /** - * @return maximum end position of items in lanes_. - */ - float EndPos() const; - /** - * @return minimum start position of items in lanes_. - */ - float StartPos() const; - - void ClearDataFrom(int32_t idx, float mainGap); - - struct Lane; - std::vector lanes_; - // mapping of all items previously or currently in lanes_. - std::unordered_map idxToLane_; - - float delta_ = 0.0f; - float totalOffset_ = 0.0f; // record total offset when continuously scrolling. Reset when jumped - float mainGap_ = 0.0f; // update this at the end of a layout - - // maximum content height encountered so far, mainly for comparing content and viewport height - float maxHeight_ = 0.0f; - float footerHeight_ = 0.0f; - -private: - /* cache */ - float startPos_ = 0.0f; - float endPos_ = 0.0f; - - bool synced_ = false; - - struct ItemInfo; -}; - -struct WaterFlowLayoutInfoSW::ItemInfo { - int32_t idx = -1; - float mainSize = 0.0f; -}; - -struct WaterFlowLayoutInfoSW::Lane { - std::string ToString() const; - - float startPos = 0.0f; - float endPos = 0.0f; - std::deque items_; -}; -} // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_LAYOUT_INFO_SW_H diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp index 023670a95be..8259313ca46 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.cpp @@ -11,513 +11,4 @@ * 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. - */ -#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h" - -#include -#include -#include - -#include "base/utils/utils.h" -#include "core/components/scroll/scroll_controller_base.h" -#include "core/components_ng/base/frame_node.h" -#include "core/components_ng/layout/layout_wrapper.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_utils.h" -#include "core/components_ng/property/measure_utils.h" -#include "core/components_ng/property/templates_parser.h" - -namespace OHOS::Ace::NG { -void WaterFlowLayoutSW::Measure(LayoutWrapper* wrapper) -{ - info_->BeginUpdate(); - wrapper_ = wrapper; - auto props = DynamicCast(wrapper->GetLayoutProperty()); - info_->axis_ = axis_ = props->GetAxis(); - - auto [size, matchChildren] = WaterFlowLayoutUtils::PreMeasureSelf(wrapper_, axis_); - Init(size); - CheckReset(); - - if (info_->jumpIndex_ != EMPTY_JUMP_INDEX) { - MeasureOnJump(info_->jumpIndex_, info_->align_); - } else if (info_->targetIndex_) { - MeasureToTarget(*info_->targetIndex_); - } else { - MeasureOnOffset(info_->delta_); - } - if (matchChildren) { - PostMeasureSelf(size.CrossSize(axis_)); - } - - info_->Sync(itemCnt_, mainLen_, mainGap_); - wrapper->SetCacheCount(props->GetCachedCountValue(1)); -} - -void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) -{ - if (info_->lanes_.empty()) { - return; - } - - auto props = DynamicCast(wrapper->GetLayoutProperty()); - auto padding = props->CreatePaddingAndBorder(); - OffsetF paddingOffset { padding.top.value_or(0.0f), padding.top.value_or(0.0f) }; - - bool reverse = props->IsReverse(); - bool rtl = props->GetNonAutoLayoutDirection() == TextDirection::RTL && axis_ == Axis::VERTICAL; - float selfCrossLen = wrapper->GetGeometryNode()->GetContentSize().CrossSize(axis_); - - float crossPos = rtl ? selfCrossLen + mainGap_ : 0.0f; - for (size_t i = 0; i < info_->lanes_.size(); ++i) { - if (rtl) { - crossPos -= itemCrossSize_[i] + mainGap_; - } - auto& lane = info_->lanes_[i]; - float mainPos = lane.startPos; - for (auto& item : lane.items_) { - auto child = wrapper->GetOrCreateChildByIndex(nodeIdx(item.idx)); - if (!child) { - continue; - } - auto childNode = child->GetGeometryNode(); - if (reverse) { - mainPos = mainLen_ - item.mainSize - mainPos; - } - auto offset = axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }; - childNode->SetMarginFrameOffset(offset + paddingOffset); - - if (child->CheckNeedForceMeasureAndLayout()) { - child->Layout(); - } else { - child->GetHostNode()->ForceSyncGeometryNode(); - } - mainPos += item.mainSize + mainGap_; - } - if (!rtl) { - crossPos += itemCrossSize_[i] + mainGap_; - } - } - - wrapper->SetActiveChildRange(nodeIdx(info_->startIndex_), nodeIdx(info_->endIndex_)); - LayoutFooter(paddingOffset, reverse); -} - -void WaterFlowLayoutSW::Init(const SizeF& frameSize) -{ - // omit footer from children count - itemCnt_ = wrapper_->GetTotalChildCount() - info_->footerIndex_ - 1; - - auto props = DynamicCast(wrapper_->GetLayoutProperty()); - auto scale = props->GetLayoutConstraint()->scaleProperty; - auto rowsGap = ConvertToPx(props->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0); - auto columnsGap = ConvertToPx(props->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0); - mainGap_ = { axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap }; - crossGap_ = { axis_ == Axis::VERTICAL ? columnsGap : rowsGap }; - - mainLen_ = frameSize.MainSize(axis_); - float crossSize = frameSize.CrossSize(axis_); - std::pair, bool> cross; - auto rowsTemplate = props->GetRowsTemplate().value_or("1fr"); - auto columnsTemplate = props->GetColumnsTemplate().value_or("1fr"); - if (axis_ == Axis::VERTICAL) { - cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(columnsTemplate), crossSize, crossGap_, itemCnt_); - } else { - cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, itemCnt_); - } - if (cross.second) { - crossGap_ = 0.0f; - } - - if (info_->lanes_.empty()) { - info_->lanes_.resize(cross.first.size()); - } - for (const auto& len : cross.first) { - itemCrossSize_.push_back(static_cast(len)); - } - if (itemCrossSize_.empty()) { - itemCrossSize_.push_back(crossSize); - } -} - -void WaterFlowLayoutSW::CheckReset() -{ - int32_t updateIdx = wrapper_->GetHostNode()->GetChildrenUpdated(); - if (updateIdx == -1) { - return; - } - if (info_->footerIndex_ == 0 && updateIdx == 0) { - // footer updated, no need to reset or clear cache - return; - } - // convert children node index to item index - updateIdx -= (info_->footerIndex_ + 1); - info_->ClearDataFrom(updateIdx, mainGap_); - if (updateIdx <= info_->startIndex_) { - info_->jumpIndex_ = std::min(info_->startIndex_, itemCnt_ - 1); - info_->align_ = ScrollAlign::START; - } - wrapper_->GetHostNode()->ChildrenUpdatedFrom(-1); -} - -void WaterFlowLayoutSW::MeasureOnOffset(float delta) -{ - ApplyDelta(delta); - AdjustOverScroll(); - // clear out items outside viewport after position change - if (Positive(delta)) { - ClearBack(mainLen_); - } else { - ClearFront(); - } -} - -void WaterFlowLayoutSW::ApplyDelta(float delta) -{ - info_->totalOffset_ += delta; - for (auto& lane : info_->lanes_) { - lane.startPos += delta; - lane.endPos += delta; - } - - if (Positive(delta)) { - // positive offset is scrolling upwards - FillFront(0.0f, info_->StartIndex() - 1, 0); - } else { - FillBack(mainLen_, info_->EndIndex() + 1, itemCnt_ - 1); - } -} - -void WaterFlowLayoutSW::MeasureToTarget(int32_t targetIdx) -{ - if (itemCnt_ == 0) { - return; - } - if (targetIdx < info_->startIndex_) { - FillFront(-FLT_MAX, info_->startIndex_ - 1, targetIdx); - } else if (targetIdx > info_->endIndex_) { - FillBack(FLT_MAX, info_->endIndex_ + 1, targetIdx); - } -} - -// [lane start/end position, lane index] -using lanePos = std::pair; -void WaterFlowLayoutSW::FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx) -{ - idx = std::max(idx, 0); - maxChildIdx = std::min(maxChildIdx, itemCnt_ - 1); - if (info_->idxToLane_.count(idx)) { - RecoverBack(viewportBound, idx, maxChildIdx); - } - std::priority_queue, std::greater<>> q; - for (size_t i = 0; i < info_->lanes_.size(); ++i) { - float endPos = info_->lanes_[i].endPos; - if (LessNotEqual(endPos + mainGap_, viewportBound)) { - q.push({ endPos, i }); - } - } - - auto props = DynamicCast(wrapper_->GetLayoutProperty()); - while (!q.empty() && idx <= maxChildIdx) { - auto [_, laneIdx] = q.top(); - q.pop(); - info_->idxToLane_[idx] = laneIdx; - float endPos = FillBackHelper(props, idx++, laneIdx); - if (LessNotEqual(endPos, viewportBound)) { - q.push({ endPos, laneIdx }); - } - } -} - -namespace { -// max heap but with smaller laneIdx at the top -struct MaxHeapCmp { - bool operator()(const lanePos& left, const lanePos& right) - { - if (NearEqual(left.first, right.first)) { - return left.second > right.second; - } - return LessNotEqual(left.first, right.first); - } -}; -} // namespace -void WaterFlowLayoutSW::FillFront(float viewportBound, int32_t idx, int32_t minChildIdx) -{ - idx = std::min(itemCnt_ - 1, idx); - minChildIdx = std::max(minChildIdx, 0); - if (info_->idxToLane_.count(idx)) { - RecoverFront(viewportBound, idx, minChildIdx); - } - std::priority_queue, MaxHeapCmp> q; - for (size_t i = 0; i < info_->lanes_.size(); ++i) { - float startPos = info_->lanes_[i].startPos; - if (GreatNotEqual(startPos - mainGap_, viewportBound)) { - q.push({ startPos, i }); - } - } - - auto props = DynamicCast(wrapper_->GetLayoutProperty()); - while (!q.empty() && idx >= minChildIdx) { - auto [_, laneIdx] = q.top(); - q.pop(); - info_->idxToLane_[idx] = laneIdx; - float startPos = FillFrontHelper(props, idx--, laneIdx); - if (GreatNotEqual(startPos - mainGap_, viewportBound)) { - q.push({ startPos, laneIdx }); - } - } -} - -float WaterFlowLayoutSW::FillBackHelper(const RefPtr& props, int32_t idx, size_t laneIdx) -{ - float mainLen = MeasureChild(props, idx, laneIdx); - auto& lane = info_->lanes_[laneIdx]; - lane.endPos += mainGap_ + mainLen; - if (lane.items_.empty()) { - lane.endPos -= mainGap_; - } - lane.items_.push_back({ idx, mainLen }); - return lane.endPos; -} - -float WaterFlowLayoutSW::FillFrontHelper(const RefPtr& props, int32_t idx, size_t laneIdx) -{ - float mainLen = MeasureChild(props, idx, laneIdx); - auto& lane = info_->lanes_[laneIdx]; - lane.startPos -= mainGap_ + mainLen; - if (lane.items_.empty()) { - lane.startPos += mainGap_; - } - lane.items_.push_front({ idx, mainLen }); - return lane.startPos; -} - -void WaterFlowLayoutSW::RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx) -{ - std::unordered_set lanes; - for (size_t i = 0; i < info_->lanes_.size(); ++i) { - if (LessNotEqual(info_->lanes_[i].endPos + mainGap_, viewportBound)) { - lanes.insert(i); - } - } - - auto props = DynamicCast(wrapper_->GetLayoutProperty()); - while (!lanes.empty() && idx <= maxChildIdx && info_->idxToLane_.count(idx)) { - size_t laneIdx = info_->idxToLane_.at(idx); - float endPos = FillBackHelper(props, idx++, laneIdx); - if (GreatOrEqual(endPos + mainGap_, viewportBound)) { - lanes.erase(laneIdx); - } - } -} - -void WaterFlowLayoutSW::RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx) -{ - std::unordered_set lanes; - for (size_t i = 0; i < info_->lanes_.size(); ++i) { - float startPos = info_->lanes_[i].startPos; - if (GreatNotEqual(startPos - mainGap_, viewportBound)) { - lanes.insert(i); - } - } - auto props = DynamicCast(wrapper_->GetLayoutProperty()); - while (!lanes.empty() && idx >= minChildIdx && info_->idxToLane_.count(idx)) { - size_t laneIdx = info_->idxToLane_.at(idx); - float startPos = FillFrontHelper(props, idx--, laneIdx); - if (LessOrEqual(startPos, viewportBound)) { - lanes.erase(laneIdx); - } - } -} - -void WaterFlowLayoutSW::ClearBack(float bound) -{ - int32_t startIdx = info_->StartIndex(); - for (int32_t i = info_->EndIndex(); i >= startIdx; --i) { - size_t laneIdx = info_->idxToLane_.at(i); - auto& lane = info_->lanes_[laneIdx]; - float itemStartPos = lane.endPos - lane.items_.back().mainSize; - if (LessNotEqual(itemStartPos, bound)) { - break; - } - lane.items_.pop_back(); - lane.endPos = itemStartPos - mainGap_; - } -} - -void WaterFlowLayoutSW::ClearFront() -{ - int32_t endIdx = info_->EndIndex(); - for (int32_t i = info_->StartIndex(); i <= endIdx; ++i) { - size_t laneIdx = info_->idxToLane_.at(i); - auto& lane = info_->lanes_[laneIdx]; - float itemEndPos = lane.startPos + lane.items_.front().mainSize; - if (Positive(itemEndPos)) { - break; - } - lane.items_.pop_front(); - lane.startPos = itemEndPos + mainGap_; - } -} - -ScrollAlign WaterFlowLayoutSW::ParseAutoAlign(int32_t jumpIdx, bool inView) -{ - if (inView) { - if (Negative(info_->DistanceToTop(jumpIdx, mainGap_))) { - return ScrollAlign::START; - } - if (Negative(info_->DistanceToBottom(jumpIdx, mainLen_, mainGap_))) { - return ScrollAlign::END; - } - // item is already fully in viewport - return ScrollAlign::NONE; - } - if (jumpIdx < info_->startIndex_) { - return ScrollAlign::START; - } - return ScrollAlign::END; -} - -void WaterFlowLayoutSW::MeasureOnJump(int32_t jumpIdx, ScrollAlign align) -{ - if (jumpIdx == LAST_ITEM) { - jumpIdx = itemCnt_ - 1; - } else if (jumpIdx == itemCnt_ && info_->footerIndex_ == 0) { - // jump to footer - info_->delta_ = -Infinity(); - } - overScroll_ = false; - - bool inView = info_->ItemInView(jumpIdx); - if (align == ScrollAlign::AUTO) { - align = ParseAutoAlign(jumpIdx, inView); - } - - // If item is close, we simply scroll to it instead of triggering a reset/jump, which would change the layout. - bool closeToView = info_->ItemCloseToView(jumpIdx); - if (closeToView) { - MeasureToTarget(jumpIdx); - } - Jump(jumpIdx, align, inView || closeToView); - if (!NearZero(info_->delta_)) { - MeasureOnOffset(info_->delta_); - } else { - AdjustOverScroll(); - ClearFront(); - ClearBack(mainLen_); - } -} - -void WaterFlowLayoutSW::Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip) -{ - switch (align) { - case ScrollAlign::START: { - if (noSkip) { - ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_)); - } else { - info_->ResetBeforeJump(0.0f); - FillBack(mainLen_, jumpIdx, itemCnt_ - 1); - } - break; - } - case ScrollAlign::CENTER: { - auto props = DynamicCast(wrapper_->GetLayoutProperty()); - if (noSkip) { - float itemH = MeasureChild(props, jumpIdx, info_->idxToLane_.at(jumpIdx)); - ApplyDelta(-info_->DistanceToTop(jumpIdx, mainGap_) + (mainLen_ - itemH) / 2.0f); - } else { - info_->ResetBeforeJump(mainLen_ / 2.0f); - info_->idxToLane_ = { { jumpIdx, 0 } }; - auto& lane = info_->lanes_[0]; - float itemH = MeasureChild(props, jumpIdx, 0); - lane.startPos = (mainLen_ - itemH) / 2.0f; - lane.endPos = (mainLen_ + itemH) / 2.0f; - lane.items_.push_back({ jumpIdx, itemH }); - - FillFront(0.0f, jumpIdx - 1, 0); - FillBack(mainLen_, jumpIdx + 1, itemCnt_ - 1); - } - break; - } - case ScrollAlign::END: { - if (noSkip) { - ApplyDelta(info_->DistanceToBottom(jumpIdx, mainLen_, mainGap_)); - } else { - info_->ResetBeforeJump(mainLen_); - FillFront(0.0f, jumpIdx, 0); - } - break; - } - default: - break; - } -} - -void WaterFlowLayoutSW::AdjustOverScroll() -{ - if (info_->lanes_.empty()) { - return; - } - float maxEnd = info_->EndPos(); - float minStart = info_->StartPos(); - - if (LessOrEqual(maxEnd, mainLen_) && info_->footerIndex_ == 0) { - info_->footerHeight_ = WaterFlowLayoutUtils::MeasureFooter(wrapper_, axis_); - maxEnd += info_->footerHeight_; - } - - if (overScroll_) { - return; - } - int32_t startIdx = info_->StartIndex(); - if (startIdx == 0 && Positive(minStart)) { - ApplyDelta(-minStart); - } else if (info_->EndIndex() == itemCnt_ - 1 && LessNotEqual(maxEnd, mainLen_)) { - float delta = mainLen_ - maxEnd; - if (startIdx == 0) { - delta = std::min(-minStart, delta); - } - ApplyDelta(delta); - } -} - -float WaterFlowLayoutSW::MeasureChild(const RefPtr& props, int32_t idx, size_t lane) -{ - auto child = wrapper_->GetOrCreateChildByIndex(nodeIdx(idx)); - CHECK_NULL_RETURN(child, 0.0f); - child->Measure( - WaterFlowLayoutUtils::CreateChildConstraint({ itemCrossSize_[lane], mainLen_, axis_ }, props, child)); - return child->GetGeometryNode()->GetMarginFrameSize().MainSize(info_->axis_); -} - -void WaterFlowLayoutSW::LayoutFooter(const OffsetF& paddingOffset, bool reverse) -{ - float endPos = info_->EndPos(); - if (info_->footerIndex_ != 0 || GreatOrEqual(endPos, mainLen_)) { - return; - } - auto footer = wrapper_->GetOrCreateChildByIndex(0); - float mainPos = endPos + mainGap_; - if (reverse) { - mainPos = mainLen_ - info_->footerHeight_ - mainPos; - } - footer->GetGeometryNode()->SetMarginFrameOffset( - (axis_ == Axis::VERTICAL) ? OffsetF(0.0f, mainPos) + paddingOffset : OffsetF(mainPos, 0.0f) + paddingOffset); - footer->Layout(); -} - -void WaterFlowLayoutSW::PostMeasureSelf(float selfCrossLen) -{ - mainLen_ = info_->GetContentHeight(); - SizeF selfSize = (axis_ == Axis::VERTICAL) ? SizeF(selfCrossLen, mainLen_) : SizeF(mainLen_, selfCrossLen); - auto props = wrapper_->GetLayoutProperty(); - AddPaddingToSize(props->CreatePaddingAndBorder(), selfSize); - wrapper_->GetGeometryNode()->SetFrameSize(selfSize); -} - -inline int32_t WaterFlowLayoutSW::nodeIdx(int32_t idx) const -{ - return idx + info_->footerIndex_ + 1; -} -} // namespace OHOS::Ace::NG + */ \ No newline at end of file diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h index a5b86344ed5..194b4a75d4d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_sw.h @@ -16,10 +16,8 @@ #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SW_LAYOUT_H #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SW_LAYOUT_H -#include "core/components/scroll/scroll_controller_base.h" -#include "core/components_ng/layout/layout_wrapper.h" -#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_info_base.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_algorithm_base.h" namespace OHOS::Ace::NG { @@ -27,120 +25,9 @@ class ACE_EXPORT WaterFlowLayoutSW : public WaterFlowLayoutBase { DECLARE_ACE_TYPE(WaterFlowLayoutSW, WaterFlowLayoutBase); public: - explicit WaterFlowLayoutSW(const RefPtr& info) : info_(info) {} - void Measure(LayoutWrapper* wrapper) override; - void Layout(LayoutWrapper* wrapper) override; - - void SetCanOverScroll(bool value) override - { - overScroll_ = value; - } - -private: - void Init(const SizeF& frameSize); - void CheckReset(); - - void MeasureOnOffset(float delta); - - void ApplyDelta(float delta); - - void MeasureToTarget(int32_t targetIdx); - - /** - * @brief When the item is within or close to viewport, layout is preserved and we merely apply an offset. - * When jumping to an item further away, the current layout would be reset for better layout performance. - * - * @param jumpIdx - * @param align ScrollAlign - * @param mainSize of the viewport - */ - void MeasureOnJump(int32_t jumpIdx, ScrollAlign align); - - /** - * @brief Helper to perform jumping to an item. - * - * @param noSkip true if we can directly apply offset to reach the target. - */ - void Jump(int32_t jumpIdx, ScrollAlign align, bool noSkip); - - /** - * @brief convert Auto align to other Align types. - * - * @param inView true if item is between startIndex and endIndex. - * @return converted ScrollAlign type. - */ - ScrollAlign ParseAutoAlign(int32_t jumpIdx, bool inView); - - /** - * @brief fills the viewport backward until [viewportBound] is reached / idx < minChildIdx. - * - * @param viewportBound boundary to fill towards. - * @param idx first item index to fill with. - * @param minChildIdx smallest item index to fill before stopping. - */ - void FillFront(float viewportBound, int32_t idx, int32_t minChildIdx); - /** - * @brief fills the viewport backward with cached idx -> lane mapping. - */ - void RecoverFront(float viewportBound, int32_t& idx, int32_t minChildIdx); - /** - * @return new startPos of the filled lane. - */ - float FillFrontHelper(const RefPtr& props, int32_t idx, size_t laneIdx); - /** - * @brief Clear items above the viewport. - * Iterate by index to keep item range continuous. - */ - void ClearFront(); - - /** - * @brief fills the viewport forward until [viewportBound] is reached / idx > maxChildIdx. - * - * @param viewportBound boundary to fill towards. - * @param idx first item index to fill with. - * @param maxChildIdx greatest item index to fill before stopping. - */ - void FillBack(float viewportBound, int32_t idx, int32_t maxChildIdx); - /** - * @brief fills the viewport backward with cached idx -> lane mapping. - */ - void RecoverBack(float viewportBound, int32_t& idx, int32_t maxChildIdx); - /** - * @return new endPos of the filled lane. - */ - float FillBackHelper(const RefPtr& props, int32_t idx, size_t laneIdx); - /** - * @brief Clear items below the viewport. - * - * @param bound of the viewport - */ - void ClearBack(float bound); - - void AdjustOverScroll(); - - /** - * @brief If need to match children size, adjust self size after measuring children. - */ - void PostMeasureSelf(float selfCrossLen); - - float MeasureChild(const RefPtr& props, int32_t idx, size_t lane); - - void LayoutFooter(const OffsetF& paddingOffset, bool reverse); - - // convert FlowItem's index to children node index. - inline int32_t nodeIdx(int32_t idx) const; - - LayoutWrapper* wrapper_ {}; - RefPtr info_; - - int32_t itemCnt_ = 0; - Axis axis_ {}; - std::vector itemCrossSize_; - float mainLen_ = 0.0f; - float mainGap_ = 0.0f; - float crossGap_ = 0.0f; - - bool overScroll_ = true; + explicit WaterFlowLayoutSW(const RefPtr& info) : info_(info) {} + void SetCanOverScroll(bool canOverScroll) override {} + RefPtr info_; }; } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_WATERFLOW_WATER_FLOW_SW_LAYOUT_H diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp index 33e500b56ae..878a481333d 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_info.cpp @@ -29,7 +29,7 @@ RefPtr WaterFlowLayoutInfoBase::Create(WaterFlowLayoutM { switch (mode) { case WaterFlowLayoutMode::SLIDING_WINDOW: - return MakeRefPtr(); + return nullptr; default: return MakeRefPtr(); } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp index 88c030467ab..6a2c9b203ec 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -131,7 +131,7 @@ RefPtr WaterFlowPattern::CreateLayoutAlgorithm() if (sections_ || SystemProperties::WaterFlowUseSegmentedLayout()) { algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); } else if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { - algorithm = MakeRefPtr(DynamicCast(layoutInfo_)); + algorithm = MakeRefPtr(layoutInfo_); } else { int32_t footerIndex = -1; auto footer = footer_.Upgrade(); @@ -451,14 +451,19 @@ void WaterFlowPattern::OnSectionChanged(int32_t start) void WaterFlowPattern::OnSectionChangedNow(int32_t start) { + if (layoutInfo_->Mode() == LayoutMode::SLIDING_WINDOW) { + return; + } + auto info = DynamicCast(layoutInfo_); + CHECK_NULL_VOID(info); auto host = GetHost(); CHECK_NULL_VOID(host); int32_t childUpdateIdx = host->GetChildrenUpdated(); if (sections_->IsSpecialUpdateCAPI(childUpdateIdx)) { start += sections_->GetSectionInfo().size(); } - layoutInfo_.InitSegments(sections_->GetSectionInfo(), start); - layoutInfo_.margins_.clear(); + info->InitSegments(sections_->GetSectionInfo(), start); + info->margins_.clear(); MarkDirtyNodeSelf(); } @@ -533,33 +538,15 @@ void WaterFlowPattern::MarkDirtyNodeSelf() host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); } -namespace { -// check if layout is misaligned after a scroll event -void CheckMisalignment(const RefPtr& info) -{ - if (info->Mode() != WaterFlowLayoutMode::SLIDING_WINDOW) { - return; - } - auto infoSW = AceType::DynamicCast(info); - if (infoSW->IsMisaligned()) { - infoSW->ResetBeforeJump(0.0f); - info->jumpIndex_ = 0; - info->align_ = ScrollAlign::START; - } -} -} // namespace - void WaterFlowPattern::OnScrollEndCallback() { scrollStop_ = true; - CheckMisalignment(layoutInfo_); MarkDirtyNodeSelf(); } void WaterFlowPattern::OnAnimateStop() { scrollStop_ = true; - CheckMisalignment(layoutInfo_); MarkDirtyNodeSelf(); } diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp index 4a95ac6646d..11f33fba762 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_segmented_layout.cpp @@ -58,7 +58,7 @@ void WaterFlowSegmentedLayout::Measure(LayoutWrapper* wrapper) if (!IsDataValid(info_)) { return; } - if (info_.childrenCount_ == 0) { + if (info_->childrenCount_ == 0) { return; } @@ -87,7 +87,7 @@ void WaterFlowSegmentedLayout::Layout(LayoutWrapper* wrapper) if (!IsDataValid(info_)) { return; } - if (info_.childrenCount_ == 0) { + if (info_->childrenCount_ == 0) { return; } diff --git a/test/unittest/core/pattern/waterflow/water_flow_scroller_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_scroller_test_ng.cpp index ad8bfc99fed..3c1d684fd0b 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_scroller_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_scroller_test_ng.cpp @@ -88,44 +88,44 @@ HWTEST_F(WaterFlowScrollerTestNg, UpdateCurrentOffset002, TestSize.Level1) model.SetEdgeEffect(EdgeEffect::SPRING, false); CreateItem(TOTAL_LINE_NUMBER * 2); }); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 0); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 10); - EXPECT_TRUE(pattern_->layoutInfo_.itemStart_); - EXPECT_FALSE(pattern_->layoutInfo_.itemEnd_); - EXPECT_FALSE(pattern_->layoutInfo_.offsetEnd_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 10); + EXPECT_TRUE(pattern_->layoutInfo_->itemStart_); + EXPECT_FALSE(pattern_->layoutInfo_->itemEnd_); + EXPECT_FALSE(pattern_->layoutInfo_->offsetEnd_); /** * @tc.steps: step2. Scroll down * @tc.expected: startIndex_ = 1 endIndex_ = 13. */ UpdateCurrentOffset(-2 * ITEM_HEIGHT); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 1); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 13); - EXPECT_FALSE(pattern_->layoutInfo_.itemStart_); - EXPECT_FALSE(pattern_->layoutInfo_.itemEnd_); - EXPECT_FALSE(pattern_->layoutInfo_.offsetEnd_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 1); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 13); + EXPECT_FALSE(pattern_->layoutInfo_->itemStart_); + EXPECT_FALSE(pattern_->layoutInfo_->itemEnd_); + EXPECT_FALSE(pattern_->layoutInfo_->offsetEnd_); /** * @tc.steps: step3. scroll down * @tc.expected: startIndex_ = 11 endIndex_ = 19. */ UpdateCurrentOffset(-10000.f); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 11); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 19); - EXPECT_FALSE(pattern_->layoutInfo_.itemStart_); - EXPECT_TRUE(pattern_->layoutInfo_.itemEnd_); - EXPECT_TRUE(pattern_->layoutInfo_.offsetEnd_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 11); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 19); + EXPECT_FALSE(pattern_->layoutInfo_->itemStart_); + EXPECT_TRUE(pattern_->layoutInfo_->itemEnd_); + EXPECT_TRUE(pattern_->layoutInfo_->offsetEnd_); /** * @tc.steps: step4. scroll up * @tc.expected: startIndex_ = 7 endIndex_ = 19. */ UpdateCurrentOffset(2 * ITEM_HEIGHT); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 7); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 19); - EXPECT_FALSE(pattern_->layoutInfo_.itemStart_); - EXPECT_TRUE(pattern_->layoutInfo_.itemEnd_); - EXPECT_FALSE(pattern_->layoutInfo_.offsetEnd_); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 7); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 19); + EXPECT_FALSE(pattern_->layoutInfo_->itemStart_); + EXPECT_TRUE(pattern_->layoutInfo_->itemEnd_); + EXPECT_FALSE(pattern_->layoutInfo_->offsetEnd_); } /** @@ -148,8 +148,8 @@ HWTEST_F(WaterFlowScrollerTestNg, UpdateCurrentOffset003, TestSize.Level1) pattern_->SetAnimateCanOverScroll(true); pattern_->UpdateCurrentOffset(10000, SCROLL_FROM_UPDATE); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.firstIndex_, 0); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->FirstIdx(), 0); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 0); /** * @tc.steps: step1. create waterFlow @@ -159,8 +159,8 @@ HWTEST_F(WaterFlowScrollerTestNg, UpdateCurrentOffset003, TestSize.Level1) pattern_->SetAnimateCanOverScroll(true); pattern_->UpdateCurrentOffset(-99999, SCROLL_FROM_UPDATE); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.firstIndex_, 19); - EXPECT_EQ(pattern_->layoutInfo_.endIndex_, 19); + EXPECT_EQ(pattern_->layoutInfo_->FirstIdx(), 19); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 19); } /** @@ -192,9 +192,9 @@ HWTEST_F(WaterFlowScrollerTestNg, PositionController001, TestSize.Level1) * @tc.steps: step2. Test JumpTo func. */ controller->JumpTo(2, false, ScrollAlign::START, 0); - EXPECT_EQ(pattern_->layoutInfo_.jumpIndex_, 2); + EXPECT_EQ(pattern_->layoutInfo_->jumpIndex_, 2); controller->JumpTo(0, false, ScrollAlign::START, 0); - EXPECT_EQ(pattern_->layoutInfo_.jumpIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->jumpIndex_, 0); } /** @@ -483,51 +483,51 @@ HWTEST_F(WaterFlowScrollerTestNg, ScrollToIndex002, TestSize.Level1) pattern_->ScrollToIndex(3, false, ScrollAlign::AUTO); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 0); - EXPECT_EQ(pattern_->layoutInfo_.storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0); pattern_->ScrollToIndex(15, false, ScrollAlign::START); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 15); - EXPECT_EQ(pattern_->layoutInfo_.storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, -1100); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 15); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -1100); pattern_->ScrollToIndex(LAST_ITEM); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 19); - EXPECT_EQ(pattern_->layoutInfo_.storedOffset_, -100); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, -1500); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 19); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -1500); pattern_->ScrollToIndex(0, false, ScrollAlign::START); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 0); - EXPECT_EQ(pattern_->layoutInfo_.storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0); pattern_->ScrollToIndex(15, false, ScrollAlign::AUTO); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 7); - EXPECT_EQ(pattern_->layoutInfo_.storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, -500); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 7); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -500); pattern_->ScrollToIndex(7, false, ScrollAlign::CENTER); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 3); - EXPECT_EQ(pattern_->layoutInfo_.storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, -200); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 3); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -200); pattern_->ScrollToIndex(14, false, ScrollAlign::END); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 3); - EXPECT_EQ(pattern_->layoutInfo_.storedOffset_, -100); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, -300); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 3); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -300); pattern_->ScrollToIndex(2, false, ScrollAlign::AUTO); FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_.startIndex_, 1); - EXPECT_EQ(pattern_->layoutInfo_.storedOffset_, -100); - EXPECT_EQ(pattern_->layoutInfo_.currentOffset_, -100); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 1); + EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -100); } /** @@ -564,7 +564,7 @@ HWTEST_F(WaterFlowScrollerTestNg, ScrollToIndex003, TestSize.Level1) pattern_->ScrollPage(false); FlushLayoutTask(frameNode_); - EXPECT_LT(pattern_->layoutInfo_.currentOffset_, 0.f); + EXPECT_LT(pattern_->layoutInfo_->Offset(), 0.f); pattern_->ScrollToIndex(3, true, ScrollAlign::AUTO); FlushLayoutTask(frameNode_); diff --git a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp index 3d006cf3b94..8259313ca46 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_sw_layout_test.cpp @@ -11,320 +11,4 @@ * 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. - */ -#include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" - -#include "core/components_ng/pattern/waterflow/layout/sliding_window/water_flow_layout_info_sw.h" -namespace OHOS::Ace::NG { -class WaterFlowSWTest : public WaterFlowTestNg { -protected: - void GetInstance() override - { - WaterFlowTestNg::GetInstance(); - info_ = AceType::DynamicCast(pattern_->layoutInfo_); - EXPECT_TRUE(info_); - } - - RefPtr info_; -}; - -/** - * @tc.name: Regular001 - * @tc.desc: waterFlow with fixed column - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, Regular001, TestSize.Level1) -{ - CreateWithItem([](WaterFlowModelNG model) { - ViewAbstract::SetWidth(CalcLength(400.0f)); - ViewAbstract::SetHeight(CalcLength(200.f)); - model.SetColumnsTemplate("1fr 1fr 1fr"); - }); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->lanes_[0].items_.size(), 2); - EXPECT_EQ(info_->lanes_[0].items_.back().idx, 3); - EXPECT_EQ(info_->lanes_[0].items_.back().mainSize, 200.0f); - EXPECT_EQ(info_->lanes_[0].endPos, 300.0f); - EXPECT_EQ(info_->lanes_[1].items_.back().idx, 1); - EXPECT_EQ(info_->lanes_[1].endPos, 200.0f); - EXPECT_EQ(info_->lanes_[2].endPos, 200.0f); - EXPECT_EQ(info_->lanes_[2].items_.back().idx, 4); - EXPECT_EQ(info_->startIndex_, 0); - EXPECT_EQ(info_->endIndex_, 4); -} - -/** - * @tc.name: Reset001 - * @tc.desc: waterFlow children update - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, Reset001, TestSize.Level1) -{ - CreateWithItem([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - model.SetFooter(GetDefaultHeaderBuilder()); - }); - pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 0); - EXPECT_EQ(info_->endIndex_, 9); - for (int i = 0; i < 5; i++) { - frameNode_->RemoveChildAtIndex(6); - } - frameNode_->ChildrenUpdatedFrom(6); - frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 0); - EXPECT_EQ(info_->endIndex_, 4); - EXPECT_FALSE(info_->idxToLane_.count(5)); - EXPECT_EQ(GetChildY(frameNode_, 1), 0.0f); - EXPECT_EQ(GetChildY(frameNode_, 5), 200.0f); - EXPECT_EQ(GetChildY(frameNode_, 0), 400.0f); -} - -/** - * @tc.name: Reset002 - * @tc.desc: waterFlow children update before startIdx - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, Reset002, TestSize.Level1) -{ - CreateWithItem([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - model.SetFooter(GetDefaultHeaderBuilder()); - CreateItem(100); - }); - pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 99); - EXPECT_EQ(info_->endIndex_, 109); - for (int i = 0; i < 5; i++) { - frameNode_->RemoveChildAtIndex(6); - } - frameNode_->ChildrenUpdatedFrom(6); - frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 95); - EXPECT_EQ(info_->endIndex_, 104); - EXPECT_TRUE(info_->offsetEnd_); - EXPECT_EQ(GetChildY(frameNode_, 95), -150.0f); - EXPECT_EQ(GetChildY(frameNode_, 0), 750.0f); -} - -/** - * @tc.name: Jump001 - * @tc.desc: waterFlow jump - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, Jump001, TestSize.Level1) -{ - CreateWithItem([](WaterFlowModelNG model) { - ViewAbstract::SetWidth(CalcLength(400.0f)); - ViewAbstract::SetHeight(CalcLength(200.f)); - model.SetColumnsTemplate("1fr 1fr 1fr"); - }); - pattern_->ScrollToIndex(8); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 5); - EXPECT_EQ(info_->endIndex_, 9); - EXPECT_EQ(info_->idxToLane_.at(8), 2); - EXPECT_EQ(info_->lanes_[0].endPos, 200.0f); - EXPECT_EQ(info_->lanes_[1].startPos, -100.0f); - EXPECT_EQ(info_->lanes_[1].endPos, 300.0f); - EXPECT_EQ(info_->lanes_[2].endPos, 100.0f); - EXPECT_EQ(info_->lanes_[0].items_.size(), 1); - EXPECT_EQ(info_->lanes_[0].items_.front().idx, 7); - EXPECT_EQ(info_->lanes_[1].items_.size(), 2); - EXPECT_EQ(info_->lanes_[1].items_.front().idx, 5); - EXPECT_EQ(info_->lanes_[1].items_.back().idx, 9); - EXPECT_EQ(info_->lanes_[2].items_.size(), 2); - EXPECT_TRUE(info_->itemEnd_); - EXPECT_FALSE(info_->offsetEnd_); - EXPECT_EQ(info_->startIndex_, 5); - EXPECT_EQ(info_->endIndex_, 9); -} - -/** - * @tc.name: ChangeTemplate001 - * @tc.desc: waterFlow change lane count - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, ChangeTemplate001, TestSize.Level1) -{ - CreateWithItem([](WaterFlowModelNG model) { - ViewAbstract::SetWidth(CalcLength(600.0f)); - ViewAbstract::SetHeight(CalcLength(200.f)); - model.SetColumnsTemplate("1fr 1fr 1fr"); - }); - UpdateCurrentOffset(-300.0f); - EXPECT_EQ(info_->startIndex_, 5); - EXPECT_EQ(info_->endIndex_, 9); - EXPECT_EQ(GetChildOffset(frameNode_, 5), OffsetF(200.0f, -100.0f)); - EXPECT_EQ(GetChildOffset(frameNode_, 6), OffsetF(400.0f, -100.0f)); - EXPECT_EQ(GetChildOffset(frameNode_, 7), OffsetF(0.0f, 0.0f)); - EXPECT_EQ(GetChildOffset(frameNode_, 8), OffsetF(400.0f, 0.0f)); - EXPECT_EQ(GetChildOffset(frameNode_, 9), OffsetF(200.0f, 100.0f)); - layoutProperty_->UpdateColumnsTemplate("1fr 1fr"); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 5); - EXPECT_EQ(info_->endIndex_, 8); - EXPECT_EQ(GetChildOffset(frameNode_, 5), OffsetF(0.0f, -100.0f)); - EXPECT_EQ(GetChildOffset(frameNode_, 6), OffsetF(300.0f, -100.0f)); - EXPECT_EQ(GetChildOffset(frameNode_, 7), OffsetF(300.0f, 0.0f)); - EXPECT_EQ(GetChildOffset(frameNode_, 8), OffsetF(0.0f, 100.0f)); -} - -/** - * @tc.name: ModifyItem002 - * @tc.desc: Test WaterFlow reacting to child height change. - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, ModifyItem002, TestSize.Level1) -{ - /** - * @tc.steps: step1. Calling the ScrollToIndex interface to set values to 20 and true. - * @tc.expected: pattern_->targetIndex_ is 20 - */ - Create([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - model.SetFooter(GetDefaultHeaderBuilder()); - CreateItem(80); - }); - - pattern_->ScrollToIndex(50, false, ScrollAlign::CENTER); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 44); - EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); - EXPECT_EQ(GetChildY(frameNode_, 51), 350.0f); - auto child = GetChildFrameNode(frameNode_, 49); - child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(300.0))); - child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 44); - EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); - EXPECT_EQ(GetChildHeight(frameNode_, 49), 300.0f); - - child = GetChildFrameNode(frameNode_, 40); - child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(10.0))); - child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->startIndex_, 44); - EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); - EXPECT_FALSE(child->IsActive()); - EXPECT_FALSE(info_->idxToLane_.count(40)); - - // update footer - pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); - FlushLayoutTask(frameNode_); - EXPECT_EQ(GetChildY(frameNode_, 80), 550.0f); - - child = GetChildFrameNode(frameNode_, 0); - child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(1.0))); - child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(GetChildY(frameNode_, 80), 599.0f); - EXPECT_EQ(GetChildHeight(frameNode_, 0), 1.0f); -} - -/** - * @tc.name: OverScroll001 - * @tc.desc: Test overScroll past limits - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, OverScroll001, TestSize.Level1) -{ - Create([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - model.SetFooter(GetDefaultHeaderBuilder()); - model.SetEdgeEffect(EdgeEffect::SPRING, true); - CreateItem(50); - }); - pattern_->SetAnimateCanOverScroll(true); - UpdateCurrentOffset(30000.0f); - const float startPos = info_->StartPos(); - EXPECT_TRUE(info_->startIndex_ > info_->endIndex_); - EXPECT_GT(info_->StartPos(), 2000.0f); - EXPECT_TRUE(info_->lanes_[0].items_.empty()); - EXPECT_TRUE(info_->lanes_[1].items_.empty()); - - info_->delta_ = -50.0f; - frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->StartPos(), startPos - 50.0f); - EXPECT_TRUE(info_->startIndex_ > info_->endIndex_); - EXPECT_TRUE(info_->lanes_[0].items_.empty()); - EXPECT_TRUE(info_->lanes_[1].items_.empty()); - - UpdateCurrentOffset(-35000.0f); - EXPECT_EQ(info_->startIndex_, 11); - EXPECT_EQ(info_->endIndex_, 22); - EXPECT_LT(info_->StartPos(), 0.0f); - EXPECT_GT(info_->EndPos(), 800.0f); -} - -/** - * @tc.name: OverScroll002 - * @tc.desc: Test overScroll past limits - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, OverScroll002, TestSize.Level1) -{ - Create([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - model.SetFooter(GetDefaultHeaderBuilder()); - model.SetEdgeEffect(EdgeEffect::SPRING, true); - CreateItem(50); - }); - pattern_->SetAnimateCanOverScroll(true); - pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); - FlushLayoutTask(frameNode_); - - UpdateCurrentOffset(-30000.0f); - EXPECT_TRUE(info_->startIndex_ > info_->endIndex_); - EXPECT_TRUE(info_->lanes_[0].items_.empty()); - EXPECT_TRUE(info_->lanes_[1].items_.empty()); - const float endPos = info_->EndPos(); - EXPECT_LT(info_->EndPos(), -2000.0f); - - info_->delta_ = 30.0f; - frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info_->EndPos(), endPos + 30.0f); - EXPECT_TRUE(info_->startIndex_ > info_->endIndex_); - EXPECT_TRUE(info_->lanes_[0].items_.empty()); - EXPECT_TRUE(info_->lanes_[1].items_.empty()); - - UpdateCurrentOffset(35000.0f); - EXPECT_EQ(info_->startIndex_, 28); - EXPECT_EQ(info_->endIndex_, 41); - EXPECT_LT(info_->StartPos(), 0.0f); - EXPECT_GT(info_->EndPos(), 800.0f); -} - -/** - * @tc.name: Misaligned001 - * @tc.desc: Test misalignment and adjustment - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowSWTest, Misaligned001, TestSize.Level1) -{ - Create([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - model.SetFooter(GetDefaultHeaderBuilder()); - CreateItem(50); - }); - EXPECT_FALSE(info_->IsMisaligned()); - pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); - FlushLayoutTask(frameNode_); - - UpdateCurrentOffset(Infinity()); - EXPECT_TRUE(info_->IsMisaligned()); - EXPECT_EQ(GetChildY(frameNode_, 1), 100.0f); - EXPECT_EQ(GetChildX(frameNode_, 1), 240.0f); - pattern_->OnScrollEndCallback(); - FlushLayoutTask(frameNode_); - EXPECT_FALSE(info_->IsMisaligned()); - EXPECT_EQ(info_->lanes_[0].startPos, 0.0f); - EXPECT_EQ(info_->lanes_[0].items_.front().idx, 0); -} -} // namespace OHOS::Ace::NG + */ \ No newline at end of file diff --git a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp index e88e486c624..b548ac4326d 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -31,6 +31,7 @@ #include "test/mock/core/common/mock_theme_manager.h" #include "test/mock/core/pipeline/mock_pipeline_context.h" #include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" +#include "water_flow_item_maps.h" #include "core/components/button/button_theme.h" #include "core/components/common/layout/constants.h" @@ -845,7 +846,7 @@ HWTEST_F(WaterFlowTestNg, WaterFlowPatternTest002, TestSize.Level1) * @tc.expected: startIndex_ = 5 endIndex_ = 27. */ HandleDrag(200.f); - EXPECT_EQ(pattern_->layoutInfo_->currentOffset_, -ITEM_HEIGHT * 2); + EXPECT_EQ(pattern_->layoutInfo_->Offset(), -ITEM_HEIGHT * 2); EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 5); EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 27); } @@ -1416,177 +1417,44 @@ HWTEST_F(WaterFlowTestNg, MeasureForAnimation001, TestSize.Level1) * @tc.desc: Layout WaterFlow and then reset to old layout * @tc.type: FUNC */ -<<<<<<< HEAD HWTEST_F(WaterFlowTestNg, ResetSections001, TestSize.Level1) -======= -HWTEST_F(WaterFlowTestNg, ModifyItem001, TestSize.Level1) { - /** - * @tc.steps: step1. Calling the ScrollToIndex interface to set values to 20 and true. - * @tc.expected: pattern_->targetIndex_ is 20 - */ - CreateWithItem([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - CreateItem(80); - }); - auto info = pattern_->layoutInfo_; - EXPECT_EQ(info->endIndex_, 10); - auto child = GetChildFrameNode(frameNode_, 8); - child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(500.0))); - child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(GetChildHeight(frameNode_, 8), 500.0f); -} - -/** - * @tc.name: ScrollToIndex001 - * @tc.desc: Test WaterFlow ScrollToIndex function. - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, ScrollToIndex001, TestSize.Level1) -{ - /** - * @tc.steps: step1. Calling the ScrollToIndex interface to set values to 20 and true. - * @tc.expected: pattern_->targetIndex_ is 20 - */ - CreateWithItem([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - CreateItem(80); - }); - pattern_->ScrollToIndex(20, true); - EXPECT_EQ(pattern_->targetIndex_, 20); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->finalPosition_, 1400.0f); - - pattern_->ScrollToIndex(80, true); - EXPECT_EQ(pattern_->targetIndex_, 80); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->finalPosition_, 5900.0f); -} - -/** - * @tc.name: ScrollToIndex002 - * @tc.desc: Test ScrollToIndex func - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, ScrollToIndex002, TestSize.Level1) -{ - Create([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - CreateItem(30); - }); - - pattern_->ScrollToIndex(3, false, ScrollAlign::AUTO); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); - EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0); - - pattern_->ScrollToIndex(15, false, ScrollAlign::START); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 15); - EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_->Offset(), -1100); - - pattern_->ScrollToIndex(LAST_ITEM); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 19); - EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); - EXPECT_EQ(pattern_->layoutInfo_->Offset(), -1500); - - pattern_->ScrollToIndex(0, false, ScrollAlign::START); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); - EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_->Offset(), 0); - - pattern_->ScrollToIndex(15, false, ScrollAlign::AUTO); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 7); - EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_->Offset(), -500); - - pattern_->ScrollToIndex(7, false, ScrollAlign::CENTER); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 3); - EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, 0); - EXPECT_EQ(pattern_->layoutInfo_->Offset(), -200); - - pattern_->ScrollToIndex(14, false, ScrollAlign::END); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 3); - EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); - EXPECT_EQ(pattern_->layoutInfo_->Offset(), -300); - - pattern_->ScrollToIndex(2, false, ScrollAlign::AUTO); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 1); - EXPECT_EQ(pattern_->layoutInfo_->storedOffset_, -100); - EXPECT_EQ(pattern_->layoutInfo_->Offset(), -100); -} - -/** - * @tc.name: ScrollToIndex003 - * @tc.desc: Test ScrollToIndex func - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, ScrollToIndex003, TestSize.Level1) ->>>>>>> review fixes -{ - Create([](WaterFlowModelNG model) { - ViewAbstract::SetWidth(CalcLength(400.0f)); - ViewAbstract::SetHeight(CalcLength(600.f)); - CreateItem(60); - }, false); + Create( + [](WaterFlowModelNG model) { + ViewAbstract::SetWidth(CalcLength(400.0f)); + ViewAbstract::SetHeight(CalcLength(600.f)); + CreateItem(60); + }, + false); auto secObj = pattern_->GetOrCreateWaterFlowSections(); secObj->ChangeData(0, 0, SECTION_5); MockPipelineContext::GetCurrent()->FlushBuildFinishCallbacks(); FlushLayoutTask(frameNode_); -<<<<<<< HEAD - auto& info = pattern_->layoutInfo_; + auto info = AceType::DynamicCast(pattern_->layoutInfo_); UpdateCurrentOffset(-205.0f); - EXPECT_EQ(info.currentOffset_, -205.0f); - EXPECT_EQ(info.startIndex_, 3); - EXPECT_EQ(info.endIndex_, 11); + EXPECT_EQ(info->Offset(), -205.0f); + EXPECT_EQ(info->startIndex_, 3); + EXPECT_EQ(info->endIndex_, 11); // fallback to layout without sections pattern_->ResetSections(); FlushLayoutTask(frameNode_); - EXPECT_EQ(info.currentOffset_, -205.0f); - EXPECT_EQ(info.startIndex_, 1); - EXPECT_EQ(info.endIndex_, 5); - EXPECT_EQ(info.GetCrossCount(), 1); + EXPECT_EQ(info->Offset(), -205.0f); + EXPECT_EQ(info->startIndex_, 1); + EXPECT_EQ(info->endIndex_, 5); + EXPECT_EQ(info->GetCrossCount(), 1); if (SystemProperties::WaterFlowUseSegmentedLayout()) { - EXPECT_EQ(info.segmentTails_.size(), 1); - EXPECT_EQ(info.margins_.size(), 1); + EXPECT_EQ(info->segmentTails_.size(), 1); + EXPECT_EQ(info->margins_.size(), 1); } else { - EXPECT_TRUE(info.segmentTails_.empty()); - EXPECT_TRUE(info.margins_.empty()); + EXPECT_TRUE(info->segmentTails_.empty()); + EXPECT_TRUE(info->margins_.empty()); } UpdateCurrentOffset(250.0f); - EXPECT_EQ(info.currentOffset_, 0.0f); - EXPECT_EQ(info.startIndex_, 0); - EXPECT_EQ(info.endIndex_, 3); -======= - EXPECT_FLOAT_EQ(pattern_->finalPosition_, 800.f); - - pattern_->ScrollToIndex(3, true, ScrollAlign::AUTO); - FlushLayoutTask(frameNode_); - EXPECT_FLOAT_EQ(pattern_->finalPosition_, 800.f); - - pattern_->ScrollPage(false); - FlushLayoutTask(frameNode_); - EXPECT_LT(pattern_->layoutInfo_->Offset(), 0.f); - - pattern_->ScrollToIndex(3, true, ScrollAlign::AUTO); - FlushLayoutTask(frameNode_); - EXPECT_FLOAT_EQ(pattern_->finalPosition_, 200.f); - - pattern_->ScrollToIndex(29, true); - FlushLayoutTask(frameNode_); - EXPECT_FLOAT_EQ(pattern_->finalPosition_, 2100.f); ->>>>>>> review fixes + EXPECT_EQ(info->Offset(), 0.0f); + EXPECT_EQ(info->startIndex_, 0); + EXPECT_EQ(info->endIndex_, 3); } } // namespace OHOS::Ace::NG diff --git a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp index c50814aac1d..8259313ca46 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_top_down_test.cpp @@ -11,372 +11,4 @@ * 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. - */ -#include "test/unittest/core/pattern/waterflow/water_flow_test_ng.h" - -#include "core/components_ng/pattern/waterflow/water_flow_layout_info.h" - -namespace OHOS::Ace::NG { -/** - * @tc.name: WaterFlowLayoutInfoTest002 - * @tc.desc: Test functions in WaterFlowLayoutInfo. - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest002, TestSize.Level1) -{ - /** - * @tc.steps: step1. Init Waterflow node - */ - CreateWithItem([](WaterFlowModelNG model) {}); - - /** - * @tc.steps: Test GetStartMainPos and GetMainHeight - * @tc.expected: step2. Check whether the return value is correct. - */ - auto info = AceType::DynamicCast(pattern_->layoutInfo_); - int32_t crossIndex = info->items_[0].rbegin()->first; - int32_t itemIndex = info->items_[0].rbegin()->second.rbegin()->first; - EXPECT_EQ(info->GetStartMainPos(crossIndex + 1, itemIndex), 0.0f); - EXPECT_EQ(info->GetMainHeight(crossIndex + 1, itemIndex), 0.0f); - - EXPECT_EQ(info->GetStartMainPos(crossIndex, itemIndex + 1), 0.0f); - EXPECT_EQ(info->GetMainHeight(crossIndex, itemIndex + 1), 0.0f); -} - -/** - * @tc.name: WaterFlowLayoutInfoTest003 - * @tc.desc: Test functions in WaterFlowLayoutInfo. - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest003, TestSize.Level1) -{ - /** - * @tc.steps: step1. Init Waterflow node - */ - CreateWithItem([](WaterFlowModelNG model) {}); - - /** - * @tc.steps: Test GetMainCount function - * @tc.expected: step2. Check whether the size is correct. - */ - auto info = AceType::DynamicCast(pattern_->layoutInfo_); - - std::size_t waterFlowItemsSize = info->items_[0].size(); - int32_t mainCount = info->GetMainCount(); - - int32_t index = info->items_[0].rbegin()->first; - info->items_[0][index + 1] = std::map>(); - EXPECT_EQ(info->items_[0].size(), waterFlowItemsSize + 1); - EXPECT_EQ(info->GetMainCount(), mainCount); - - auto lastItem = info->items_[0].begin()->second.rbegin(); - float mainSize = lastItem->second.first + lastItem->second.second - 1.0f; - EXPECT_FALSE(info->IsAllCrossReachEnd(mainSize)); - - info->ClearCacheAfterIndex(index + 1); - EXPECT_EQ(info->items_[0].size(), waterFlowItemsSize + 1); -} - -/** - * @tc.name: WaterFlowLayoutInfoTest004 - * @tc.desc: Test Reset functions in WaterFlowLayoutInfo. - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest004, TestSize.Level1) -{ - /** - * @tc.steps: step1. Init Waterflow node - */ - CreateWithItem([](WaterFlowModelNG model) {}); - - /** - * @tc.steps: Test Reset function - * @tc.expected: step2. Check whether the endIndex_ is correct. - */ - auto info = AceType::DynamicCast(pattern_->layoutInfo_); - - int32_t resetFrom = pattern_->layoutInfo_->endIndex_; - info->Reset(resetFrom + 1); - EXPECT_EQ(pattern_->layoutInfo_->endIndex_, resetFrom); - - info->Reset(resetFrom - 1); - EXPECT_EQ(pattern_->layoutInfo_->endIndex_, resetFrom); -} - -/** - * @tc.name: WaterFlowLayoutInfoTest005 - * @tc.desc: Test functions in WaterFlowLayoutInfo. - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, WaterFlowLayoutInfoTest005, TestSize.Level1) -{ - /** - * @tc.steps: step1. Init Waterflow node - */ - CreateWithItem([](WaterFlowModelNG model) {}); - - /** - * @tc.steps: Test GetMaxMainHeight function - * @tc.expected: step2. Check whether the return value is correct. - */ - auto info = AceType::DynamicCast(pattern_->layoutInfo_); - - float maxMainHeight = info->GetMaxMainHeight(); - int32_t crossIndex = info->items_[0].rbegin()->first; - info->items_[0][crossIndex + 1][0] = std::pair(1.0f, maxMainHeight); - info->itemInfos_.clear(); - info->endPosArray_.clear(); - EXPECT_EQ(info->GetMaxMainHeight(), maxMainHeight + 1.0f); - - /** - * @tc.steps: Test GetCrossIndexForNextItem function - * @tc.expected: step3. Check whether the return value is correct. - */ - info->items_[0][crossIndex + 1][1] = std::pair(0.0f, 0.0f); - FlowItemIndex position = info->GetCrossIndexForNextItem(0); - EXPECT_EQ(position.crossIndex, crossIndex + 1); - EXPECT_EQ(position.lastItemIndex, 1); -} - -/** - * @tc.name: WaterFlowTest007 - * @tc.desc: waterFlow with fixed column, scroll to index not fully showed at last line - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, WaterFlowTest007, TestSize.Level1) -{ - CreateWithItem([](WaterFlowModelNG model) { - ViewAbstract::SetWidth(CalcLength(WATERFLOW_WIDTH)); - ViewAbstract::SetHeight(CalcLength(200.f)); - model.SetColumnsTemplate("1fr 1fr 1fr"); - }); - pattern_->UpdateStartIndex(8); - FlushLayoutTask(frameNode_); - EXPECT_FALSE(GetChildFrameNode(frameNode_, 3)->IsActive()); - EXPECT_FALSE(GetChildFrameNode(frameNode_, 4)->IsActive()); - EXPECT_TRUE(GetChildFrameNode(frameNode_, 5)->IsActive()); - EXPECT_TRUE(GetChildFrameNode(frameNode_, 6)->IsActive()); -} - -/** - * @tc.name: UpdateCurrentOffset003 - * @tc.desc: Test the firstIndex and endIndex after UpdateCurrentOffset - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, UpdateCurrentOffset003, TestSize.Level1) -{ - /** - * @tc.steps: step1. create waterFlow - * @tc.steps: step2. scroll up to a remote position - * @tc.expected: startIndex_ = 0 endIndex_ = 0. - */ - Create([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - model.SetEdgeEffect(EdgeEffect::SPRING, true); - CreateItem(TOTAL_LINE_NUMBER * 2); - }); - pattern_->SetAnimateCanOverScroll(true); - pattern_->UpdateCurrentOffset(10000, SCROLL_FROM_UPDATE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->FirstIdx(), 0); - EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 0); - - /** - * @tc.steps: step1. create waterFlow - * @tc.steps: step2. scroll down to a remote position - * @tc.expected: startIndex_ = TOTAL_LINE_NUMBER * 2 - 1, endIndex_ = TOTAL_LINE_NUMBER * 2 - 1. - */ - pattern_->SetAnimateCanOverScroll(true); - pattern_->UpdateCurrentOffset(-99999, SCROLL_FROM_UPDATE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(pattern_->layoutInfo_->FirstIdx(), 19); - EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 19); -} - -/** - * @tc.name: onWillScrollAndOnDidScroll001 - * @tc.desc: Test onScroll event - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, OnWillScrollAndOnDidScroll001, TestSize.Level1) -{ - bool isOnScrollCallBack = false; - bool isOnWillScrollCallBack = false; - bool isOnDidScrollCallBack = false; - - CalcDimension offsetY; - ScrollState scrollState = ScrollState::IDLE; - auto onScroll = [&offsetY, &scrollState, &isOnScrollCallBack](CalcDimension offset, ScrollState state) { - offsetY = offset; - scrollState = state; - isOnScrollCallBack = true; - }; - Dimension willScrollOffset; - ScrollState willScrollState; - auto onWillScroll = [&willScrollOffset, &willScrollState, &isOnWillScrollCallBack]( - Dimension offset, ScrollState state, ScrollSource source) { - willScrollOffset = offset; - willScrollState = state; - isOnWillScrollCallBack = true; - ScrollFrameResult result; - result.offset = offset; - return result; - }; - Dimension didScrollOffset; - ScrollState didScrollState = ScrollState::IDLE; - auto onDidScroll = [&didScrollOffset, &didScrollState, &isOnDidScrollCallBack]( - Dimension offset, ScrollState state) { - didScrollOffset = offset; - didScrollState = state; - isOnDidScrollCallBack = true; - }; - - CreateWithItem([onScroll](WaterFlowModelNG model) { model.SetOnScroll(onScroll); }); - eventHub_->SetOnWillScroll(std::move(onWillScroll)); - eventHub_->SetOnDidScroll(std::move(onDidScroll)); - - /** - * @tc.steps: step1. finger moves down at top - * @tc.expected: Trigger onWillScroll and onDidScroll with SCROLL state - */ - pattern_->ScrollTo(ITEM_HEIGHT * 5); - FlushLayoutTask(frameNode_); - EXPECT_TRUE(isOnScrollCallBack); - EXPECT_TRUE(isOnWillScrollCallBack); - EXPECT_TRUE(isOnDidScrollCallBack); - EXPECT_EQ(offsetY.Value(), ITEM_HEIGHT * 5); - EXPECT_EQ(willScrollOffset.Value(), ITEM_HEIGHT * 5); - EXPECT_EQ(didScrollOffset.Value(), ITEM_HEIGHT * 5); - EXPECT_EQ(scrollState, willScrollState); - EXPECT_EQ(scrollState, didScrollState); -} - -/** - * @tc.name: onScroll - * @tc.desc: Test onScroll event - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, OnWillScrollAndOnDidScroll002, TestSize.Level1) -{ - bool isOnScrollCallBack = false; - bool isOnWillScrollCallBack = false; - bool isOnDidScrollCallBack = false; - - CalcDimension offsetY; - ScrollState scrollState = ScrollState::IDLE; - auto onScroll = [&offsetY, &scrollState, &isOnScrollCallBack](CalcDimension offset, ScrollState state) { - offsetY = offset; - scrollState = state; - isOnScrollCallBack = true; - }; - Dimension willScrollOffset; - ScrollState willScrollState; - auto onWillScroll = [&willScrollOffset, &willScrollState, &isOnWillScrollCallBack]( - Dimension offset, ScrollState state, ScrollSource source) { - willScrollOffset = offset; - willScrollState = state; - isOnWillScrollCallBack = true; - ScrollFrameResult result; - result.offset = offset; - return result; - }; - Dimension didScrollOffset; - ScrollState didScrollState = ScrollState::IDLE; - auto onDidScroll = [&didScrollOffset, &didScrollState, &isOnDidScrollCallBack]( - Dimension offset, ScrollState state) { - didScrollOffset = offset; - didScrollState = state; - isOnDidScrollCallBack = true; - }; - - CreateWithItem([onScroll](WaterFlowModelNG model) { - model.SetOnScroll(onScroll); - model.SetLayoutDirection(FlexDirection::ROW); - }); - eventHub_->SetOnWillScroll(std::move(onWillScroll)); - eventHub_->SetOnDidScroll(std::move(onDidScroll)); - - /** - * @tc.steps: step1. finger moves down at top - * @tc.expected: Trigger onScroll with SCROLL state - */ - pattern_->ScrollTo(ITEM_HEIGHT * 5); - FlushLayoutTask(frameNode_); - EXPECT_TRUE(isOnScrollCallBack); - EXPECT_TRUE(isOnWillScrollCallBack); - EXPECT_TRUE(isOnDidScrollCallBack); - EXPECT_EQ(offsetY.Value(), ITEM_HEIGHT * 5); - EXPECT_EQ(willScrollOffset.Value(), ITEM_HEIGHT * 5); - EXPECT_EQ(didScrollOffset.Value(), ITEM_HEIGHT * 5); - EXPECT_EQ(scrollState, willScrollState); - EXPECT_EQ(scrollState, didScrollState); -} - -/** - * @tc.name: ModifyItem001 - * @tc.desc: Test WaterFlow reacting to child height change. - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, ModifyItem002, TestSize.Level1) -{ - /** - * @tc.steps: step1. Calling the ScrollToIndex interface to set values to 20 and true. - * @tc.expected: pattern_->targetIndex_ is 20 - */ - CreateWithItem([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - CreateItem(80); - }); - auto info = pattern_->layoutInfo_; - - pattern_->ScrollToIndex(50, false, ScrollAlign::CENTER); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info->startIndex_, 43); - EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); - auto child = GetChildFrameNode(frameNode_, 49); - child->layoutProperty_->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(300.0))); - child->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); - FlushLayoutTask(frameNode_); - EXPECT_EQ(info->startIndex_, 43); - EXPECT_EQ(GetChildY(frameNode_, 45), -50.0f); - EXPECT_EQ(GetChildHeight(frameNode_, 49), 300.0f); -} - -/** - * @tc.name: OverScroll001 - * @tc.desc: Test overScroll past limits - * @tc.type: FUNC - */ -HWTEST_F(WaterFlowTestNg, OverScroll001, TestSize.Level1) -{ - /** - * @tc.steps: step1. create waterFlow - * @tc.expected: startIndex_ = 0 endIndex_ = 10. - */ - Create([](WaterFlowModelNG model) { - model.SetColumnsTemplate("1fr 1fr"); - model.SetFooter(GetDefaultHeaderBuilder()); - model.SetEdgeEffect(EdgeEffect::SPRING, true); - CreateItem(50); - }); - pattern_->SetAnimateCanOverScroll(true); - auto info = pattern_->layoutInfo_; - for (int i = 0; i < 50; ++i) { - UpdateCurrentOffset(500.0f); - EXPECT_EQ(info->startIndex_, 0); - EXPECT_GT(info->Offset(), 0.0f); - } - EXPECT_GT(info->Offset(), 2500.0f); - UpdateCurrentOffset(-25500.0f); - EXPECT_EQ(info->startIndex_, 0); - EXPECT_EQ(info->endIndex_, 10); - pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); - FlushLayoutTask(frameNode_); - for (int i = 0; i < 50; ++i) { - UpdateCurrentOffset(-200.0f); - EXPECT_EQ(info->endIndex_, std::max(49, info->footerIndex_)); - EXPECT_EQ(info->BottomFinalPos(800.0f), -3050.0f); - } - EXPECT_LT(info->Offset(), -4000.0f); -} -} // namespace OHOS::Ace::NG + */ \ No newline at end of file -- Gitee