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 431975904d6f854408d344f2c59afd49ec7bbc41..214382d77f04ccf89756bcdc9dddd6c76cf8d04d 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,25 +22,37 @@ namespace OHOS::Ace::NG { void WaterFlowLayoutInfoSW::Sync(int32_t itemCnt, float mainSize, float mainGap) { + if (lanes_.empty()) { + return; + } startIndex_ = StartIndex(); endIndex_ = EndIndex(); - if (startIndex_ <= endIndex_) { - storedOffset_ = lanes_[idxToLane_.at(startIndex_)].startPos; + if (startIndex_ > endIndex_) { + return; } + if (!idxToLane_.count(startIndex_) || lanes_.size() <= idxToLane_.at(startIndex_)) { + return; + } + 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; + itemStart_ = startIndex_ == 0 && NonNegative(startPos_); + itemEnd_ = endIndex_ == itemCnt - 1; + if (footerIndex_ == 0) { + itemEnd_ &= LessOrEqual(endPos_, mainSize); } offsetEnd_ = itemEnd_ && LessOrEqual(endPos_ + footerHeight_, mainSize); maxHeight_ = std::max(endPos_ - startPos_ + footerHeight_, maxHeight_); + if (!itemEnd_) { + footerHeight_ = 0.0f; + } + synced_ = true; } @@ -85,9 +97,9 @@ bool WaterFlowLayoutInfoSW::OutOfBounds() const if (itemStart_ && Positive(lanes_[0].startPos)) { return true; } - if (itemEnd_) { + if (offsetEnd_) { return std::all_of(lanes_.begin(), lanes_.end(), - [mainSize = lastMainSize_](const Lane& lane) { return LessNotEqual(lane.endPos, mainSize); }); + [this](const Lane& lane) { return LessNotEqual(lane.endPos + footerHeight_, lastMainSize_); }); } return false; } @@ -114,7 +126,7 @@ OverScrollOffset WaterFlowLayoutInfoSW::GetOverScrolledDelta(float delta) const return res; } float disToBot = EndPos() + footerHeight_ - lastMainSize_; - if (!itemEnd_) { + if (!offsetEnd_) { res.end = std::min(0.0f, disToBot + delta); } else if (Negative(delta)) { res.end = delta; @@ -361,12 +373,11 @@ bool WaterFlowLayoutInfoSW::IsMisaligned() const if (lanes_.empty()) { return false; } - if (!itemStart_ || !NearZero(StartPos())) { + if (StartIndex() > 0) { return false; } - bool laneNotAligned = std::any_of(lanes_.begin(), lanes_.end(), [](const auto& lane) { - return !NearZero(lane.startPos); - }); + bool laneNotAligned = std::any_of( + lanes_.begin(), lanes_.end(), [this](const auto& lane) { return !NearEqual(lane.startPos, StartPos()); }); 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 b1b87bc3b34350a6f2435d9b70bdd36cdbbf447e..e04dd2683efe801458ea4766513405a370c05116 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,6 +88,8 @@ public: void Reset() override; + bool IsMisaligned() const override; + /** * @brief reset layout data before performing a jump. * @@ -125,15 +127,6 @@ public: */ 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 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 75e0833e1c2c772c86efc3e92732060988c8fcca..9678a253d1108f82c513119e227fe221e124387e 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 @@ -23,8 +23,8 @@ #include "core/components_ng/base/frame_node.h" #include "core/components_ng/layout/layout_wrapper.h" #include "core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h" -#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/pattern/waterflow/layout/water_flow_layout_utils.h" +#include "core/components_ng/pattern/waterflow/water_flow_layout_property.h" #include "core/components_ng/property/measure_utils.h" #include "core/components_ng/property/templates_parser.h" @@ -72,7 +72,7 @@ void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) float crossPos = rtl ? selfCrossLen + mainGap_ : 0.0f; for (size_t i = 0; i < info_->lanes_.size(); ++i) { if (rtl) { - crossPos -= itemCrossSize_[i] + mainGap_; + crossPos -= itemCrossSize_[i] + crossGap_; } auto& lane = info_->lanes_[i]; float mainPos = lane.startPos; @@ -82,10 +82,11 @@ void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) continue; } auto childNode = child->GetGeometryNode(); - if (reverse) { - mainPos = mainLen_ - item.mainSize - mainPos; + auto offset = + reverse ? OffsetF { crossPos, mainLen_ - item.mainSize - mainPos } : OffsetF { crossPos, mainPos }; + if (axis_ != Axis::VERTICAL) { + offset = OffsetF { offset.GetY(), offset.GetX() }; } - auto offset = axis_ == Axis::VERTICAL ? OffsetF { crossPos, mainPos } : OffsetF { mainPos, crossPos }; childNode->SetMarginFrameOffset(offset + paddingOffset); if (child->CheckNeedForceMeasureAndLayout()) { @@ -96,7 +97,7 @@ void WaterFlowLayoutSW::Layout(LayoutWrapper* wrapper) mainPos += item.mainSize + mainGap_; } if (!rtl) { - crossPos += itemCrossSize_[i] + mainGap_; + crossPos += itemCrossSize_[i] + crossGap_; } } @@ -126,6 +127,9 @@ void WaterFlowLayoutSW::Init(const SizeF& frameSize) } else { cross = ParseTemplateArgs(WaterFlowLayoutUtils::PreParseArgs(rowsTemplate), crossSize, crossGap_, itemCnt_); } + if (cross.first.empty()) { + cross.first = { crossSize }; + } if (cross.second) { crossGap_ = 0.0f; } @@ -347,7 +351,7 @@ void WaterFlowLayoutSW::RecoverFront(float viewportBound, int32_t& idx, int32_t void WaterFlowLayoutSW::ClearBack(float bound) { int32_t startIdx = info_->StartIndex(); - for (int32_t i = info_->EndIndex(); i >= startIdx; --i) { + 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; @@ -362,7 +366,7 @@ void WaterFlowLayoutSW::ClearBack(float bound) void WaterFlowLayoutSW::ClearFront() { int32_t endIdx = info_->EndIndex(); - for (int32_t i = info_->StartIndex(); i <= endIdx; ++i) { + 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; @@ -488,7 +492,7 @@ void WaterFlowLayoutSW::AdjustOverScroll() ApplyDelta(-minStart); } else if (info_->EndIndex() == itemCnt_ - 1 && LessNotEqual(maxEnd, mainLen_)) { float delta = mainLen_ - maxEnd; - if (startIdx == 0) { + if (startIdx == 0) { delta = std::min(-minStart, delta); } ApplyDelta(delta); @@ -506,12 +510,11 @@ float WaterFlowLayoutSW::MeasureChild(const RefPtr& pro void WaterFlowLayoutSW::LayoutFooter(const OffsetF& paddingOffset, bool reverse) { - float endPos = info_->EndPos(); - if (info_->footerIndex_ != 0 || GreatOrEqual(endPos, mainLen_)) { + float mainPos = info_->EndPos(); + if (info_->footerIndex_ != 0 || GreatOrEqual(mainPos, mainLen_)) { return; } auto footer = wrapper_->GetOrCreateChildByIndex(0); - float mainPos = endPos + mainGap_; if (reverse) { mainPos = mainLen_ - info_->footerHeight_ - mainPos; } diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h index 13ec41d60949c173eedb575b0b1d46977b714014..d72e7c46137b365142b484e32bdd6eee7b4d215e 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/top_down/water_flow_layout_info.h @@ -113,6 +113,11 @@ public: float JumpToTargetAlign(const std::pair& item) const; void JumpTo(const std::pair& item); + bool IsMisaligned() const override + { + return false; + } + /** * @brief Init data structures based on new WaterFlow Sections. * diff --git a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h index ee280cc8184b0bef206a82198cfa93464e5afce7..934b76e30096882fb59f77b66625d1358cc0537b 100644 --- a/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h +++ b/frameworks/core/components_ng/pattern/waterflow/layout/water_flow_layout_info_base.h @@ -123,6 +123,15 @@ public: // for compatibility virtual void UpdateStartIndex() {}; + /** + * @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. + */ + virtual bool IsMisaligned() const = 0; + virtual void InitSegments(const std::vector& sections, int32_t start) {} /** * @brief Get the Segment index of a FlowItem @@ -133,7 +142,12 @@ public: int32_t GetSegment(int32_t itemIdx) const; bool itemStart_ = false; - bool itemEnd_ = false; // last item is partially in viewport + + /** + * @brief last item is partially in viewport. + * With footer, footer should be considered the last item. + */ + bool itemEnd_ = false; bool offsetEnd_ = false; // last item's bottom is in viewport Axis axis_ = Axis::VERTICAL; diff --git a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.cpp b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.cpp index 4ef26810a425523f7cb2cf4743f172baf1ab3c94..e0e80c51f3bd75e75aafb50cf2e1510dcfb51a40 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_layout_property.cpp @@ -96,19 +96,30 @@ RefPtr WaterFlowLayoutProperty::Clone() const namespace { inline bool UseSegmentedLayout(const RefPtr& host) { - return SystemProperties::WaterFlowUseSegmentedLayout() || host->GetPattern()->GetSections(); + CHECK_NULL_RETURN(host, false); + auto pattern = host->GetPattern(); + return SystemProperties::WaterFlowUseSegmentedLayout() || (pattern && pattern->GetSections()); +} + +inline bool SWLayout(const RefPtr& host) +{ + CHECK_NULL_RETURN(host, false); + auto pattern = host->GetPattern(); + return pattern && pattern->GetLayoutMode() == WaterFlowLayoutMode::SLIDING_WINDOW; } } // namespace void WaterFlowLayoutProperty::OnRowsGapUpdate(Dimension /* rowsGap */) const { - if (GetAxis() == Axis::VERTICAL || UseSegmentedLayout(GetHost())) { + auto host = GetHost(); + if (GetAxis() == Axis::VERTICAL || UseSegmentedLayout(host) || SWLayout(host)) { ResetWaterflowLayoutInfoAndMeasure(); } } void WaterFlowLayoutProperty::OnColumnsGapUpdate(Dimension /* columnsGap */) const { - if (GetAxis() == Axis::HORIZONTAL || UseSegmentedLayout(GetHost())) { + auto host = GetHost(); + if (GetAxis() == Axis::HORIZONTAL || UseSegmentedLayout(host) || SWLayout(host)) { ResetWaterflowLayoutInfoAndMeasure(); } } 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 745b8d86c203c24bb3397e9a20d02bb82ec55a91..a5a60d6f033716350fa2748e6e94923e60f53922 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.cpp @@ -195,22 +195,28 @@ void WaterFlowPattern::TriggerModifyDone() OnModifyDone(); } -bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dirty, const DirtySwapConfig& config) +namespace { +// check if layout is misaligned after a scroll event +bool CheckMisalignment(const RefPtr& info) { - if (config.skipMeasure && config.skipLayout) { - return false; + if (info->IsMisaligned()) { + info->Reset(); + return true; } - auto layoutAlgorithmWrapper = dirty->GetLayoutAlgorithm(); - CHECK_NULL_RETURN(layoutAlgorithmWrapper, false); - auto layoutAlgorithm = DynamicCast(layoutAlgorithmWrapper->GetLayoutAlgorithm()); - CHECK_NULL_RETURN(layoutAlgorithm, false); + return false; +} +} // namespace + +void WaterFlowPattern::TriggerPostLayoutEvents() +{ auto host = GetHost(); - CHECK_NULL_RETURN(host, false); + CHECK_NULL_VOID(host); auto eventHub = host->GetEventHub(); - CHECK_NULL_RETURN(eventHub, false); - auto onScroll = eventHub->GetOnScroll(); + CHECK_NULL_VOID(eventHub); float delta = layoutInfo_->GetDelta(prevOffset_); PrintOffsetLog(AceLogTag::ACE_WATERFLOW, host->GetId(), delta); + + auto onScroll = eventHub->GetOnScroll(); if (onScroll) { FireOnScroll(delta, onScroll); } @@ -235,6 +241,14 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir onReachEnd(); } OnScrollStop(eventHub->GetOnScrollStop()); +} + +bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dirty, const DirtySwapConfig& config) +{ + if (config.skipMeasure && config.skipLayout) { + return false; + } + TriggerPostLayoutEvents(); if (targetIndex_.has_value()) { ScrollToTargetIndex(targetIndex_.value()); @@ -249,6 +263,9 @@ bool WaterFlowPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dir isInitialized_ = true; + if (layoutInfo_->itemStart_ && CheckMisalignment(layoutInfo_)) { + MarkDirtyNodeSelf(); + } return NeedRender(); } @@ -539,22 +556,6 @@ 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; 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 d40b355f33de134864b813673da4efc1311fccad..10bb09b59a149134ca0020b5da0923985936589a 100644 --- a/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h +++ b/frameworks/core/components_ng/pattern/waterflow/water_flow_pattern.h @@ -160,6 +160,9 @@ private: bool OnDirtyLayoutWrapperSwap(const RefPtr& dirty, const DirtySwapConfig& config) override; void CheckScrollable(); bool IsOutOfBoundary(bool useCurrentDelta = true) override; + + void TriggerPostLayoutEvents(); + void SetEdgeEffectCallback(const RefPtr& scrollEffect) override; SizeF GetContentSize() const; void MarkDirtyNodeSelf(); 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 3d006cf3b94f1ac1da82ca72bac06e98418891de..9356151d7ed2168df8bd69f2784bf47602c811f7 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 @@ -242,18 +242,16 @@ HWTEST_F(WaterFlowSWTest, OverScroll001, TestSize.Level1) 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()); + EXPECT_EQ(info_->startIndex_, 0); + EXPECT_EQ(info_->endIndex_, 0); 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()); + EXPECT_EQ(info_->startIndex_, 0); + EXPECT_EQ(info_->endIndex_, 0); UpdateCurrentOffset(-35000.0f); EXPECT_EQ(info_->startIndex_, 11); @@ -280,19 +278,24 @@ HWTEST_F(WaterFlowSWTest, OverScroll002, TestSize.Level1) 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); + EXPECT_EQ(info_->startIndex_, 49); + EXPECT_EQ(info_->endIndex_, 49); 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()); + EXPECT_EQ(info_->startIndex_, 49); + EXPECT_EQ(info_->endIndex_, 49); + + info_->delta_ = -30.0f; + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->EndPos(), endPos); + EXPECT_EQ(info_->startIndex_, 49); + EXPECT_EQ(info_->endIndex_, 49); UpdateCurrentOffset(35000.0f); EXPECT_EQ(info_->startIndex_, 28); @@ -301,6 +304,58 @@ HWTEST_F(WaterFlowSWTest, OverScroll002, TestSize.Level1) EXPECT_GT(info_->EndPos(), 800.0f); } +/** + * @tc.name: OverScroll003 + * @tc.desc: Test overScroll past limits incrementally + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, OverScroll003, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetEdgeEffect(EdgeEffect::SPRING, true); + CreateRandomItem(50); + }); + pattern_->SetAnimateCanOverScroll(true); + for (int i = 1; i <= 10; ++i) { + info_->delta_ = 100.0f; + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->StartPos(), 100.0f * i); + EXPECT_TRUE(info_->itemStart_); + } + EXPECT_EQ(info_->startIndex_, 0); + EXPECT_EQ(info_->endIndex_, 0); + EXPECT_EQ(info_->TopFinalPos(), -1000.0f); +} + +/** + * @tc.name: OverScroll004 + * @tc.desc: Test overScroll past limits incrementally + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, OverScroll004, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + model.SetEdgeEffect(EdgeEffect::SPRING, true); + CreateRandomItem(50); + }); + pattern_->SetAnimateCanOverScroll(true); + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + for (int i = 1; i <= 10; ++i) { + info_->delta_ = -100.0f; + frameNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); + FlushLayoutTask(frameNode_); + EXPECT_EQ(info_->EndPos(), -100.0f * i + WATERFLOW_HEIGHT); + EXPECT_TRUE(info_->offsetEnd_); + } + EXPECT_EQ(info_->startIndex_, 49); + EXPECT_EQ(info_->endIndex_, 49); + EXPECT_EQ(info_->BottomFinalPos(WATERFLOW_HEIGHT), 1000.0f); +} + /** * @tc.name: Misaligned001 * @tc.desc: Test misalignment and adjustment @@ -318,13 +373,94 @@ HWTEST_F(WaterFlowSWTest, Misaligned001, TestSize.Level1) FlushLayoutTask(frameNode_); UpdateCurrentOffset(Infinity()); - EXPECT_TRUE(info_->IsMisaligned()); + EXPECT_EQ(info_->startIndex_, 0); EXPECT_EQ(GetChildY(frameNode_, 1), 100.0f); EXPECT_EQ(GetChildX(frameNode_, 1), 240.0f); - pattern_->OnScrollEndCallback(); + EXPECT_EQ(info_->jumpIndex_, 0); FlushLayoutTask(frameNode_); EXPECT_FALSE(info_->IsMisaligned()); + EXPECT_EQ(GetChildY(frameNode_, 1), 0.0f); + EXPECT_EQ(GetChildX(frameNode_, 1), 0.0f); EXPECT_EQ(info_->lanes_[0].startPos, 0.0f); EXPECT_EQ(info_->lanes_[0].items_.front().idx, 0); } + +/** + * @tc.name: PositionController100 + * @tc.desc: Test PositionController AnimateTo and ScrollTo, should be disabled + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, PositionController100, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create List Item + */ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + CreateItem(TOTAL_LINE_NUMBER * 2); + }); + auto controller = pattern_->positionController_; + /** + * @tc.steps: step8. Test AnimateTo function + * @tc.expected: pattern_->isAnimationStop_ is false + */ + pattern_->AnimateTo(1.5, 1.f, Curves::LINEAR, false, false); + EXPECT_TRUE(pattern_->isAnimationStop_); + + /** + * @tc.steps: step8. test event + * @tc.expected: return the scroll event is ture. + */ + bool isOnWillScrollCallBack = false; + 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; + }; + + eventHub_->SetOnWillScroll(std::move(onWillScroll)); + pattern_->ScrollTo(ITEM_HEIGHT * 5); + EXPECT_FALSE(isOnWillScrollCallBack); +} + +/** + * @tc.name: ScrollToEdge002 + * @tc.desc: ScrollToEdge and check overScroll + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowSWTest, ScrollToEdge002, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetFooter(GetDefaultHeaderBuilder()); + model.SetColumnsTemplate("1fr 1fr 1fr"); + model.SetRowsGap(Dimension(5.0f)); + CreateRandomItem(100); + }); + pattern_->ScrollToEdge(ScrollEdgeType::SCROLL_BOTTOM, false); + FlushLayoutTask(frameNode_); + auto info = pattern_->layoutInfo_; + EXPECT_EQ(info->endIndex_, 99); + EXPECT_EQ(GetChildOffset(frameNode_, info->footerIndex_), OffsetF(0.0f, 750.0f)); + EXPECT_EQ(info_->EndPos(), 750.0f); + EXPECT_FALSE(info->OutOfBounds()); + EXPECT_EQ(info->GetOverScrolledDelta(20.0f).end, 0.0f); + EXPECT_EQ(info->GetOverScrolledDelta(-10.0f).end, -10.0f); + + UpdateCurrentOffset(30.0f); + EXPECT_EQ(info->GetOverScrolledDelta(-20.0f).end, 0.0f); + EXPECT_EQ(info->GetOverScrolledDelta(20.0f).end, 0.0f); + + UpdateCurrentOffset(20.0f); + EXPECT_EQ(info->GetOverScrolledDelta(-20.0f).end, 0.0f); + EXPECT_EQ(info->GetOverScrolledDelta(20.0f).end, 0.0f); + + UpdateCurrentOffset(5.0f); + EXPECT_FALSE(info->itemEnd_); +} } // 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 fec0c98a450faa9daf7177788cd555cacd79c122..1c4b47eea6b878eb61e4ae51008e1fed691d4dac 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.cpp @@ -14,6 +14,7 @@ */ #include +#include #include #include @@ -141,6 +142,17 @@ void WaterFlowTestNg::CreateItem(int32_t number) } } +void WaterFlowTestNg::CreateRandomItem(int32_t number) +{ + for (int32_t i = 0; i < number; i++) { + WaterFlowItemModelNG waterFlowItemModel; + waterFlowItemModel.Create(); + ViewAbstract::SetWidth(CalcLength(FILL_LENGTH)); + ViewAbstract::SetHeight(CalcLength(std::rand() % 200 + 50.0f)); + ViewStackProcessor::GetInstance()->Pop(); + } +} + void WaterFlowTestNg::AddItems(int32_t number) { for (int i = 0; i < number; ++i) { @@ -351,7 +363,20 @@ HWTEST_F(WaterFlowTestNg, Property008, TestSize.Level1) model.SetRowsGap(Dimension(-5)); model.SetColumnsGap(Dimension(-10)); }); - EXPECT_FALSE(false); + + EXPECT_EQ(GetChildWidth(frameNode_, 0), WATERFLOW_WIDTH / 2.0f); + EXPECT_EQ(GetChildWidth(frameNode_, 1), WATERFLOW_WIDTH / 2.0f); + + layoutProperty_->UpdateColumnsGap(Dimension(5.0f)); + FlushLayoutTask(frameNode_); + EXPECT_EQ(GetChildWidth(frameNode_, 0), (WATERFLOW_WIDTH - 5.0f) / 2.0f); + EXPECT_EQ(GetChildWidth(frameNode_, 1), (WATERFLOW_WIDTH - 5.0f) / 2.0f); + EXPECT_EQ(GetChildX(frameNode_, 1), WATERFLOW_WIDTH / 2.0f + 2.5f); + layoutProperty_->UpdateColumnsGap(Dimension(10.0f)); + FlushLayoutTask(frameNode_); + EXPECT_EQ(GetChildWidth(frameNode_, 0), (WATERFLOW_WIDTH - 10.0f) / 2.0f); + EXPECT_EQ(GetChildWidth(frameNode_, 1), (WATERFLOW_WIDTH - 10.0f) / 2.0f); + EXPECT_EQ(GetChildX(frameNode_, 1), WATERFLOW_WIDTH / 2.0f + 5.0f); } /** @@ -775,7 +800,7 @@ HWTEST_F(WaterFlowTestNg, PositionController005, TestSize.Level1) CreateItem(TOTAL_LINE_NUMBER * 2); }); auto controller = pattern_->positionController_; - + /** * @tc.steps: step2. Scroll to the left edge * expected: Return fixed verify @@ -836,7 +861,7 @@ HWTEST_F(WaterFlowTestNg, PositionController005, TestSize.Level1) } EXPECT_TRUE(pattern_->IsAtTop()); - /** + /** * @tc.steps: step8. Test ScrollBy */ controller->ScrollBy(0, ITEM_HEIGHT, true); @@ -864,7 +889,7 @@ HWTEST_F(WaterFlowTestNg, PositionController006, TestSize.Level1) CreateItem(TOTAL_LINE_NUMBER * 2); }); auto controller = pattern_->positionController_; - + /** * @tc.steps: step2. Scroll to the left edge * expected: Return fixed verify @@ -924,13 +949,6 @@ HWTEST_F(WaterFlowTestNg, PositionController006, TestSize.Level1) FlushLayoutTask(frameNode_); } EXPECT_TRUE(pattern_->IsAtTop()); - - /** - * @tc.steps: step8. Test AnimateTo function - * @tc.expected: pattern_->isAnimationStop_ is false - */ - pattern_->AnimateTo(1.5, 1.f, Curves::LINEAR, false, false); - EXPECT_FALSE(pattern_->isAnimationStop_); } namespace { @@ -953,7 +971,7 @@ HWTEST_F(WaterFlowTestNg, PositionController007, TestSize.Level1) CreateItem(TOTAL_LINE_NUMBER * 2); }); auto controller = pattern_->positionController_; - + /** * @tc.steps: step2. Scroll to the left edge * expected: Return fixed verify @@ -1046,7 +1064,7 @@ HWTEST_F(WaterFlowTestNg, PositionController008, TestSize.Level1) CreateItem(TOTAL_LINE_NUMBER * 2); }); auto controller = pattern_->positionController_; - + /** * @tc.steps: step2. Scroll to the left edge * expected: Return fixed verify @@ -1137,7 +1155,7 @@ HWTEST_F(WaterFlowTestNg, PositionController009, TestSize.Level1) CreateItem(TOTAL_LINE_NUMBER * 2); }); auto controller = pattern_->positionController_; - + /** * @tc.steps: step2. Scroll to the left edge * expected: Return fixed verify @@ -1197,27 +1215,6 @@ HWTEST_F(WaterFlowTestNg, PositionController009, TestSize.Level1) FlushLayoutTask(frameNode_); } EXPECT_TRUE(pattern_->IsAtTop()); - - /** - * @tc.steps: step8. test event - * @tc.expected: return the scroll event is ture. - */ - bool isOnWillScrollCallBack = false; - 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; - }; - - eventHub_->SetOnWillScroll(std::move(onWillScroll)); - pattern_->ScrollTo(ITEM_HEIGHT * 5); - EXPECT_TRUE(isOnWillScrollCallBack); } /** @@ -1699,4 +1696,44 @@ HWTEST_F(WaterFlowTestNg, MeasureForAnimation001, TestSize.Level1) auto crossIndex = pattern_->layoutInfo_->GetCrossIndex(10); EXPECT_FALSE(IsEqual(crossIndex, -1)); } + +/** + * @tc.name: Illegal001 + * @tc.desc: Test illegal columns template + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, Illegal001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("a"); + model.SetFooter(GetDefaultHeaderBuilder()); + CreateItem(20); + }); + EXPECT_EQ(GetChildWidth(frameNode_, 1), WATERFLOW_WIDTH); + EXPECT_EQ(GetChildWidth(frameNode_, 2), WATERFLOW_WIDTH); + EXPECT_EQ(GetChildX(frameNode_, 1), 0.0f); + EXPECT_EQ(pattern_->layoutInfo_->startIndex_, 0); + EXPECT_EQ(pattern_->layoutInfo_->endIndex_, 5); +} + +/** + * @tc.name: Reverse001 + * @tc.desc: Test reverse layout + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, Reverse001, TestSize.Level1) +{ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("a"); + CreateItem(20); + }); + layoutProperty_->UpdateWaterflowDirection(FlexDirection::COLUMN_REVERSE); + FlushLayoutTask(frameNode_); + EXPECT_EQ(GetChildY(frameNode_, 0), 700.0f); + EXPECT_EQ(GetChildY(frameNode_, 1), 500.0f); + EXPECT_EQ(GetChildY(frameNode_, 2), 400.0f); + EXPECT_EQ(GetChildY(frameNode_, 3), 200.0f); + EXPECT_EQ(GetChildY(frameNode_, 4), 100.0f); + EXPECT_EQ(GetChildY(frameNode_, 5), -100.0f); +} } // namespace OHOS::Ace::NG 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 a9d7a62f8a647706b93abac863bcb51294b45b2b..817ae576cf278c8178460751cee766a1e3df1224 100644 --- a/test/unittest/core/pattern/waterflow/water_flow_test_ng.h +++ b/test/unittest/core/pattern/waterflow/water_flow_test_ng.h @@ -57,6 +57,7 @@ protected: void Create(const std::function& callback = nullptr, bool flushLayout = true); void CreateWithItem(const std::function& callback = nullptr); static void CreateItem(int32_t number = 10); + static void CreateRandomItem(int32_t number); static void CreateItemWithHeight(float height); void UpdateCurrentOffset(float offset, int32_t source = SCROLL_FROM_UPDATE); void MouseSelect(Offset start, Offset end); 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 1273c2ebca8822e4fcfb52f282ab7f3123eba5a4..4e26338de7bde824194350b4dea90fcda562345f 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 @@ -435,4 +435,48 @@ HWTEST_F(WaterFlowTestNg, WaterFlowTest012, TestSize.Level1) EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(ITEM_HEIGHT), { 100, 100 })); EXPECT_TRUE(IsEqual(pattern_->GetOverScrollOffset(3 * ITEM_HEIGHT), { 300, 300 })); } + +/** + * @tc.name: PositionController100 + * @tc.desc: Test PositionController AnimateTo and ScrollTo + * @tc.type: FUNC + */ +HWTEST_F(WaterFlowTestNg, PositionController100, TestSize.Level1) +{ + /** + * @tc.steps: step1. Create List Item + */ + Create([](WaterFlowModelNG model) { + model.SetColumnsTemplate("1fr 1fr"); + CreateItem(TOTAL_LINE_NUMBER * 2); + }); + auto controller = pattern_->positionController_; + /** + * @tc.steps: step8. Test AnimateTo function + * @tc.expected: pattern_->isAnimationStop_ is false + */ + pattern_->AnimateTo(1.5, 1.f, Curves::LINEAR, false, false); + EXPECT_FALSE(pattern_->isAnimationStop_); + + /** + * @tc.steps: step8. test event + * @tc.expected: return the scroll event is ture. + */ + bool isOnWillScrollCallBack = false; + 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; + }; + + eventHub_->SetOnWillScroll(std::move(onWillScroll)); + pattern_->ScrollTo(ITEM_HEIGHT * 5); + EXPECT_TRUE(isOnWillScrollCallBack); +} } // namespace OHOS::Ace::NG